Merge from vendor branch OPENSSH:
[dragonfly.git] / sys / platform / pc64 / apic / mpapic.c
1 /*
2  * Copyright (c) 1996, by Steve Passe
3  * Copyright (c) 2008 The DragonFly Project.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. The name of the developer may NOT be used to endorse or promote products
12  *    derived from this software without specific prior written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
27  * $DragonFly: src/sys/platform/pc64/apic/mpapic.c,v 1.1 2008/08/29 17:07:12 dillon Exp $
28  */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <machine/globaldata.h>
33 #include <machine/smp.h>
34 #include <machine_base/apic/mpapic.h>
35 #include <machine/segments.h>
36 #include <sys/thread2.h>
37
38 #include <machine_base/isa/intr_machdep.h>      /* Xspuriousint() */
39
40 /* EISA Edge/Level trigger control registers */
41 #define ELCR0   0x4d0                   /* eisa irq 0-7 */
42 #define ELCR1   0x4d1                   /* eisa irq 8-15 */
43
44 /*
45  * pointers to pmapped apic hardware.
46  */
47
48 volatile ioapic_t       **ioapic;
49
50 /*
51  * Enable APIC, configure interrupts.
52  */
53 void
54 apic_initialize(void)
55 {
56         u_int   temp;
57
58         /*
59          * setup LVT1 as ExtINT on the BSP.  This is theoretically an
60          * aggregate interrupt input from the 8259.  The INTA cycle
61          * will be routed to the external controller (the 8259) which
62          * is expected to supply the vector.
63          *
64          * Must be setup edge triggered, active high.
65          *
66          * Disable LVT1 on the APs.  It doesn't matter what delivery
67          * mode we use because we leave it masked.
68          */
69         temp = lapic.lvt_lint0;
70         temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK | 
71                   APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK);
72         if (mycpu->gd_cpuid == 0)
73                 temp |= APIC_LVT_DM_EXTINT;
74         else
75                 temp |= APIC_LVT_DM_FIXED | APIC_LVT_MASKED;
76         lapic.lvt_lint0 = temp;
77
78         /*
79          * setup LVT2 as NMI, masked till later.  Edge trigger, active high.
80          */
81         temp = lapic.lvt_lint1;
82         temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK | 
83                   APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK);
84         temp |= APIC_LVT_MASKED | APIC_LVT_DM_NMI;
85         lapic.lvt_lint1 = temp;
86
87         /*
88          * Mask the apic error interrupt, apic performance counter
89          * interrupt, and the apic timer interrupt.
90          */
91         lapic.lvt_error = lapic.lvt_error | APIC_LVT_MASKED;
92         lapic.lvt_pcint = lapic.lvt_pcint | APIC_LVT_MASKED;
93         lapic.lvt_timer = lapic.lvt_timer | APIC_LVT_MASKED;
94
95         /*
96          * Set the Task Priority Register as needed.   At the moment allow
97          * interrupts on all cpus (the APs will remain CLId until they are
98          * ready to deal).  We could disable all but IPIs by setting
99          * temp |= TPR_IPI_ONLY for cpu != 0.
100          */
101         temp = lapic.tpr;
102         temp &= ~APIC_TPR_PRIO;         /* clear priority field */
103 #ifndef APIC_IO
104         /*
105          * If we are NOT running the IO APICs, the LAPIC will only be used
106          * for IPIs.  Set the TPR to prevent any unintentional interrupts.
107          */
108         temp |= TPR_IPI_ONLY;
109 #endif
110
111         lapic.tpr = temp;
112
113         /* 
114          * enable the local APIC 
115          */
116         temp = lapic.svr;
117         temp |= APIC_SVR_ENABLE;        /* enable the APIC */
118         temp &= ~APIC_SVR_FOCUS_DISABLE; /* enable lopri focus processor */
119
120         /*
121          * Set the spurious interrupt vector.  The low 4 bits of the vector
122          * must be 1111.
123          */
124         if ((XSPURIOUSINT_OFFSET & 0x0F) != 0x0F)
125                 panic("bad XSPURIOUSINT_OFFSET: 0x%08x", XSPURIOUSINT_OFFSET);
126         temp &= ~APIC_SVR_VECTOR;
127         temp |= XSPURIOUSINT_OFFSET;
128
129         lapic.svr = temp;
130
131         /*
132          * Pump out a few EOIs to clean out interrupts that got through
133          * before we were able to set the TPR.
134          */
135         lapic.eoi = 0;
136         lapic.eoi = 0;
137         lapic.eoi = 0;
138
139         if (bootverbose)
140                 apic_dump("apic_initialize()");
141 }
142
143
144 /*
145  * dump contents of local APIC registers
146  */
147 void
148 apic_dump(char* str)
149 {
150         kprintf("SMP: CPU%d %s:\n", mycpu->gd_cpuid, str);
151         kprintf("     lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n",
152                 lapic.lvt_lint0, lapic.lvt_lint1, lapic.tpr, lapic.svr);
153 }
154
155
156 #if defined(APIC_IO)
157
158 /*
159  * IO APIC code,
160  */
161
162 #define IOAPIC_ISA_INTS         16
163 #define REDIRCNT_IOAPIC(A) \
164             ((int)((io_apic_versions[(A)] & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1)
165
166 static int trigger (int apic, int pin, u_int32_t * flags);
167 static void polarity (int apic, int pin, u_int32_t * flags, int level);
168
169 #define DEFAULT_FLAGS           \
170         ((u_int32_t)            \
171          (IOART_INTMSET |       \
172           IOART_DESTPHY |       \
173           IOART_DELLOPRI))
174
175 #define DEFAULT_ISA_FLAGS       \
176         ((u_int32_t)            \
177          (IOART_INTMSET |       \
178           IOART_TRGREDG |       \
179           IOART_INTAHI |        \
180           IOART_DESTPHY |       \
181           IOART_DELLOPRI))
182
183 void
184 io_apic_set_id(int apic, int id)
185 {
186         u_int32_t ux;
187         
188         ux = io_apic_read(apic, IOAPIC_ID);     /* get current contents */
189         if (((ux & APIC_ID_MASK) >> 24) != id) {
190                 kprintf("Changing APIC ID for IO APIC #%d"
191                        " from %d to %d on chip\n",
192                        apic, ((ux & APIC_ID_MASK) >> 24), id);
193                 ux &= ~APIC_ID_MASK;    /* clear the ID field */
194                 ux |= (id << 24);
195                 io_apic_write(apic, IOAPIC_ID, ux);     /* write new value */
196                 ux = io_apic_read(apic, IOAPIC_ID);     /* re-read && test */
197                 if (((ux & APIC_ID_MASK) >> 24) != id)
198                         panic("can't control IO APIC #%d ID, reg: 0x%08x",
199                               apic, ux);
200         }
201 }
202
203
204 int
205 io_apic_get_id(int apic)
206 {
207   return (io_apic_read(apic, IOAPIC_ID) & APIC_ID_MASK) >> 24;
208 }
209   
210
211
212 /*
213  * Setup the IO APIC.
214  */
215
216 extern int      apic_pin_trigger;       /* 'opaque' */
217
218 void
219 io_apic_setup_intpin(int apic, int pin)
220 {
221         int bus, bustype, irq;
222         u_char          select;         /* the select register is 8 bits */
223         u_int32_t       flags;          /* the window register is 32 bits */
224         u_int32_t       target;         /* the window register is 32 bits */
225         u_int32_t       vector;         /* the window register is 32 bits */
226         int             level;
227
228         select = pin * 2 + IOAPIC_REDTBL0;      /* register */
229
230         /*
231          * Always clear an IO APIC pin before [re]programming it.  This is
232          * particularly important if the pin is set up for a level interrupt
233          * as the IOART_REM_IRR bit might be set.   When we reprogram the
234          * vector any EOI from pending ints on this pin could be lost and
235          * IRR might never get reset.
236          *
237          * To fix this problem, clear the vector and make sure it is 
238          * programmed as an edge interrupt.  This should theoretically
239          * clear IRR so we can later, safely program it as a level 
240          * interrupt.
241          */
242         imen_lock();
243
244         flags = io_apic_read(apic, select) & IOART_RESV;
245         flags |= IOART_INTMSET | IOART_TRGREDG | IOART_INTAHI;
246         flags |= IOART_DESTPHY | IOART_DELFIXED;
247
248         target = io_apic_read(apic, select + 1) & IOART_HI_DEST_RESV;
249         target |= 0;    /* fixed mode cpu mask of 0 - don't deliver anywhere */
250
251         vector = 0;
252
253         io_apic_write(apic, select, flags | vector);
254         io_apic_write(apic, select + 1, target);
255
256         imen_unlock();
257
258         /*
259          * We only deal with vectored interrupts here.  ? documentation is
260          * lacking, I'm guessing an interrupt type of 0 is the 'INT' type,
261          * vs ExTINT, etc.
262          *
263          * This test also catches unconfigured pins.
264          */
265         if (apic_int_type(apic, pin) != 0)
266                 return;
267
268         /*
269          * Leave the pin unprogrammed if it does not correspond to
270          * an IRQ.
271          */
272         irq = apic_irq(apic, pin);
273         if (irq < 0)
274                 return;
275         
276         /* determine the bus type for this pin */
277         bus = apic_src_bus_id(apic, pin);
278         if (bus < 0)
279                 return;
280         bustype = apic_bus_type(bus);
281         
282         if ((bustype == ISA) &&
283             (pin < IOAPIC_ISA_INTS) && 
284             (irq == pin) &&
285             (apic_polarity(apic, pin) == 0x1) &&
286             (apic_trigger(apic, pin) == 0x3)) {
287                 /* 
288                  * A broken BIOS might describe some ISA 
289                  * interrupts as active-high level-triggered.
290                  * Use default ISA flags for those interrupts.
291                  */
292                 flags = DEFAULT_ISA_FLAGS;
293         } else {
294                 /* 
295                  * Program polarity and trigger mode according to 
296                  * interrupt entry.
297                  */
298                 flags = DEFAULT_FLAGS;
299                 level = trigger(apic, pin, &flags);
300                 if (level == 1)
301                         apic_pin_trigger |= (1 << irq);
302                 polarity(apic, pin, &flags, level);
303         }
304         
305         if (bootverbose) {
306                 kprintf("IOAPIC #%d intpin %d -> irq %d\n",
307                        apic, pin, irq);
308         }
309
310         /*
311          * Program the appropriate registers.  This routing may be 
312          * overridden when an interrupt handler for a device is
313          * actually added (see register_int(), which calls through
314          * the MACHINTR ABI to set up an interrupt handler/vector).
315          *
316          * The order in which we must program the two registers for
317          * safety is unclear! XXX
318          */
319         imen_lock();
320
321         vector = IDT_OFFSET + irq;                      /* IDT vec */
322         target = io_apic_read(apic, select + 1) & IOART_HI_DEST_RESV;
323         target |= IOART_HI_DEST_BROADCAST;
324         flags |= io_apic_read(apic, select) & IOART_RESV;
325         io_apic_write(apic, select, flags | vector);
326         io_apic_write(apic, select + 1, target);
327
328         imen_unlock();
329 }
330
331 int
332 io_apic_setup(int apic)
333 {
334         int             maxpin;
335         int             pin;
336
337         if (apic == 0)
338                 apic_pin_trigger = 0;   /* default to edge-triggered */
339
340         maxpin = REDIRCNT_IOAPIC(apic);         /* pins in APIC */
341         kprintf("Programming %d pins in IOAPIC #%d\n", maxpin, apic);
342         
343         for (pin = 0; pin < maxpin; ++pin) {
344                 io_apic_setup_intpin(apic, pin);
345         }
346         while (pin < 32) {
347                 if (apic_int_type(apic, pin) >= 0) {
348                         kprintf("Warning: IOAPIC #%d pin %d does not exist,"
349                                 " cannot program!\n", apic, pin);
350                 }
351                 ++pin;
352         }
353
354         /* return GOOD status */
355         return 0;
356 }
357 #undef DEFAULT_ISA_FLAGS
358 #undef DEFAULT_FLAGS
359
360
361 #define DEFAULT_EXTINT_FLAGS    \
362         ((u_int32_t)            \
363          (IOART_INTMSET |       \
364           IOART_TRGREDG |       \
365           IOART_INTAHI |        \
366           IOART_DESTPHY |       \
367           IOART_DELLOPRI))
368
369 /*
370  * Setup the source of External INTerrupts.
371  */
372 int
373 ext_int_setup(int apic, int intr)
374 {
375         u_char  select;         /* the select register is 8 bits */
376         u_int32_t flags;        /* the window register is 32 bits */
377         u_int32_t target;       /* the window register is 32 bits */
378         u_int32_t vector;       /* the window register is 32 bits */
379
380         if (apic_int_type(apic, intr) != 3)
381                 return -1;
382
383         target = IOART_HI_DEST_BROADCAST;
384         select = IOAPIC_REDTBL0 + (2 * intr);
385         vector = IDT_OFFSET + intr;
386         flags = DEFAULT_EXTINT_FLAGS;
387
388         io_apic_write(apic, select, flags | vector);
389         io_apic_write(apic, select + 1, target);
390
391         return 0;
392 }
393 #undef DEFAULT_EXTINT_FLAGS
394
395
396 /*
397  * Set the trigger level for an IO APIC pin.
398  */
399 static int
400 trigger(int apic, int pin, u_int32_t * flags)
401 {
402         int     id;
403         int     eirq;
404         int     level;
405         static int intcontrol = -1;
406
407         switch (apic_trigger(apic, pin)) {
408
409         case 0x00:
410                 break;
411
412         case 0x01:
413                 *flags &= ~IOART_TRGRLVL;       /* *flags |= IOART_TRGREDG */
414                 return 0;
415
416         case 0x03:
417                 *flags |= IOART_TRGRLVL;
418                 return 1;
419
420         case -1:
421         default:
422                 goto bad;
423         }
424
425         if ((id = apic_src_bus_id(apic, pin)) == -1)
426                 goto bad;
427
428         switch (apic_bus_type(id)) {
429         case ISA:
430                 *flags &= ~IOART_TRGRLVL;       /* *flags |= IOART_TRGREDG; */
431                 return 0;
432
433         case EISA:
434                 eirq = apic_src_bus_irq(apic, pin);
435
436                 if (eirq < 0 || eirq > 15) {
437                         kprintf("EISA IRQ %d?!?!\n", eirq);
438                         goto bad;
439                 }
440
441                 if (intcontrol == -1) {
442                         intcontrol = inb(ELCR1) << 8;
443                         intcontrol |= inb(ELCR0);
444                         kprintf("EISA INTCONTROL = %08x\n", intcontrol);
445                 }
446
447                 /* Use ELCR settings to determine level or edge mode */
448                 level = (intcontrol >> eirq) & 1;
449
450                 /*
451                  * Note that on older Neptune chipset based systems, any
452                  * pci interrupts often show up here and in the ELCR as well
453                  * as level sensitive interrupts attributed to the EISA bus.
454                  */
455
456                 if (level)
457                         *flags |= IOART_TRGRLVL;
458                 else
459                         *flags &= ~IOART_TRGRLVL;
460
461                 return level;
462
463         case PCI:
464                 *flags |= IOART_TRGRLVL;
465                 return 1;
466
467         case -1:
468         default:
469                 goto bad;
470         }
471
472 bad:
473         panic("bad APIC IO INT flags");
474 }
475
476
477 /*
478  * Set the polarity value for an IO APIC pin.
479  */
480 static void
481 polarity(int apic, int pin, u_int32_t * flags, int level)
482 {
483         int     id;
484
485         switch (apic_polarity(apic, pin)) {
486
487         case 0x00:
488                 break;
489
490         case 0x01:
491                 *flags &= ~IOART_INTALO;        /* *flags |= IOART_INTAHI */
492                 return;
493
494         case 0x03:
495                 *flags |= IOART_INTALO;
496                 return;
497
498         case -1:
499         default:
500                 goto bad;
501         }
502
503         if ((id = apic_src_bus_id(apic, pin)) == -1)
504                 goto bad;
505
506         switch (apic_bus_type(id)) {
507         case ISA:
508                 *flags &= ~IOART_INTALO;        /* *flags |= IOART_INTAHI */
509                 return;
510
511         case EISA:
512                 /* polarity converter always gives active high */
513                 *flags &= ~IOART_INTALO;
514                 return;
515
516         case PCI:
517                 *flags |= IOART_INTALO;
518                 return;
519
520         case -1:
521         default:
522                 goto bad;
523         }
524
525 bad:
526         panic("bad APIC IO INT flags");
527 }
528
529
530 /*
531  * Print contents of apic_imen.
532  */
533 extern  u_int apic_imen;                /* keep apic_imen 'opaque' */
534 void
535 imen_dump(void)
536 {
537         int x;
538
539         kprintf("SMP: enabled INTs: ");
540         for (x = 0; x < 24; ++x)
541                 if ((apic_imen & (1 << x)) == 0)
542                         kprintf("%d, ", x);
543         kprintf("apic_imen: 0x%08x\n", apic_imen);
544 }
545
546
547 /*
548  * Inter Processor Interrupt functions.
549  */
550
551 #endif  /* APIC_IO */
552
553 /*
554  * Send APIC IPI 'vector' to 'destType' via 'deliveryMode'.
555  *
556  *  destType is 1 of: APIC_DEST_SELF, APIC_DEST_ALLISELF, APIC_DEST_ALLESELF
557  *  vector is any valid SYSTEM INT vector
558  *  delivery_mode is 1 of: APIC_DELMODE_FIXED, APIC_DELMODE_LOWPRIO
559  *
560  * A backlog of requests can create a deadlock between cpus.  To avoid this
561  * we have to be able to accept IPIs at the same time we are trying to send
562  * them.  The critical section prevents us from attempting to send additional
563  * IPIs reentrantly, but also prevents IPIQ processing so we have to call
564  * lwkt_process_ipiq() manually.  It's rather messy and expensive for this
565  * to occur but fortunately it does not happen too often.
566  */
567 int
568 apic_ipi(int dest_type, int vector, int delivery_mode)
569 {
570         u_long  icr_lo;
571
572         crit_enter();
573         if ((lapic.icr_lo & APIC_DELSTAT_MASK) != 0) {
574             unsigned int eflags = read_eflags();
575             cpu_enable_intr();
576             while ((lapic.icr_lo & APIC_DELSTAT_MASK) != 0) {
577                 lwkt_process_ipiq();
578             }
579             write_eflags(eflags);
580         }
581
582         icr_lo = (lapic.icr_lo & APIC_ICRLO_RESV_MASK) | dest_type | 
583                 delivery_mode | vector;
584         lapic.icr_lo = icr_lo;
585         crit_exit();
586         return 0;
587 }
588
589 void
590 single_apic_ipi(int cpu, int vector, int delivery_mode)
591 {
592         u_long  icr_lo;
593         u_long  icr_hi;
594
595         crit_enter();
596         if ((lapic.icr_lo & APIC_DELSTAT_MASK) != 0) {
597             unsigned int eflags = read_eflags();
598             cpu_enable_intr();
599             while ((lapic.icr_lo & APIC_DELSTAT_MASK) != 0) {
600                 lwkt_process_ipiq();
601             }
602             write_eflags(eflags);
603         }
604         icr_hi = lapic.icr_hi & ~APIC_ID_MASK;
605         icr_hi |= (CPU_TO_ID(cpu) << 24);
606         lapic.icr_hi = icr_hi;
607
608         /* build IRC_LOW */
609         icr_lo = (lapic.icr_lo & APIC_ICRLO_RESV_MASK)
610             | APIC_DEST_DESTFLD | delivery_mode | vector;
611
612         /* write APIC ICR */
613         lapic.icr_lo = icr_lo;
614         crit_exit();
615 }
616
617 #if 0   
618
619 /*
620  * Returns 0 if the apic is busy, 1 if we were able to queue the request.
621  *
622  * NOT WORKING YET!  The code as-is may end up not queueing an IPI at all
623  * to the target, and the scheduler does not 'poll' for IPI messages.
624  */
625 int
626 single_apic_ipi_passive(int cpu, int vector, int delivery_mode)
627 {
628         u_long  icr_lo;
629         u_long  icr_hi;
630
631         crit_enter();
632         if ((lapic.icr_lo & APIC_DELSTAT_MASK) != 0) {
633             crit_exit();
634             return(0);
635         }
636         icr_hi = lapic.icr_hi & ~APIC_ID_MASK;
637         icr_hi |= (CPU_TO_ID(cpu) << 24);
638         lapic.icr_hi = icr_hi;
639
640         /* build IRC_LOW */
641         icr_lo = (lapic.icr_lo & APIC_RESV2_MASK)
642             | APIC_DEST_DESTFLD | delivery_mode | vector;
643
644         /* write APIC ICR */
645         lapic.icr_lo = icr_lo;
646         crit_exit();
647         return(1);
648 }
649
650 #endif
651
652 /*
653  * Send APIC IPI 'vector' to 'target's via 'delivery_mode'.
654  *
655  * target is a bitmask of destination cpus.  Vector is any
656  * valid system INT vector.  Delivery mode may be either
657  * APIC_DELMODE_FIXED or APIC_DELMODE_LOWPRIO.
658  */
659 void
660 selected_apic_ipi(u_int target, int vector, int delivery_mode)
661 {
662         crit_enter();
663         while (target) {
664                 int n = bsfl(target);
665                 target &= ~(1 << n);
666                 single_apic_ipi(n, vector, delivery_mode);
667         }
668         crit_exit();
669 }
670
671 /*
672  * Timer code, in development...
673  *  - suggested by rgrimes@gndrsh.aac.dev.com
674  */
675
676 /** XXX FIXME: temp hack till we can determin bus clock */
677 #ifndef BUS_CLOCK
678 #define BUS_CLOCK       66000000
679 #define bus_clock()     66000000
680 #endif
681
682 #if defined(READY)
683 int acquire_apic_timer (void);
684 int release_apic_timer (void);
685
686 /*
687  * Acquire the APIC timer for exclusive use.
688  */
689 int
690 acquire_apic_timer(void)
691 {
692 #if 1
693         return 0;
694 #else
695         /** XXX FIXME: make this really do something */
696         panic("APIC timer in use when attempting to acquire");
697 #endif
698 }
699
700
701 /*
702  * Return the APIC timer.
703  */
704 int
705 release_apic_timer(void)
706 {
707 #if 1
708         return 0;
709 #else
710         /** XXX FIXME: make this really do something */
711         panic("APIC timer was already released");
712 #endif
713 }
714 #endif  /* READY */
715
716
717 /*
718  * Load a 'downcount time' in uSeconds.
719  */
720 void
721 set_apic_timer(int value)
722 {
723         u_long  lvtt;
724         long    ticks_per_microsec;
725
726         /*
727          * Calculate divisor and count from value:
728          * 
729          *  timeBase == CPU bus clock divisor == [1,2,4,8,16,32,64,128]
730          *  value == time in uS
731          */
732         lapic.dcr_timer = APIC_TDCR_1;
733         ticks_per_microsec = bus_clock() / 1000000;
734
735         /* configure timer as one-shot */
736         lvtt = lapic.lvt_timer;
737         lvtt &= ~(APIC_LVTT_VECTOR | APIC_LVTT_DS);
738         lvtt &= ~(APIC_LVTT_PERIODIC);
739         lvtt |= APIC_LVTT_MASKED;               /* no INT, one-shot */
740         lapic.lvt_timer = lvtt;
741
742         /* */
743         lapic.icr_timer = value * ticks_per_microsec;
744 }
745
746
747 /*
748  * Read remaining time in timer.
749  */
750 int
751 read_apic_timer(void)
752 {
753 #if 0
754         /** XXX FIXME: we need to return the actual remaining time,
755          *         for now we just return the remaining count.
756          */
757 #else
758         return lapic.ccr_timer;
759 #endif
760 }
761
762
763 /*
764  * Spin-style delay, set delay time in uS, spin till it drains.
765  */
766 void
767 u_sleep(int count)
768 {
769         set_apic_timer(count);
770         while (read_apic_timer())
771                  /* spin */ ;
772 }