The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/pc/i8253.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 22b481a235c684a1d7d9767be2e27277


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.