clock.c 6.3 KB

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