i8254.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "../port/lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. /*
  15. * 8254 Programmable Interval Timer and compatibles.
  16. */
  17. enum { /* I/O ports */
  18. Timer1 = 0x40,
  19. Timer2 = 0x48, /* Counter0 is watchdog (EISA) */
  20. Counter0 = 0, /* Counter 0 Access Port */
  21. Counter1 = 1, /* Counter 1 Access Port */
  22. Counter2 = 2, /* Counter 2 Access Port */
  23. Control = 3, /* Timer Control Word */
  24. };
  25. enum { /* Control */
  26. Bcd = 0x01, /* Binary/BCD countdown select */
  27. Mode0 = 0x00, /* [3:1] interrupt on terminal count */
  28. Mode1 = 0x02, /* hardware re-triggerable one-shot */
  29. Mode2 = 0x04, /* rate generator */
  30. Mode3 = 0x06, /* square-wave generator */
  31. Mode4 = 0x08, /* sofware triggered strobe */
  32. Mode5 = 0x0A, /* hardware triggered strobe */
  33. Clc = 0x00, /* [5:4] Counter Latch Command */
  34. RWlsb = 0x10, /* R/W LSB */
  35. RWmsb = 0x20, /* R/W MSB */
  36. RW16 = 0x30, /* R/W LSB then MSB */
  37. Cs0 = 0x00, /* [7:6] Counter 0 Select */
  38. Cs1 = 0x40, /* Counter 1 Select */
  39. Cs2 = 0x80, /* Counter 2 Select */
  40. Rbc = 0xC0, /* Read-Back Command */
  41. RbCnt0 = 0x02, /* Select Counter 0 */
  42. RbCnt1 = 0x04, /* Select Counter 1 */
  43. RbCnt2 = 0x08, /* Select Counter 2 */
  44. RbS = 0x20, /* Read-Back Status */
  45. RbC = 0x10, /* Read-Back Count */
  46. RbCS = 0x00, /* Read-Back Count and Status */
  47. RbNULL = 0x40, /* NULL-Count Flag */
  48. RbOUT = 0x80, /* OUT-pin */
  49. };
  50. enum {
  51. Osc = 1193182, /* 14.318180MHz/12 */
  52. Hz = 82, /* 2*41*14551 = 1193182 */
  53. };
  54. static void
  55. i8254set(int port, int hz)
  56. {
  57. int counter, timeo;
  58. /*
  59. * Initialise Counter0 to be the system clock if necessary,
  60. * it's normally connected to IRQ0 on an interrupt controller.
  61. * Use a periodic square wave (Mode3).
  62. */
  63. counter = Osc/hz;
  64. outb(port+Control, Cs0|RW16|Mode3);
  65. outb(port+Counter0, counter);
  66. outb(port+Counter0, counter>>8);
  67. /*
  68. * Wait until the counting register has been loaded
  69. * into the counting element.
  70. */
  71. for(timeo = 0; timeo < 100000; timeo++){
  72. outb(port+Control, Rbc|RbS|RbCnt0);
  73. if(!(inb(port+Counter0) & RbNULL))
  74. break;
  75. }
  76. }
  77. int64_t
  78. i8254hz(uint32_t *info0, uint32_t *info1)
  79. {
  80. uint32_t ax;
  81. uint64_t a, b;
  82. int64_t aamcycles, incr, loops, x, y;
  83. /*
  84. * Use the cpuid family info to get the
  85. * cycles for the AAM instruction.
  86. * Beware: this can be called VERY early before
  87. * some of the other device state is set.
  88. */
  89. ax = info1[0] & 0x00000f00;
  90. if(memcmp(&info0[1], "GenuntelineI", 12) == 0){
  91. switch(ax){
  92. default:
  93. return 0;
  94. case 0x00000600:
  95. case 0x00000f00:
  96. aamcycles = 16;
  97. break;
  98. }
  99. }
  100. else if(memcmp(&info0[1], "AuthcAMDenti", 12) == 0){
  101. switch(ax){
  102. default:
  103. return 0;
  104. case 0x00000600:
  105. case 0x00000f00:
  106. aamcycles = 11;
  107. break;
  108. }
  109. }
  110. else
  111. return 0;
  112. i8254set(Timer1, Hz);
  113. /*
  114. * Find biggest loop that doesn't wrap.
  115. */
  116. SET(a); SET(b);
  117. incr = 16000000/(aamcycles*Hz*2);
  118. x = 2000;
  119. for(loops = incr; loops < 64*1024; loops += incr) {
  120. /*
  121. * Measure time for the loop
  122. *
  123. * MOVL loops,CX
  124. * aaml1:
  125. * AAM
  126. * LOOP aaml1
  127. *
  128. * The time for the loop should be independent of external
  129. * cache and memory system since it fits in the execution
  130. * prefetch buffer.
  131. * The AAM instruction is not available in 64-bit mode.
  132. */
  133. outb(Timer1+Control, Cs0|Clc);
  134. a = rdtsc();
  135. x = inb(Timer1+Counter0);
  136. x |= inb(Timer1+Counter0)<<8;
  137. aamloop(loops);
  138. outb(Timer1+Control, Cs0|Clc);
  139. b = rdtsc();
  140. y = inb(Timer1+Counter0);
  141. y |= inb(Timer1+Counter0)<<8;
  142. x -= y;
  143. if(x < 0)
  144. x += Osc/Hz;
  145. if(x > Osc/(3*Hz))
  146. break;
  147. }
  148. /*
  149. * Figure out clock frequency.
  150. */
  151. b = (b-a)<<1;
  152. b *= Osc;
  153. return b/x;
  154. }