i8253.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. #include "u.h"
  2. #include "../port/lib.h"
  3. #include "mem.h"
  4. #include "dat.h"
  5. #include "fns.h"
  6. #include "io.h"
  7. /*
  8. * 8253 timer
  9. */
  10. enum
  11. {
  12. T0cntr= 0x40, /* counter ports */
  13. T1cntr= 0x41, /* ... */
  14. T2cntr= 0x42, /* ... */
  15. Tmode= 0x43, /* mode port (control word register) */
  16. T2ctl= 0x61, /* counter 2 control port */
  17. /* commands */
  18. Latch0= 0x00, /* latch counter 0's value */
  19. Load0l= 0x10, /* load counter 0's lsb */
  20. Load0m= 0x20, /* load counter 0's msb */
  21. Load0= 0x30, /* load counter 0 with 2 bytes */
  22. Latch1= 0x40, /* latch counter 1's value */
  23. Load1l= 0x50, /* load counter 1's lsb */
  24. Load1m= 0x60, /* load counter 1's msb */
  25. Load1= 0x70, /* load counter 1 with 2 bytes */
  26. Latch2= 0x80, /* latch counter 2's value */
  27. Load2l= 0x90, /* load counter 2's lsb */
  28. Load2m= 0xa0, /* load counter 2's msb */
  29. Load2= 0xb0, /* load counter 2 with 2 bytes */
  30. /* 8254 read-back command: everything > pc-at has an 8254 */
  31. Rdback= 0xc0, /* readback counters & status */
  32. Rdnstat=0x10, /* don't read status */
  33. Rdncnt= 0x20, /* don't read counter value */
  34. Rd0cntr=0x02, /* read back for which counter */
  35. Rd1cntr=0x04,
  36. Rd2cntr=0x08,
  37. /* modes */
  38. ModeMsk=0xe,
  39. Square= 0x6, /* periodic square wave */
  40. Trigger=0x0, /* interrupt on terminal count */
  41. Sstrobe=0x8, /* software triggered strobe */
  42. /* T2ctl bits */
  43. T2gate= (1<<0), /* enable T2 counting */
  44. T2spkr= (1<<1), /* connect T2 out to speaker */
  45. T2out= (1<<5), /* output of T2 */
  46. Freq= 1193182, /* Real clock frequency */
  47. Tickshift=8, /* extra accuracy */
  48. MaxPeriod=Freq/HZ,
  49. MinPeriod=Freq/(100*HZ),
  50. };
  51. static struct
  52. {
  53. Lock;
  54. ulong period; /* current clock period */
  55. int enabled;
  56. uvlong hz;
  57. ushort last; /* last value of clock 1 */
  58. uvlong ticks; /* cumulative ticks of counter 1 */
  59. }i8253;
  60. void
  61. i8253init(void)
  62. {
  63. int loops, x;
  64. ioalloc(T0cntr, 4, 0, "i8253");
  65. ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
  66. i8253.period = Freq/HZ;
  67. /*
  68. * enable a 1/HZ interrupt for providing scheduling interrupts
  69. */
  70. outb(Tmode, Load0|Square);
  71. outb(T0cntr, (Freq/HZ)); /* low byte */
  72. outb(T0cntr, (Freq/HZ)>>8); /* high byte */
  73. /*
  74. * enable a longer period counter to use as a clock
  75. */
  76. outb(Tmode, Load2|Square);
  77. outb(T2cntr, 0); /* low byte */
  78. outb(T2cntr, 0); /* high byte */
  79. x = inb(T2ctl);
  80. x |= T2gate;
  81. outb(T2ctl, x);
  82. /*
  83. * Introduce a little delay to make sure the count is
  84. * latched and the timer is counting down; with a fast
  85. * enough processor this may not be the case.
  86. * The i8254 (which this probably is) has a read-back
  87. * command which can be used to make sure the counting
  88. * register has been written into the counting element.
  89. */
  90. x = (Freq/HZ);
  91. for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
  92. outb(Tmode, Latch0);
  93. x = inb(T0cntr);
  94. x |= inb(T0cntr)<<8;
  95. }
  96. }
  97. void
  98. guesscpuhz(int aalcycles)
  99. {
  100. int cpufreq, loops, incr, x, y;
  101. uvlong a, b;
  102. /* find biggest loop that doesn't wrap */
  103. incr = 16000000/(aalcycles*HZ*2);
  104. x = 2000;
  105. for(loops = incr; loops < 64*1024; loops += incr) {
  106. /*
  107. * measure time for the loop
  108. *
  109. * MOVL loops,CX
  110. * aaml1: AAM
  111. * LOOP aaml1
  112. *
  113. * the time for the loop should be independent of external
  114. * cache and memory system since it fits in the execution
  115. * prefetch buffer.
  116. *
  117. */
  118. outb(Tmode, Latch0);
  119. if(m->havetsc)
  120. rdtsc(&a);
  121. x = inb(T0cntr);
  122. x |= inb(T0cntr)<<8;
  123. aamloop(loops);
  124. outb(Tmode, Latch0);
  125. if(m->havetsc)
  126. rdtsc(&b);
  127. y = inb(T0cntr);
  128. y |= inb(T0cntr)<<8;
  129. x -= y;
  130. if(x < 0)
  131. x += Freq/HZ;
  132. if(x > Freq/(3*HZ))
  133. break;
  134. }
  135. /*
  136. * figure out clock frequency and a loop multiplier for delay().
  137. * n.b. counter goes up by 2*Freq
  138. */
  139. cpufreq = loops*((aalcycles*2*Freq)/x);
  140. m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */
  141. if(m->havetsc){
  142. /* counter goes up by 2*Freq */
  143. b = (b-a)<<1;
  144. b *= Freq;
  145. b /= x;
  146. /*
  147. * round to the nearest megahz
  148. */
  149. m->cpumhz = (b+500000)/1000000L;
  150. m->cpuhz = b;
  151. } else {
  152. /*
  153. * add in possible 0.5% error and convert to MHz
  154. */
  155. m->cpumhz = (cpufreq + cpufreq/200)/1000000;
  156. m->cpuhz = cpufreq;
  157. }
  158. i8253.hz = Freq<<Tickshift;
  159. }
  160. ulong i8253periodset;
  161. int i8253dotimerset = 1;
  162. ulong phist[128];
  163. void
  164. i8253timerset(uvlong next)
  165. {
  166. long period;
  167. ulong want;
  168. ulong now;
  169. if(i8253dotimerset == 0)
  170. return;
  171. want = next>>Tickshift;
  172. now = i8253.ticks; /* assuming whomever called us just did fastticks() */
  173. period = MaxPeriod;
  174. if(next != 0){
  175. period = want - now;
  176. if(period < MinPeriod)
  177. period = MinPeriod;
  178. else if(period > (4*MaxPeriod)/5) /* strong attraction to MaxPeriod */
  179. period = MaxPeriod;
  180. }
  181. /* hysteresis */
  182. if(i8253.period != period){
  183. memmove(&phist[0], &phist[1], sizeof(phist)-sizeof(ulong));
  184. phist[nelem(phist)-1] = period;
  185. ilock(&i8253);
  186. /* load new value */
  187. outb(Tmode, Load0|Square);
  188. outb(T0cntr, period); /* low byte */
  189. outb(T0cntr, period >> 8); /* high byte */
  190. /* remember period */
  191. i8253.period = period;
  192. i8253periodset++;
  193. iunlock(&i8253);
  194. }
  195. }
  196. static void
  197. i8253clock(Ureg* ureg, void*)
  198. {
  199. timerintr(ureg, 0);
  200. }
  201. void
  202. i8253enable(void)
  203. {
  204. i8253.enabled = 1;
  205. i8253.period = Freq/HZ;
  206. intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
  207. }
  208. void
  209. i8253link(void)
  210. {
  211. }
  212. /*
  213. * return the total ticks of counter 1. We shift by
  214. * 8 to give timesync more wriggle room for interpretation
  215. * of the frequency
  216. */
  217. uvlong
  218. i8253read(uvlong *hz)
  219. {
  220. ushort y, x;
  221. uvlong ticks;
  222. if(hz)
  223. *hz = i8253.hz;
  224. ilock(&i8253);
  225. outb(Tmode, Latch2);
  226. y = inb(T2cntr);
  227. y |= inb(T2cntr)<<8;
  228. if(y < i8253.last)
  229. x = i8253.last - y;
  230. else
  231. x = i8253.last + (0x10000 - y);
  232. i8253.last = y;
  233. i8253.ticks += x>>1;
  234. ticks = i8253.ticks;
  235. iunlock(&i8253);
  236. return ticks<<Tickshift;
  237. }
  238. void
  239. delay(int millisecs)
  240. {
  241. millisecs *= m->loopconst;
  242. if(millisecs <= 0)
  243. millisecs = 1;
  244. aamloop(millisecs);
  245. }
  246. void
  247. microdelay(int microsecs)
  248. {
  249. microsecs *= m->loopconst;
  250. microsecs /= 1000;
  251. if(microsecs <= 0)
  252. microsecs = 1;
  253. aamloop(microsecs);
  254. }
  255. /*
  256. * performance measurement ticks. must be low overhead.
  257. * doesn't have to count over a second.
  258. */
  259. ulong
  260. perfticks(void)
  261. {
  262. uvlong x;
  263. if(!m->havetsc)
  264. return m->ticks;
  265. rdtsc(&x);
  266. return x;
  267. }