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. typedef struct I8253 I8253;
  52. struct I8253
  53. {
  54. Lock;
  55. ulong period; /* current clock period */
  56. int enabled;
  57. uvlong hz;
  58. ushort last; /* last value of clock 1 */
  59. uvlong ticks; /* cumulative ticks of counter 1 */
  60. ulong periodset;
  61. };
  62. I8253 i8253;
  63. void
  64. i8253init(void)
  65. {
  66. int loops, x;
  67. ioalloc(T0cntr, 4, 0, "i8253");
  68. ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
  69. i8253.period = Freq/HZ;
  70. /*
  71. * enable a 1/HZ interrupt for providing scheduling interrupts
  72. */
  73. outb(Tmode, Load0|Square);
  74. outb(T0cntr, (Freq/HZ)); /* low byte */
  75. outb(T0cntr, (Freq/HZ)>>8); /* high byte */
  76. /*
  77. * enable a longer period counter to use as a clock
  78. */
  79. outb(Tmode, Load2|Square);
  80. outb(T2cntr, 0); /* low byte */
  81. outb(T2cntr, 0); /* high byte */
  82. x = inb(T2ctl);
  83. x |= T2gate;
  84. outb(T2ctl, x);
  85. /*
  86. * Introduce a little delay to make sure the count is
  87. * latched and the timer is counting down; with a fast
  88. * enough processor this may not be the case.
  89. * The i8254 (which this probably is) has a read-back
  90. * command which can be used to make sure the counting
  91. * register has been written into the counting element.
  92. */
  93. x = (Freq/HZ);
  94. for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
  95. outb(Tmode, Latch0);
  96. x = inb(T0cntr);
  97. x |= inb(T0cntr)<<8;
  98. }
  99. }
  100. void
  101. guesscpuhz(int aalcycles)
  102. {
  103. int loops, incr, x, y;
  104. uvlong a, b, cpufreq;
  105. /* find biggest loop that doesn't wrap */
  106. incr = 16000000/(aalcycles*HZ*2);
  107. x = 2000;
  108. for(loops = incr; loops < 64*1024; loops += incr) {
  109. /*
  110. * measure time for the loop
  111. *
  112. * MOVL loops,CX
  113. * aaml1: AAM
  114. * LOOP aaml1
  115. *
  116. * the time for the loop should be independent of external
  117. * cache and memory system since it fits in the execution
  118. * prefetch buffer.
  119. *
  120. */
  121. outb(Tmode, Latch0);
  122. cycles(&a);
  123. x = inb(T0cntr);
  124. x |= inb(T0cntr)<<8;
  125. aamloop(loops);
  126. outb(Tmode, Latch0);
  127. cycles(&b);
  128. y = inb(T0cntr);
  129. y |= inb(T0cntr)<<8;
  130. x -= y;
  131. if(x < 0)
  132. x += Freq/HZ;
  133. if(x > Freq/(3*HZ))
  134. break;
  135. }
  136. /*
  137. * figure out clock frequency and a loop multiplier for delay().
  138. * n.b. counter goes up by 2*Freq
  139. */
  140. cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
  141. m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */
  142. if(m->havetsc){
  143. /* counter goes up by 2*Freq */
  144. b = (b-a)<<1;
  145. b *= Freq;
  146. b /= x;
  147. /*
  148. * round to the nearest megahz
  149. */
  150. m->cpumhz = (b+500000)/1000000L;
  151. m->cpuhz = b;
  152. m->cyclefreq = b;
  153. } else {
  154. /*
  155. * add in possible 0.5% error and convert to MHz
  156. */
  157. m->cpumhz = (cpufreq + cpufreq/200)/1000000;
  158. m->cpuhz = cpufreq;
  159. }
  160. i8253.hz = Freq<<Tickshift;
  161. }
  162. void
  163. i8253timerset(uvlong next)
  164. {
  165. long period;
  166. ulong want;
  167. ulong now;
  168. period = MaxPeriod;
  169. if(next != 0){
  170. want = next>>Tickshift;
  171. now = i8253.ticks; /* assuming whomever called us just did fastticks() */
  172. period = want - now;
  173. if(period < MinPeriod)
  174. period = MinPeriod;
  175. else if(period > MaxPeriod)
  176. period = MaxPeriod;
  177. }
  178. /* hysteresis */
  179. if(i8253.period != period){
  180. ilock(&i8253);
  181. /* load new value */
  182. outb(Tmode, Load0|Square);
  183. outb(T0cntr, period); /* low byte */
  184. outb(T0cntr, period >> 8); /* high byte */
  185. /* remember period */
  186. i8253.period = period;
  187. i8253.periodset++;
  188. iunlock(&i8253);
  189. }
  190. }
  191. static void
  192. i8253clock(Ureg* ureg, void*)
  193. {
  194. timerintr(ureg, 0);
  195. }
  196. void
  197. i8253enable(void)
  198. {
  199. i8253.enabled = 1;
  200. i8253.period = Freq/HZ;
  201. intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
  202. }
  203. void
  204. i8253link(void)
  205. {
  206. }
  207. /*
  208. * return the total ticks of counter 2. We shift by
  209. * 8 to give timesync more wriggle room for interpretation
  210. * of the frequency
  211. */
  212. uvlong
  213. i8253read(uvlong *hz)
  214. {
  215. ushort y, x;
  216. uvlong ticks;
  217. if(hz)
  218. *hz = i8253.hz;
  219. ilock(&i8253);
  220. outb(Tmode, Latch2);
  221. y = inb(T2cntr);
  222. y |= inb(T2cntr)<<8;
  223. if(y < i8253.last)
  224. x = i8253.last - y;
  225. else {
  226. x = i8253.last + (0x10000 - y);
  227. if (x > 3*MaxPeriod) {
  228. outb(Tmode, Load2|Square);
  229. outb(T2cntr, 0); /* low byte */
  230. outb(T2cntr, 0); /* high byte */
  231. y = 0xFFFF;
  232. x = i8253.period;
  233. }
  234. }
  235. i8253.last = y;
  236. i8253.ticks += x>>1;
  237. ticks = i8253.ticks;
  238. iunlock(&i8253);
  239. return ticks<<Tickshift;
  240. }
  241. void
  242. delay(int millisecs)
  243. {
  244. millisecs *= m->loopconst;
  245. if(millisecs <= 0)
  246. millisecs = 1;
  247. aamloop(millisecs);
  248. }
  249. void
  250. microdelay(int microsecs)
  251. {
  252. microsecs *= m->loopconst;
  253. microsecs /= 1000;
  254. if(microsecs <= 0)
  255. microsecs = 1;
  256. aamloop(microsecs);
  257. }
  258. /*
  259. * performance measurement ticks. must be low overhead.
  260. * doesn't have to count over a second.
  261. */
  262. ulong
  263. perfticks(void)
  264. {
  265. uvlong x;
  266. if(m->havetsc)
  267. cycles(&x);
  268. else
  269. x = 0;
  270. return x;
  271. }