8253.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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 uvlong cpufreq = 66000000;
  23. static uvlong cpuhz;
  24. static int cpumhz = 66;
  25. static int loopconst = 100;
  26. /*static*/ int cpuidax, cpuiddx;
  27. static char cpuidid[16];
  28. static int havetsc;
  29. static void
  30. clockintr(Ureg *ur, void *v)
  31. {
  32. USED(v);
  33. clock(0, ur->pc);
  34. }
  35. #define STEPPING(x) ((x)&0xf)
  36. #define MODEL(x) (((x)>>4)&0xf)
  37. #define FAMILY(x) (((x)>>8)&0xf)
  38. enum
  39. {
  40. /* flags */
  41. CpuidFPU = 0x001, /* on-chip floating point unit */
  42. CpuidMCE = 0x080, /* machine check exception */
  43. CpuidCX8 = 0x100, /* CMPXCHG8B instruction */
  44. };
  45. typedef struct
  46. {
  47. int family;
  48. int model;
  49. int aalcycles;
  50. char *name;
  51. } X86type;
  52. static X86type x86intel[] =
  53. {
  54. { 4, 0, 22, "486DX", }, /* known chips */
  55. { 4, 1, 22, "486DX50", },
  56. { 4, 2, 22, "486SX", },
  57. { 4, 3, 22, "486DX2", },
  58. { 4, 4, 22, "486SL", },
  59. { 4, 5, 22, "486SX2", },
  60. { 4, 7, 22, "DX2WB", }, /* P24D */
  61. { 4, 8, 22, "DX4", }, /* P24C */
  62. { 4, 9, 22, "DX4WB", }, /* P24CT */
  63. { 5, 0, 23, "P5", },
  64. { 5, 1, 23, "P5", },
  65. { 5, 2, 23, "P54C", },
  66. { 5, 3, 23, "P24T", },
  67. { 5, 4, 23, "P55C MMX", },
  68. { 5, 7, 23, "P54C VRT", },
  69. { 6, 1, 16, "PentiumPro", },/* trial and error */
  70. { 6, 3, 16, "PentiumII", },
  71. { 6, 5, 16, "PentiumII/Xeon", },
  72. { 6, 6, 16, "Celeron", },
  73. { 6, 7, 16, "PentiumIII/Xeon", },
  74. { 6, 8, 16, "PentiumIII/Xeon", },
  75. { 6, 0xB, 16, "PentiumIII/Xeon", },
  76. { 0xF, 1, 16, "P4", }, /* P4 */
  77. { 0xF, 2, 16, "PentiumIV/Xeon", },
  78. { 3, -1, 32, "386", }, /* family defaults */
  79. { 4, -1, 22, "486", },
  80. { 5, -1, 23, "P5", },
  81. { 6, -1, 16, "P6", },
  82. { 0xF, -1, 16, "P4", }, /* P4 */
  83. { -1, -1, 16, "unknown", }, /* total default */
  84. };
  85. /*
  86. * The AMD processors all implement the CPUID instruction.
  87. * The later ones also return the processor name via functions
  88. * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
  89. * and DX:
  90. * K5 "AMD-K5(tm) Processor"
  91. * K6 "AMD-K6tm w/ multimedia extensions"
  92. * K6 3D "AMD-K6(tm) 3D processor"
  93. * K6 3D+ ?
  94. */
  95. static X86type x86amd[] =
  96. {
  97. { 5, 0, 23, "AMD-K5", }, /* guesswork */
  98. { 5, 1, 23, "AMD-K5", }, /* guesswork */
  99. { 5, 2, 23, "AMD-K5", }, /* guesswork */
  100. { 5, 3, 23, "AMD-K5", }, /* guesswork */
  101. { 5, 6, 11, "AMD-K6", }, /* trial and error */
  102. { 5, 7, 11, "AMD-K6", }, /* trial and error */
  103. { 5, 8, 11, "AMD-K6-2", }, /* trial and error */
  104. { 5, 9, 11, "AMD-K6-III", },/* trial and error */
  105. { 6, 1, 11, "AMD-Athlon", },/* trial and error */
  106. { 6, 2, 11, "AMD-Athlon", },/* trial and error */
  107. { 4, -1, 22, "Am486", }, /* guesswork */
  108. { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */
  109. { 6, -1, 11, "AMD-Athlon", },/* guesswork */
  110. { 0xF, -1, 11, "AMD64", }, /* guesswork */
  111. { -1, -1, 23, "unknown", }, /* total default */
  112. };
  113. /*
  114. * WinChip 240MHz
  115. */
  116. static X86type x86winchip[] =
  117. {
  118. {5, 4, 23, "Winchip",}, /* guesswork */
  119. {6, 7, 23, "Via C3 Samuel 2 or Ezra",},
  120. {6, 8, 23, "Via C3 Ezra-T",},
  121. { -1, -1, 23, "unknown", }, /* total default */
  122. };
  123. /*
  124. * SiS 55x
  125. */
  126. static X86type x86sis[] =
  127. {
  128. {5, 0, 23, "SiS 55x",}, /* guesswork */
  129. { -1, -1, 23, "unknown", }, /* total default */
  130. };
  131. static X86type *cputype;
  132. static void
  133. simplecycles(uvlong*x)
  134. {
  135. *x = m->ticks;
  136. }
  137. void (*cycles)(uvlong*) = simplecycles;
  138. void _cycles(uvlong*); /* in l.s */
  139. static void
  140. nop(void)
  141. {
  142. }
  143. void (*coherence)(void) = nop;
  144. /*
  145. * delay for l milliseconds more or less. delayloop is set by
  146. * clockinit() to match the actual CPU speed.
  147. */
  148. void
  149. delay(int l)
  150. {
  151. l *= loopconst;
  152. if(l <= 0)
  153. l = 1;
  154. aamloop(l);
  155. }
  156. /*
  157. * microsecond delay
  158. */
  159. void
  160. microdelay(int l)
  161. {
  162. l *= loopconst;
  163. l /= 1000;
  164. if(l <= 0)
  165. l = 1;
  166. aamloop(l);
  167. }
  168. void
  169. printcpufreq(void)
  170. {
  171. int i;
  172. char buf[128];
  173. i = sprint(buf, "cpu%d: %dMHz ", 0, cpumhz);
  174. if(cpuidid[0])
  175. i += sprint(buf+i, "%s ", cpuidid);
  176. sprint(buf+i, "%s (cpuid: AX 0x%4.4ux DX 0x%4.4ux)\n",
  177. cputype->name, cpuidax, cpuiddx);
  178. print(buf);
  179. }
  180. void
  181. clockinit(void)
  182. {
  183. int x, y; /* change in counter */
  184. int family, model, loops, incr;
  185. X86type *t;
  186. uvlong a, b;
  187. /*
  188. * set vector for clock interrupts
  189. */
  190. setvec(Clockvec, clockintr, 0);
  191. /*
  192. * figure out what we are
  193. */
  194. cpuid(cpuidid, &cpuidax, &cpuiddx);
  195. if(strncmp(cpuidid, "AuthenticAMD", 12) == 0)
  196. t = x86amd;
  197. else if(strncmp(cpuidid, "CentaurHauls", 12) == 0)
  198. t = x86winchip;
  199. else if(strncmp(cpuidid, "SiS SiS SiS ", 12) == 0)
  200. t = x86sis;
  201. else
  202. t = x86intel;
  203. family = FAMILY(cpuidax);
  204. model = MODEL(cpuidax);
  205. while(t->name){
  206. if((t->family == family && t->model == model)
  207. || (t->family == family && t->model == -1)
  208. || (t->family == -1))
  209. break;
  210. t++;
  211. }
  212. cputype = t;
  213. if(family >= 5)
  214. coherence = wbflush;
  215. /*
  216. * if there is one, set tsc to a known value
  217. */
  218. if(cpuiddx & 0x10){
  219. havetsc = 1;
  220. cycles = _cycles;
  221. if(cpuiddx & 0x20)
  222. wrmsr(0x10, 0);
  223. }
  224. /*
  225. * set clock for 1/HZ seconds
  226. */
  227. outb(Tmode, Load0|Square);
  228. outb(T0cntr, (Freq/HZ)); /* low byte */
  229. outb(T0cntr, (Freq/HZ)>>8); /* high byte */
  230. /*
  231. * Introduce a little delay to make sure the count is
  232. * latched and the timer is counting down; with a fast
  233. * enough processor this may not be the case.
  234. * The i8254 (which this probably is) has a read-back
  235. * command which can be used to make sure the counting
  236. * register has been written into the counting element.
  237. */
  238. x = (Freq/HZ);
  239. for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
  240. outb(Tmode, Latch0);
  241. x = inb(T0cntr);
  242. x |= inb(T0cntr)<<8;
  243. }
  244. /* find biggest loop that doesn't wrap */
  245. incr = 16000000/(t->aalcycles*HZ*2);
  246. x = 2000;
  247. for(loops = incr; loops < 64*1024; loops += incr) {
  248. /*
  249. * measure time for the loop
  250. *
  251. * MOVL loops,CX
  252. * aaml1: AAM
  253. * LOOP aaml1
  254. *
  255. * the time for the loop should be independent of external
  256. * cache and memory system since it fits in the execution
  257. * prefetch buffer.
  258. *
  259. */
  260. outb(Tmode, Latch0);
  261. cycles(&a);
  262. x = inb(T0cntr);
  263. x |= inb(T0cntr)<<8;
  264. aamloop(loops);
  265. outb(Tmode, Latch0);
  266. cycles(&b);
  267. y = inb(T0cntr);
  268. y |= inb(T0cntr)<<8;
  269. x -= y;
  270. if(x < 0)
  271. x += Freq/HZ;
  272. if(x > Freq/(3*HZ))
  273. break;
  274. }
  275. /*
  276. * figure out clock frequency and a loop multiplier for delay().
  277. * n.b. counter goes up by 2*Freq
  278. */
  279. cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x);
  280. loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */
  281. if (0)
  282. print("loops %d x %d cpufreq %,lld loopconst %d\n", loops, x, cpufreq, loopconst);
  283. if(havetsc){
  284. /* counter goes up by 2*Freq */
  285. b = (b-a)<<1;
  286. b *= Freq;
  287. b /= x;
  288. /*
  289. * round to the nearest megahz
  290. */
  291. cpumhz = (b+500000)/1000000L;
  292. cpuhz = b;
  293. } else {
  294. /*
  295. * add in possible 0.5% error and convert to MHz
  296. */
  297. cpumhz = (cpufreq + cpufreq/200)/1000000;
  298. cpuhz = cpufreq;
  299. }
  300. if (0) {
  301. print("cpuhz %,lld cpumhz %d\n", cpuhz, cpumhz);
  302. delay(10*1000);
  303. }
  304. }
  305. void
  306. clockreload(Timet n)
  307. {
  308. USED(n);
  309. }