root/arch/i386/i386/lapic.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lapic_map
  2. lapic_enable
  3. lapic_set_softvectors
  4. lapic_set_lvt
  5. lapic_boot_init
  6. lapic_gettick
  7. lapic_clockintr
  8. lapic_initclocks
  9. lapic_calibrate_timer
  10. lapic_delay
  11. i386_ipi_microset
  12. lapic_microtime
  13. i386_ipi_init
  14. i386_ipi

    1 /*      $OpenBSD: lapic.c,v 1.17 2007/08/01 13:18:18 martin Exp $       */
    2 /* $NetBSD: lapic.c,v 1.1.2.8 2000/02/23 06:10:50 sommerfeld Exp $ */
    3 
    4 /*-
    5  * Copyright (c) 2000 The NetBSD Foundation, Inc.
    6  * All rights reserved.
    7  *
    8  * This code is derived from software contributed to The NetBSD Foundation
    9  * by RedBack Networks Inc.
   10  *
   11  * Author: Bill Sommerfeld
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  * 1. Redistributions of source code must retain the above copyright
   17  *    notice, this list of conditions and the following disclaimer.
   18  * 2. Redistributions in binary form must reproduce the above copyright
   19  *    notice, this list of conditions and the following disclaimer in the
   20  *    documentation and/or other materials provided with the distribution.
   21  * 3. All advertising materials mentioning features or use of this software
   22  *    must display the following acknowledgement:
   23  *        This product includes software developed by the NetBSD
   24  *        Foundation, Inc. and its contributors.
   25  * 4. Neither the name of The NetBSD Foundation nor the names of its
   26  *    contributors may be used to endorse or promote products derived
   27  *    from this software without specific prior written permission.
   28  *
   29  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   32  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   39  * POSSIBILITY OF SUCH DAMAGE.
   40  */
   41 
   42 #include <sys/param.h>
   43 #include <sys/proc.h>
   44 #include <sys/user.h>
   45 #include <sys/systm.h>
   46 #include <sys/device.h>
   47 #include <sys/timetc.h>
   48 
   49 #include <uvm/uvm_extern.h>
   50 
   51 #include <machine/cpu.h>
   52 #include <machine/cpufunc.h>
   53 #include <machine/cpuvar.h>
   54 #include <machine/pmap.h>
   55 #include <machine/vmparam.h>
   56 #include <machine/mpbiosvar.h>
   57 #include <machine/pcb.h>
   58 #include <machine/specialreg.h>
   59 #include <machine/segments.h>
   60 
   61 #include <machine/apicvar.h>
   62 #include <machine/i82489reg.h>
   63 #include <machine/i82489var.h>
   64 #include <machine/pctr.h>
   65 
   66 #include <dev/ic/i8253reg.h>
   67 
   68 struct evcount clk_count;
   69 struct evcount ipi_count;
   70 
   71 void    lapic_delay(int);
   72 void    lapic_microtime(struct timeval *);
   73 static __inline u_int32_t lapic_gettick(void);
   74 void    lapic_clockintr(void *);
   75 void    lapic_initclocks(void);
   76 void    lapic_map(paddr_t);
   77 
   78 void
   79 lapic_map(lapic_base)
   80         paddr_t lapic_base;
   81 {
   82         int s;
   83         pt_entry_t *pte;
   84         vaddr_t va = (vaddr_t)&local_apic;
   85 
   86         disable_intr();
   87         s = lapic_tpr;
   88 
   89         /*
   90          * Map local apic.  If we have a local apic, it's safe to assume
   91          * we're on a 486 or better and can use invlpg and non-cacheable PTE's
   92          *
   93          * Whap the PTE "by hand" rather than calling pmap_kenter_pa because
   94          * the latter will attempt to invoke TLB shootdown code just as we
   95          * might have changed the value of cpu_number()..
   96          */
   97 
   98         pte = kvtopte(va);
   99         *pte = lapic_base | PG_RW | PG_V | PG_N;
  100         invlpg(va);
  101 
  102 #ifdef MULTIPROCESSOR
  103         cpu_init_first();       /* catch up to changed cpu_number() */
  104 #endif
  105 
  106         lapic_tpr = s;
  107         enable_intr();
  108 }
  109 
  110 /*
  111  * enable local apic
  112  */
  113 void
  114 lapic_enable()
  115 {
  116         i82489_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
  117 }
  118 
  119 void
  120 lapic_set_softvectors()
  121 {
  122         idt_vec_set(LAPIC_SOFTCLOCK_VECTOR, Xintrsoftclock);
  123         idt_vec_set(LAPIC_SOFTNET_VECTOR, Xintrsoftnet);
  124         idt_vec_set(LAPIC_SOFTTTY_VECTOR, Xintrsofttty);
  125         idt_vec_set(LAPIC_SOFTAST_VECTOR, Xintrsoftast);
  126 }
  127 
  128 void
  129 lapic_set_lvt()
  130 {
  131         struct cpu_info *ci = curcpu();
  132         int i;
  133         struct mp_intr_map *mpi;
  134 
  135 #ifdef MULTIPROCESSOR
  136         if (mp_verbose) {
  137                 apic_format_redir(ci->ci_dev.dv_xname, "prelint", 0, 0,
  138                     i82489_readreg(LAPIC_LVINT0));
  139                 apic_format_redir(ci->ci_dev.dv_xname, "prelint", 1, 0,
  140                     i82489_readreg(LAPIC_LVINT1));
  141         }
  142 #endif
  143 
  144         for (i = 0; i < mp_nintrs; i++) {
  145                 mpi = &mp_intrs[i];
  146                 if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS
  147                                             || mpi->cpu_id == ci->ci_apicid)) {
  148 #ifdef DIAGNOSTIC
  149                         if (mpi->ioapic_pin > 1)
  150                                 panic("lapic_set_lvt: bad pin value %d",
  151                                     mpi->ioapic_pin);
  152 #endif
  153                         if (mpi->ioapic_pin == 0)
  154                                 i82489_writereg(LAPIC_LVINT0, mpi->redir);
  155                         else
  156                                 i82489_writereg(LAPIC_LVINT1, mpi->redir);
  157                 }
  158         }
  159 
  160 #ifdef MULTIPROCESSOR
  161         if (mp_verbose) {
  162                 apic_format_redir(ci->ci_dev.dv_xname, "timer", 0, 0,
  163                     i82489_readreg(LAPIC_LVTT));
  164                 apic_format_redir(ci->ci_dev.dv_xname, "pcint", 0, 0,
  165                     i82489_readreg(LAPIC_PCINT));
  166                 apic_format_redir(ci->ci_dev.dv_xname, "lint", 0, 0,
  167                     i82489_readreg(LAPIC_LVINT0));
  168                 apic_format_redir(ci->ci_dev.dv_xname, "lint", 1, 0,
  169                     i82489_readreg(LAPIC_LVINT1));
  170                 apic_format_redir(ci->ci_dev.dv_xname, "err", 0, 0,
  171                     i82489_readreg(LAPIC_LVERR));
  172         }
  173 #endif
  174 }
  175 
  176 /*
  177  * Initialize fixed idt vectors for use by local apic.
  178  */
  179 void
  180 lapic_boot_init(paddr_t lapic_base)
  181 {
  182         static int clk_irq = 0;
  183         static int ipi_irq = 0;
  184 
  185         lapic_map(lapic_base);
  186 
  187 #ifdef MULTIPROCESSOR
  188         idt_vec_set(LAPIC_IPI_VECTOR, Xintripi);
  189         idt_vec_set(LAPIC_IPI_AST, Xintripi_ast);
  190         idt_vec_set(LAPIC_IPI_INVLTLB, Xintripi_invltlb);
  191         idt_vec_set(LAPIC_IPI_INVLPG, Xintripi_invlpg);
  192         idt_vec_set(LAPIC_IPI_INVLRANGE, Xintripi_invlrange);
  193 #endif
  194         idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
  195         idt_vec_set(LAPIC_TIMER_VECTOR, Xintrltimer);
  196 
  197         evcount_attach(&clk_count, "clock", (void *)&clk_irq, &evcount_intr);
  198         evcount_attach(&ipi_count, "ipi", (void *)&ipi_irq, &evcount_intr);
  199 }
  200 
  201 static __inline u_int32_t
  202 lapic_gettick()
  203 {
  204         return (i82489_readreg(LAPIC_CCR_TIMER));
  205 }
  206 
  207 #include <sys/kernel.h>         /* for hz */
  208 
  209 u_int32_t lapic_tval;
  210 
  211 /*
  212  * this gets us up to a 4GHz busclock....
  213  */
  214 u_int32_t lapic_per_second;
  215 u_int32_t lapic_frac_usec_per_cycle;
  216 u_int64_t lapic_frac_cycle_per_usec;
  217 u_int32_t lapic_delaytab[26];
  218 u_int64_t scaled_pentium_mhz;
  219 
  220 void
  221 lapic_clockintr(arg)
  222         void *arg;
  223 {
  224         struct cpu_info *ci = curcpu();
  225         struct clockframe *frame = arg;
  226 
  227         if (CPU_IS_PRIMARY(ci)) {
  228                 ci->ci_tscbase = rdtsc();
  229                 i386_broadcast_ipi(I386_IPI_MICROSET);
  230         }
  231         hardclock(frame);
  232 
  233         clk_count.ec_count++;
  234 }
  235 
  236 void
  237 lapic_initclocks()
  238 {
  239         /*
  240          * Start local apic countdown timer running, in repeated mode.
  241          *
  242          * Mask the clock interrupt and set mode,
  243          * then set divisor,
  244          * then unmask and set the vector.
  245          */
  246         i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M);
  247         i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
  248         i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);
  249         i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_TIMER_VECTOR);
  250 }
  251 
  252 extern int gettick(void);       /* XXX put in header file */
  253 extern void (*initclock_func)(void); /* XXX put in header file */
  254 
  255 /*
  256  * Calibrate the local apic count-down timer (which is running at
  257  * bus-clock speed) vs. the i8254 counter/timer (which is running at
  258  * a fixed rate).
  259  *
  260  * The Intel MP spec says: "An MP operating system may use the IRQ8
  261  * real-time clock as a reference to determine the actual APIC timer clock
  262  * speed."
  263  *
  264  * We're actually using the IRQ0 timer.  Hmm.
  265  */
  266 void
  267 lapic_calibrate_timer(struct cpu_info *ci)
  268 {
  269         unsigned int starttick, tick1, tick2, endtick;
  270         unsigned int startapic, apic1, apic2, endapic;
  271         u_int64_t dtick, dapic, tmp;
  272         int i;
  273         char tbuf[9];
  274 
  275         if (mp_verbose)
  276                 printf("%s: calibrating local timer\n", ci->ci_dev.dv_xname);
  277 
  278         /*
  279          * Configure timer to one-shot, interrupt masked,
  280          * large positive number.
  281          */
  282         i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_M);
  283         i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
  284         i82489_writereg(LAPIC_ICR_TIMER, 0x80000000);
  285 
  286         starttick = gettick();
  287         startapic = lapic_gettick();
  288 
  289         DELAY(2);               /* using "old" delay here.. */
  290 
  291         for (i=0; i<hz; i++) {
  292                 do {
  293                         tick1 = gettick();
  294                         apic1 = lapic_gettick();
  295                 } while (tick1 < starttick);
  296 
  297                 do {
  298                         tick2 = gettick();
  299                         apic2 = lapic_gettick();
  300                 } while (tick2 > starttick);
  301         }
  302 
  303         endtick = gettick();
  304         endapic = lapic_gettick();
  305 
  306         dtick = hz * TIMER_DIV(hz) + (starttick-endtick);
  307         dapic = startapic-endapic;
  308 
  309         /*
  310          * there are TIMER_FREQ ticks per second.
  311          * in dtick ticks, there are dapic bus clocks.
  312          */
  313         tmp = (TIMER_FREQ * dapic) / dtick;
  314 
  315         lapic_per_second = tmp;
  316 
  317 #if 0
  318         humanize_number(tbuf, sizeof(tbuf), tmp, "Hz", 1000);
  319 #else /* XXX: from NetBSD sources... sigh. */
  320         {
  321                 /* prefixes are: (none), Kilo, Mega, Giga, Tera, Peta, Exa */
  322                 static const char prefixes[] = " KMGTPE";
  323 
  324                 int             i;
  325                 u_int64_t       max;
  326                 size_t          suffixlen;
  327 
  328                 if (tbuf == NULL)
  329                         goto out;
  330                 if (sizeof(tbuf) > 0)
  331                         tbuf[0] = '\0';
  332                 suffixlen = sizeof "Hz" - 1;
  333                 /* check if enough room for `x y' + suffix + `\0' */
  334                 if (sizeof(tbuf) < 4 + suffixlen)
  335                         goto out;
  336 
  337                 max = 1;
  338                 for (i = 0; i < sizeof(tbuf) - suffixlen - 3; i++)
  339                         max *= 10;
  340                 for (i = 0; tmp >= max && i < sizeof(prefixes); i++)
  341                         tmp /= 1000;
  342 
  343                 snprintf(tbuf, sizeof(tbuf), "%qu%s%c%s",
  344                     (unsigned long long)tmp, i == 0 ? "" : " ", prefixes[i],
  345                     "Hz");
  346         out:
  347                 ;
  348         }
  349 #endif
  350 
  351         printf("%s: apic clock running at %s\n", ci->ci_dev.dv_xname, tbuf);
  352 
  353         if (lapic_per_second != 0) {
  354                 /*
  355                  * reprogram the apic timer to run in periodic mode.
  356                  * XXX need to program timer on other cpu's, too.
  357                  */
  358                 lapic_tval = (lapic_per_second * 2) / hz;
  359                 lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
  360 
  361                 i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM | LAPIC_LVTT_M |
  362                     LAPIC_TIMER_VECTOR);
  363                 i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
  364                 i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);
  365 
  366                 /*
  367                  * Compute fixed-point ratios between cycles and
  368                  * microseconds to avoid having to do any division
  369                  * in lapic_delay and lapic_microtime.
  370                  */
  371 
  372                 tmp = (1000000 * (u_int64_t)1 << 32) / lapic_per_second;
  373                 lapic_frac_usec_per_cycle = tmp;
  374 
  375                 tmp = (lapic_per_second * (u_int64_t)1 << 32) / 1000000;
  376 
  377                 lapic_frac_cycle_per_usec = tmp;
  378 
  379                 scaled_pentium_mhz = (1ULL << 32) / cpuspeed;
  380 
  381                 /*
  382                  * Compute delay in cycles for likely short delays in usec.
  383                  */
  384                 for (i = 0; i < 26; i++)
  385                         lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
  386                             32;
  387 
  388                 /*
  389                  * Now that the timer's calibrated, use the apic timer routines
  390                  * for all our timing needs..
  391                  */
  392                 delay_func = lapic_delay;
  393                 initclock_func = lapic_initclocks;
  394         }
  395 }
  396 
  397 /*
  398  * delay for N usec.
  399  */
  400 
  401 void
  402 lapic_delay(int usec)
  403 {
  404         int32_t tick, otick;
  405         int64_t deltat;         /* XXX may want to be 64bit */
  406 
  407         otick = lapic_gettick();
  408 
  409         if (usec <= 0)
  410                 return;
  411         if (usec <= 25)
  412                 deltat = lapic_delaytab[usec];
  413         else
  414                 deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
  415 
  416         while (deltat > 0) {
  417                 tick = lapic_gettick();
  418                 if (tick > otick)
  419                         deltat -= lapic_tval - (tick - otick);
  420                 else
  421                         deltat -= otick - tick;
  422                 otick = tick;
  423         }
  424 }
  425 
  426 #define LAPIC_TICK_THRESH 200
  427 
  428 /*
  429  * An IPI handler to record current timer value
  430  */
  431 void
  432 i386_ipi_microset(struct cpu_info *ci)
  433 {
  434         ci->ci_tscbase = rdtsc();
  435 }
  436 
  437 #if 0
  438 /*
  439  * XXX need to make work correctly on other than cpu 0.
  440  */
  441 void
  442 lapic_microtime(tv)
  443         struct timeval *tv;
  444 {
  445         struct cpu_info *ci = curcpu();
  446         struct timeval now;
  447         u_int64_t tmp;
  448 
  449         disable_intr();
  450         now = time;
  451         tmp = rdtsc() - ci->ci_tscbase;
  452         enable_intr();
  453 
  454         now.tv_usec += (tmp * scaled_pentium_mhz) >> 32;
  455 
  456         while (now.tv_usec >= 1000000) {
  457                 now.tv_sec += 1;
  458                 now.tv_usec -= 1000000;
  459         }
  460 
  461         *tv = now;
  462 }
  463 #endif
  464 
  465 /*
  466  * XXX the following belong mostly or partly elsewhere..
  467  */
  468 
  469 int
  470 i386_ipi_init(target)
  471         int target;
  472 {
  473         unsigned j;
  474 
  475         if ((target & LAPIC_DEST_MASK) == 0)
  476                 i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
  477 
  478         i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
  479             LAPIC_DLMODE_INIT | LAPIC_LVL_ASSERT );
  480 
  481         for (j = 100000; j > 0; j--) {
  482                 __asm __volatile("pause": : :"memory");
  483                 if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) == 0)
  484                         break;
  485         }
  486 
  487         delay(10000);
  488 
  489         i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
  490              LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
  491 
  492         for (j = 100000; j > 0; j--) {
  493                 __asm __volatile("pause": : :"memory");
  494                 if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) == 0)
  495                         break;
  496         }
  497 
  498         return (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY)?EBUSY:0;
  499 }
  500 
  501 int
  502 i386_ipi(vec,target,dl)
  503         int vec,target,dl;
  504 {
  505         unsigned j;
  506 
  507         if ((target & LAPIC_DEST_MASK) == 0)
  508                 i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
  509 
  510         i82489_writereg(LAPIC_ICRLO,
  511             (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT);
  512 
  513         for (j = 100000;
  514              j > 0 && (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY);
  515              j--)
  516                 SPINLOCK_SPIN_HOOK;
  517 
  518         return (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) ? EBUSY : 0;
  519 }

/* [<][>][^][v][top][bottom][index][help] */