clock.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "io.h"
  15. #include "ureg.h"
  16. /*
  17. * 8253 timer
  18. */
  19. enum
  20. {
  21. T0cntr= 0x40, /* counter ports */
  22. T1cntr= 0x41, /* ... */
  23. T2cntr= 0x42, /* ... */
  24. Tmode= 0x43, /* mode port */
  25. /* commands */
  26. Latch0= 0x00, /* latch counter 0's value */
  27. Load0= 0x30, /* load counter 0 with 2 bytes */
  28. /* modes */
  29. Square= 0x36, /* perioic square wave */
  30. Freq= 1193182, /* Real clock frequency */
  31. };
  32. static uint64_t cpuhz = 66000000;
  33. static int cpumhz = 66;
  34. static int loopconst = 100;
  35. int cpuidax, cpuiddx;
  36. int havetsc;
  37. extern void _cycles(uint64_t*); /* in l.s */
  38. extern void wrmsr(int, int64_t);
  39. static void
  40. clockintr(Ureg*, void*)
  41. {
  42. machp()->ticks++;
  43. checkalarms();
  44. }
  45. #define STEPPING(x) ((x)&0xf)
  46. #define X86MODEL(x) (((x)>>4)&0xf)
  47. #define X86FAMILY(x) (((x)>>8)&0xf)
  48. enum
  49. {
  50. /* flags */
  51. CpuidFPU = 0x001, /* on-chip floating point unit */
  52. CpuidMCE = 0x080, /* machine check exception */
  53. CpuidCX8 = 0x100, /* CMPXCHG8B instruction */
  54. };
  55. typedef struct
  56. {
  57. int family;
  58. int model;
  59. int aalcycles;
  60. char *name;
  61. } X86type;
  62. X86type x86intel[] =
  63. {
  64. { 4, 0, 22, "486DX", }, /* known chips */
  65. { 4, 1, 22, "486DX50", },
  66. { 4, 2, 22, "486SX", },
  67. { 4, 3, 22, "486DX2", },
  68. { 4, 4, 22, "486SL", },
  69. { 4, 5, 22, "486SX2", },
  70. { 4, 7, 22, "DX2WB", }, /* P24D */
  71. { 4, 8, 22, "DX4", }, /* P24C */
  72. { 4, 9, 22, "DX4WB", }, /* P24CT */
  73. { 5, 0, 23, "P5", },
  74. { 5, 1, 23, "P5", },
  75. { 5, 2, 23, "P54C", },
  76. { 5, 3, 23, "P24T", },
  77. { 5, 4, 23, "P55C MMX", },
  78. { 5, 7, 23, "P54C VRT", },
  79. { 6, 1, 16, "PentiumPro", },/* trial and error */
  80. { 6, 3, 16, "PentiumII", },
  81. { 6, 5, 16, "PentiumII/Xeon", },
  82. { 6, 6, 16, "Celeron", },
  83. { 6, 7, 16, "PentiumIII/Xeon", },
  84. { 6, 8, 16, "PentiumIII/Xeon", },
  85. { 6, 0xB, 16, "PentiumIII/Xeon", },
  86. { 0xF, 1, 16, "P4", }, /* P4 */
  87. { 0xF, 2, 16, "PentiumIV/Xeon", },
  88. { 3, -1, 32, "386", }, /* family defaults */
  89. { 4, -1, 22, "486", },
  90. { 5, -1, 23, "P5", },
  91. { 6, -1, 16, "P6", },
  92. { 0xF, -1, 16, "P4", }, /* P4 */
  93. { -1, -1, 16, "unknown", }, /* total default */
  94. };
  95. /*
  96. * The AMD processors all implement the CPUID instruction.
  97. * The later ones also return the processor name via functions
  98. * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
  99. * and DX:
  100. * K5 "AMD-K5(tm) Processor"
  101. * K6 "AMD-K6tm w/ multimedia extensions"
  102. * K6 3D "AMD-K6(tm) 3D processor"
  103. * K6 3D+ ?
  104. */
  105. static X86type x86amd[] =
  106. {
  107. { 5, 0, 23, "AMD-K5", }, /* guesswork */
  108. { 5, 1, 23, "AMD-K5", }, /* guesswork */
  109. { 5, 2, 23, "AMD-K5", }, /* guesswork */
  110. { 5, 3, 23, "AMD-K5", }, /* guesswork */
  111. { 5, 6, 11, "AMD-K6", }, /* trial and error */
  112. { 5, 7, 11, "AMD-K6", }, /* trial and error */
  113. { 5, 8, 11, "AMD-K6-2", }, /* trial and error */
  114. { 5, 9, 11, "AMD-K6-III", },/* trial and error */
  115. { 6, 1, 11, "AMD-Athlon", },/* trial and error */
  116. { 6, 2, 11, "AMD-Athlon", },/* trial and error */
  117. { 4, -1, 22, "Am486", }, /* guesswork */
  118. { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */
  119. { 6, -1, 11, "AMD-Athlon", },/* guesswork */
  120. { 0xF, -1, 11, "AMD64", }, /* guesswork */
  121. { -1, -1, 11, "unknown", }, /* total default */
  122. };
  123. static X86type *cputype;
  124. void
  125. delay(int millisecs)
  126. {
  127. millisecs *= loopconst;
  128. if(millisecs <= 0)
  129. millisecs = 1;
  130. aamloop(millisecs);
  131. }
  132. void
  133. microdelay(int microsecs)
  134. {
  135. microsecs *= loopconst;
  136. microsecs /= 1000;
  137. if(microsecs <= 0)
  138. microsecs = 1;
  139. aamloop(microsecs);
  140. }
  141. extern void cpuid(char*, int*, int*);
  142. X86type*
  143. cpuidentify(void)
  144. {
  145. int family, model;
  146. X86type *t;
  147. char cpuidid[16];
  148. int cpuidax, cpuiddx;
  149. cpuid(cpuidid, &cpuidax, &cpuiddx);
  150. if(strncmp(cpuidid, "AuthenticAMD", 12) == 0)
  151. t = x86amd;
  152. else
  153. t = x86intel;
  154. family = X86FAMILY(cpuidax);
  155. model = X86MODEL(cpuidax);
  156. if (1)
  157. print("cpuidentify: cpuidax 0x%ux cpuiddx 0x%ux\n",
  158. cpuidax, cpuiddx);
  159. while(t->name){
  160. if((t->family == family && t->model == model)
  161. || (t->family == family && t->model == -1)
  162. || (t->family == -1))
  163. break;
  164. t++;
  165. }
  166. if(t->name == nil)
  167. panic("cpuidentify");
  168. if(cpuiddx & 0x10){
  169. havetsc = 1;
  170. if(cpuiddx & 0x20)
  171. wrmsr(0x10, 0);
  172. }
  173. return t;
  174. }
  175. void
  176. clockinit(void)
  177. {
  178. uint64_t a, b, cpufreq;
  179. int loops, incr, x, y;
  180. X86type *t;
  181. /*
  182. * set vector for clock interrupts
  183. */
  184. setvec(VectorCLOCK, clockintr, 0);
  185. t = cpuidentify();
  186. /*
  187. * set clock for 1/HZ seconds
  188. */
  189. outb(Tmode, Load0|Square);
  190. outb(T0cntr, (Freq/HZ)); /* low byte */
  191. outb(T0cntr, (Freq/HZ)>>8); /* high byte */
  192. /*
  193. * Introduce a little delay to make sure the count is
  194. * latched and the timer is counting down; with a fast
  195. * enough processor this may not be the case.
  196. * The i8254 (which this probably is) has a read-back
  197. * command which can be used to make sure the counting
  198. * register has been written into the counting element.
  199. */
  200. x = (Freq/HZ);
  201. for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
  202. outb(Tmode, Latch0);
  203. x = inb(T0cntr);
  204. x |= inb(T0cntr)<<8;
  205. }
  206. /* find biggest loop that doesn't wrap */
  207. incr = 16000000/(t->aalcycles*HZ*2);
  208. x = 2000;
  209. for(loops = incr; loops < 64*1024; loops += incr) {
  210. /*
  211. * measure time for the loop
  212. *
  213. * MOVL loops,CX
  214. * aaml1: AAM
  215. * LOOP aaml1
  216. *
  217. * the time for the loop should be independent of external
  218. * cache and memory system since it fits in the execution
  219. * prefetch buffer.
  220. *
  221. */
  222. outb(Tmode, Latch0);
  223. if(havetsc)
  224. _cycles(&a);
  225. x = inb(T0cntr);
  226. x |= inb(T0cntr)<<8;
  227. aamloop(loops);
  228. outb(Tmode, Latch0);
  229. if(havetsc)
  230. _cycles(&b);
  231. y = inb(T0cntr);
  232. y |= inb(T0cntr)<<8;
  233. x -= y;
  234. if(x < 0)
  235. x += Freq/HZ;
  236. if(x > Freq/(3*HZ))
  237. break;
  238. }
  239. /*
  240. * figure out clock frequency and a loop multiplier for delay().
  241. * counter goes at twice the frequency, once per transition,
  242. * i.e., twice per square wave
  243. */
  244. cpufreq = (int64_t)loops*((t->aalcycles*2*Freq)/x);
  245. loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */
  246. if(havetsc){
  247. /* counter goes up by 2*Freq */
  248. b = (b-a)<<1;
  249. b *= Freq;
  250. b /= x;
  251. /*
  252. * round to the nearest megahz
  253. */
  254. cpumhz = (b+500000)/1000000L;
  255. cpuhz = b;
  256. }
  257. else{
  258. /*
  259. * add in possible .5% error and convert to MHz
  260. */
  261. cpumhz = (cpufreq + cpufreq/200)/1000000;
  262. cpuhz = cpufreq;
  263. }
  264. if(debug){
  265. int timeo;
  266. print("%dMHz %s loop %d\n", cpumhz, t->name, loopconst);
  267. print("tick...");
  268. for(timeo = 0; timeo < 10; timeo++)
  269. delay(1000);
  270. print("tock...\n");
  271. }
  272. }