clock.c 5.7 KB

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