clock.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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. { 3, -1, 32, "386", }, /* family defaults */
  75. { 4, -1, 22, "486", },
  76. { 5, -1, 23, "Pentium", },
  77. { 6, -1, 16, "PentiumPro", },
  78. { -1, -1, 23, "unknown", }, /* total default */
  79. };
  80. /*
  81. * The AMD processors all implement the CPUID instruction.
  82. * The later ones also return the processor name via functions
  83. * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
  84. * and DX:
  85. * K5 "AMD-K5(tm) Processor"
  86. * K6 "AMD-K6tm w/ multimedia extensions"
  87. * K6 3D "AMD-K6(tm) 3D processor"
  88. * K6 3D+ ?
  89. */
  90. static X86type x86amd[] =
  91. {
  92. { 5, 0, 23, "AMD-K5", }, /* guesswork */
  93. { 5, 1, 23, "AMD-K5", }, /* guesswork */
  94. { 5, 2, 23, "AMD-K5", }, /* guesswork */
  95. { 5, 3, 23, "AMD-K5", }, /* guesswork */
  96. { 5, 6, 11, "AMD-K6", }, /* trial and error */
  97. { 5, 7, 11, "AMD-K6", }, /* trial and error */
  98. { 5, 8, 11, "AMD-K6-2", }, /* trial and error */
  99. { 5, 9, 11, "AMD-K6-III", },/* trial and error */
  100. { 6, 1, 11, "AMD-Athlon", },/* trial and error */
  101. { 6, 2, 11, "AMD-Athlon", },/* trial and error */
  102. { 4, -1, 22, "Am486", }, /* guesswork */
  103. { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */
  104. { 6, -1, 11, "AMD-Athlon", },/* guesswork */
  105. { -1, -1, 23, "unknown", }, /* total default */
  106. };
  107. static X86type *cputype;
  108. /*
  109. * delay for l milliseconds more or less. delayloop is set by
  110. * clockinit() to match the actual CPU speed.
  111. */
  112. void
  113. delay(int l)
  114. {
  115. l *= loopconst;
  116. if(l <= 0)
  117. l = 1;
  118. aamloop(l);
  119. }
  120. void
  121. microdelay(int l)
  122. {
  123. l *= loopconst;
  124. l /= 1000;
  125. if(l <= 0)
  126. l = 1;
  127. aamloop(l);
  128. }
  129. extern void cpuid(char*, int*, int*);
  130. X86type*
  131. cpuidentify(void)
  132. {
  133. int family, model;
  134. X86type *t;
  135. char cpuidid[16];
  136. int cpuidax, cpuiddx;
  137. cpuid(cpuidid, &cpuidax, &cpuiddx);
  138. if(strncmp(cpuidid, "AuthenticAMD", 12) == 0)
  139. t = x86amd;
  140. else
  141. t = x86intel;
  142. family = X86FAMILY(cpuidax);
  143. model = X86MODEL(cpuidax);
  144. while(t->name){
  145. if((t->family == family && t->model == model)
  146. || (t->family == family && t->model == -1)
  147. || (t->family == -1))
  148. break;
  149. t++;
  150. }
  151. if(t->name == nil)
  152. panic("cpuidentify");
  153. return t;
  154. }
  155. void
  156. clockinit(void)
  157. {
  158. int x, y; /* change in counter */
  159. int loops, incr;
  160. X86type *t;
  161. /*
  162. * set vector for clock interrupts
  163. */
  164. setvec(VectorCLOCK, clockintr, 0);
  165. t = cpuidentify();
  166. /*
  167. * set clock for 1/HZ seconds
  168. */
  169. outb(Tmode, Load0|Square);
  170. outb(T0cntr, (Freq/HZ)); /* low byte */
  171. outb(T0cntr, (Freq/HZ)>>8); /* high byte */
  172. /*
  173. * Introduce a little delay to make sure the count is
  174. * latched and the timer is counting down; with a fast
  175. * enough processor this may not be the case.
  176. * The i8254 (which this probably is) has a read-back
  177. * command which can be used to make sure the counting
  178. * register has been written into the counting element.
  179. */
  180. x = (Freq/HZ);
  181. for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
  182. outb(Tmode, Latch0);
  183. x = inb(T0cntr);
  184. x |= inb(T0cntr)<<8;
  185. }
  186. /* find biggest loop that doesn't wrap */
  187. incr = 16000000/(t->aalcycles*HZ*2);
  188. x = 2000;
  189. for(loops = incr; loops < 64*1024; loops += incr) {
  190. /*
  191. * measure time for the loop
  192. *
  193. * MOVL loops,CX
  194. * aaml1: AAM
  195. * LOOP aaml1
  196. *
  197. * the time for the loop should be independent of external
  198. * cache and memory system since it fits in the execution
  199. * prefetch buffer.
  200. *
  201. */
  202. outb(Tmode, Latch0);
  203. x = inb(T0cntr);
  204. x |= inb(T0cntr)<<8;
  205. aamloop(loops);
  206. outb(Tmode, Latch0);
  207. y = inb(T0cntr);
  208. y |= inb(T0cntr)<<8;
  209. x -= y;
  210. if(x < 0)
  211. x += Freq/HZ;
  212. if(x > Freq/(3*HZ))
  213. break;
  214. }
  215. /*
  216. * counter goes at twice the frequency, once per transition,
  217. * i.e., twice per square wave
  218. */
  219. x >>= 1;
  220. /*
  221. * figure out clock frequency and a loop multiplier for delay().
  222. */
  223. cpufreq = loops*((t->aalcycles*Freq)/x);
  224. loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */
  225. /*
  226. * add in possible .2% error and convert to MHz
  227. */
  228. cpumhz = (cpufreq + cpufreq/500)/1000000;
  229. // print("%dMHz %s loop %d\n", cpumhz, t->name, loopconst);
  230. }