clock.c 6.8 KB

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