8253.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #include "all.h"
  2. #include "mem.h"
  3. #include "ureg.h"
  4. #include "io.h"
  5. /*
  6. * 8253 timer
  7. */
  8. enum
  9. {
  10. T0cntr = 0x40, /* counter ports */
  11. T1cntr = 0x41, /* ... */
  12. T2cntr = 0x42, /* ... */
  13. Tmode = 0x43, /* mode port */
  14. /* commands */
  15. Latch0 = 0x00, /* latch counter 0's value */
  16. Load0 = 0x30, /* load counter 0 with 2 bytes */
  17. /* modes */
  18. Square = 0x36, /* periodic square wave */
  19. Trigger = 0x30, /* interrupt on terminal count */
  20. Freq = 1193182, /* Real clock frequency */
  21. };
  22. static int cpufreq = 66000000;
  23. static int cpuhz;
  24. static int cpumhz = 66;
  25. static int loopconst = 100;
  26. /*static*/ int cpuidax, cpuiddx;
  27. static char cpuidid[16];
  28. static void
  29. clockintr(Ureg *ur, void *v)
  30. {
  31. USED(v);
  32. clock(0, ur->pc);
  33. }
  34. #define STEPPING(x) ((x)&0xf)
  35. #define MODEL(x) (((x)>>4)&0xf)
  36. #define FAMILY(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. static 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, "P5", },
  77. { 6, -1, 16, "P6", },
  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. static void
  109. nop(void)
  110. {
  111. }
  112. void (*coherence)(void) = nop;
  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. /*
  126. * microsecond delay
  127. */
  128. void
  129. microdelay(int l)
  130. {
  131. l *= loopconst;
  132. l /= 1000;
  133. if(l <= 0)
  134. l = 1;
  135. aamloop(l);
  136. }
  137. void
  138. printcpufreq(void)
  139. {
  140. int i;
  141. char buf[128];
  142. i = sprint(buf, "cpu%d: %dMHz ", 0, cpumhz);
  143. if(cpuidid[0])
  144. i += sprint(buf+i, "%s ", cpuidid);
  145. sprint(buf+i, "%s (cpuid: AX 0x%4.4ux DX 0x%4.4ux)\n",
  146. cputype->name, cpuidax, cpuiddx);
  147. print(buf);
  148. }
  149. void
  150. clockinit(void)
  151. {
  152. int x, y; /* change in counter */
  153. int family, model, loops, incr, havecycleclock;
  154. X86type *t;
  155. uvlong a, b;
  156. /*
  157. * set vector for clock interrupts
  158. */
  159. setvec(Clockvec, clockintr, 0);
  160. /*
  161. * figure out what we are
  162. */
  163. cpuid(cpuidid, &cpuidax, &cpuiddx);
  164. if(strncmp(cpuidid, "AuthenticAMD", 12) == 0)
  165. t = x86amd;
  166. else
  167. t = x86intel;
  168. family = FAMILY(cpuidax);
  169. model = MODEL(cpuidax);
  170. while(t->name){
  171. if((t->family == family && t->model == model)
  172. || (t->family == family && t->model == -1)
  173. || (t->family == -1))
  174. break;
  175. t++;
  176. }
  177. cputype = t;
  178. if(family >= 5){
  179. havecycleclock = 1;
  180. coherence = wbflush;
  181. }
  182. else
  183. havecycleclock = 0;
  184. /*
  185. * set clock for 1/HZ seconds
  186. */
  187. outb(Tmode, Load0|Square);
  188. outb(T0cntr, (Freq/HZ)); /* low byte */
  189. outb(T0cntr, (Freq/HZ)>>8); /* high byte */
  190. /*
  191. * Introduce a little delay to make sure the count is
  192. * latched and the timer is counting down; with a fast
  193. * enough processor this may not be the case.
  194. * The i8254 (which this probably is) has a read-back
  195. * command which can be used to make sure the counting
  196. * register has been written into the counting element.
  197. */
  198. x = (Freq/HZ);
  199. for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
  200. outb(Tmode, Latch0);
  201. x = inb(T0cntr);
  202. x |= inb(T0cntr)<<8;
  203. }
  204. /* find biggest loop that doesn't wrap */
  205. incr = 16000000/(t->aalcycles*HZ*2);
  206. x = 2000;
  207. for(loops = incr; loops < 64*1024; loops += incr) {
  208. /*
  209. * measure time for the loop
  210. *
  211. * MOVL loops,CX
  212. * aaml1: AAM
  213. * LOOP aaml1
  214. *
  215. * the time for the loop should be independent of external
  216. * cache and memory system since it fits in the execution
  217. * prefetch buffer.
  218. *
  219. */
  220. outb(Tmode, Latch0);
  221. if(havecycleclock)
  222. rdtsc(&a);
  223. x = inb(T0cntr);
  224. x |= inb(T0cntr)<<8;
  225. aamloop(loops);
  226. outb(Tmode, Latch0);
  227. if(havecycleclock)
  228. rdtsc(&b);
  229. y = inb(T0cntr);
  230. y |= inb(T0cntr)<<8;
  231. x -= y;
  232. if(x < 0)
  233. x += Freq/HZ;
  234. if(x > Freq/(3*HZ))
  235. break;
  236. }
  237. /*
  238. * figure out clock frequency and a loop multiplier for delay().
  239. * n.b. counter goes up by 2*Freq
  240. */
  241. cpufreq = loops*((t->aalcycles*2*Freq)/x);
  242. loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */
  243. if(havecycleclock){
  244. /* counter goes up by 2*Freq */
  245. b = (b-a)<<1;
  246. b *= Freq;
  247. b /= x;
  248. /*
  249. * round to the nearest megahz
  250. */
  251. cpumhz = (b+500000)/1000000L;
  252. cpuhz = b;
  253. } else {
  254. /*
  255. * add in possible 0.5% error and convert to MHz
  256. */
  257. cpumhz = (cpufreq + cpufreq/200)/1000000;
  258. cpuhz = cpufreq;
  259. }
  260. }
  261. void
  262. clockreload(ulong n)
  263. {
  264. USED(n);
  265. }