Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/sommerfeld_i386mp_1]: src/sys/arch/i386/i386 Local APIC driver.
details: https://anonhg.NetBSD.org/src/rev/8ab124ef0aec
branches: sommerfeld_i386mp_1
changeset: 482152:8ab124ef0aec
user: sommerfeld <sommerfeld%NetBSD.org@localhost>
date: Sun Feb 20 17:04:20 2000 +0000
description:
Local APIC driver.
Includes variant DELAY and microtime implementations using the local
APIC timer.
diffstat:
sys/arch/i386/i386/lapic.c | 424 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 424 insertions(+), 0 deletions(-)
diffs (truncated from 428 to 300 lines):
diff -r 7c060b17d504 -r 8ab124ef0aec sys/arch/i386/i386/lapic.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/arch/i386/i386/lapic.c Sun Feb 20 17:04:20 2000 +0000
@@ -0,0 +1,424 @@
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by RedBack Networks Inc.
+ *
+ * Author: Bill Sommerfeld
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "opt_ddb.h"
+#include "opt_multiprocessor.h"
+#include "opt_mpbios.h" /* for MPDEBUG */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/cpuvar.h>
+#include <machine/pmap.h>
+#include <machine/vmparam.h>
+#include <machine/mpbiosvar.h>
+#include <machine/pcb.h>
+#include <machine/specialreg.h>
+#include <machine/segments.h>
+
+#include <machine/apicvar.h>
+#include <machine/i82489reg.h>
+#include <machine/i82489var.h>
+
+#include <i386/isa/timerreg.h> /* XXX for TIMER_FREQ */
+
+void lapic_delay __P((int));
+void lapic_microtime __P((struct timeval *));
+static u_int32_t lapic_gettick __P((void));
+void lapic_clockintr __P((void *));
+void lapic_initclocks __P((void));
+
+void
+lapic_map(apic_paddr)
+ paddr_t apic_paddr;
+{
+ int s;
+
+ disable_intr();
+ s = lapic_tpr;
+ /*
+ * Map local apic.
+ */
+ pmap_enter(pmap_kernel(),(vaddr_t)&local_apic,
+ apic_paddr,
+ VM_PROT_ALL, PMAP_WIRED|VM_PROT_ALL);
+ lapic_tpr = s;
+ enable_intr();
+}
+
+/*
+ * enable local apic
+ */
+void
+lapic_enable()
+{
+ i82489_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
+}
+
+extern struct mp_intr_map *lapic_ints[]; /* XXX header file? */
+
+void
+lapic_set_lvt ()
+{
+#ifdef MULTIPROCESSOR
+ struct cpu_info *ci = curcpu();
+
+ if (mp_verbose) {
+ apic_format_redir (ci->ci_dev.dv_xname, "prelint", 0, 0,
+ i82489_readreg(LAPIC_LVINT0));
+ apic_format_redir (ci->ci_dev.dv_xname, "prelint", 1, 0,
+ i82489_readreg(LAPIC_LVINT1));
+ }
+#endif
+ if (lapic_ints[0])
+ i82489_writereg(LAPIC_LVINT0, lapic_ints[0]->redir);
+ if (lapic_ints[1])
+ i82489_writereg(LAPIC_LVINT1, lapic_ints[1]->redir);
+
+#ifdef MULTIPROCESSOR
+ if (mp_verbose) {
+ apic_format_redir (ci->ci_dev.dv_xname, "timer", 0, 0,
+ i82489_readreg(LAPIC_LVTT));
+ apic_format_redir (ci->ci_dev.dv_xname, "pcint", 0, 0,
+ i82489_readreg(LAPIC_PCINT));
+ apic_format_redir (ci->ci_dev.dv_xname, "lint", 0, 0,
+ i82489_readreg(LAPIC_LVINT0));
+ apic_format_redir (ci->ci_dev.dv_xname, "lint", 1, 0,
+ i82489_readreg(LAPIC_LVINT1));
+ apic_format_redir (ci->ci_dev.dv_xname, "err", 0, 0,
+ i82489_readreg(LAPIC_LVERR));
+ }
+#endif
+}
+
+/*
+ * Initialize fixed idt vectors for use by local apic.
+ */
+void
+lapic_vector_init(void)
+{
+ idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
+ idt_vec_set(LAPIC_TIMER_VECTOR, Xintrltimer);
+
+ idt_vec_set(LAPIC_SOFTCLOCK_VECTOR, Xintrsoftclock);
+ idt_vec_set(LAPIC_SOFTNET_VECTOR, Xintrsoftnet);
+ idt_vec_set(LAPIC_SOFTSER_VECTOR, Xintrsoftser);
+
+#ifdef MULTIPROCESSOR
+ idt_vec_set(LAPIC_IPI_VECTOR, Xintripi);
+#endif
+}
+
+static inline u_int32_t lapic_gettick()
+{
+ return i82489_readreg(LAPIC_CCR_TIMER);
+}
+
+#include <sys/kernel.h> /* for hz */
+
+int lapic_timer = 0;
+u_int32_t lapic_tval;
+
+/*
+ * this gets us up to a 4GHz busclock....
+ */
+u_int32_t lapic_per_second;
+u_int32_t lapic_frac_usec_per_cycle;
+u_int64_t lapic_frac_cycle_per_usec;
+u_int32_t lapic_delaytab[26];
+
+void
+lapic_clockintr (arg)
+ void *arg;
+{
+ struct clockframe *frame = arg;
+
+ hardclock(frame);
+}
+
+void
+lapic_initclocks ()
+{
+ /*
+ * Start local apic countdown timer running, in repeated mode.
+ *
+ * Mask the clock interrupt and set mode,
+ * then set divisor,
+ * then unmask and set the vector.
+ */
+ i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M);
+ i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
+ i82489_writereg (LAPIC_ICR_TIMER, lapic_tval);
+ i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_TIMER_VECTOR);
+}
+
+extern int gettick __P((void)); /* XXX put in header file */
+extern int rtclock_tval; /* XXX put in header file */
+extern void (*initclock_func) __P((void)); /* XXX put in header file */
+
+/*
+ * Calibrate the local apic count-down timer (which is running at
+ * bus-clock speed) vs. the i8254 counter/timer (which is running at
+ * a fixed rate).
+ *
+ * The Intel MP spec says: "An MP operating system may use the IRQ8
+ * real-time clock as a reference to determine the actual APIC timer clock
+ * speed."
+ *
+ * We're actually using the IRQ0 timer. Hmm.
+ */
+void
+lapic_calibrate_timer(ci)
+ struct cpu_info *ci;
+{
+ unsigned int starttick, tick1, tick2, endtick;
+ unsigned int startapic, apic1, apic2, endapic;
+ u_int64_t dtick, dapic, tmp;
+ int i;
+ char tbuf[9];
+
+ printf("%s: calibrating local timer\n", ci->ci_dev.dv_xname);
+
+ /*
+ * Configure timer to one-shot, interrupt masked,
+ * large positive number.
+ */
+ i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_M);
+ i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
+ i82489_writereg (LAPIC_ICR_TIMER, 0x80000000);
+
+ starttick = gettick();
+ startapic = lapic_gettick();
+
+ DELAY(2); /* using "old" delay here.. */
+
+ for (i=0; i<hz; i++) {
+ do {
+ tick1 = gettick();
+ apic1 = lapic_gettick();
+ } while (tick1 < starttick);
+
+ do {
+ tick2 = gettick();
+ apic2 = lapic_gettick();
+ } while (tick2 > starttick);
+ }
+
+ endtick = gettick();
+ endapic = lapic_gettick();
+
+ dtick = hz * rtclock_tval + (starttick-endtick);
+ dapic = startapic-endapic;
+
+ /*
+ * there are TIMER_FREQ ticks per second.
+ * in dtick ticks, there are dapic bus clocks.
+ */
+ tmp = (TIMER_FREQ * dapic) / dtick;
+
+ lapic_per_second = tmp;
+
+ humanize_number(tbuf, sizeof(tbuf), tmp, "Hz", 1000);
+
+ printf("%s: apic clock running at %s\n", ci->ci_dev.dv_xname, tbuf);
+
+ /*
+ * reprogram the apic timer to run in periodic mode.
+ * XXX need to program timer on other cpu's, too.
+ */
+ lapic_tval = (lapic_per_second * 2) / hz;
+ lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
+
+ i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M
+ |LAPIC_TIMER_VECTOR);
+ i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
+ i82489_writereg (LAPIC_ICR_TIMER, lapic_tval);
+
+ /*
+ * Compute fixed-point ratios between cycles and
+ * microseconds to avoid having to do any division
+ * in lapic_delay and lapic_microtime.
+ */
+
+ tmp = (1000000 * (u_int64_t)1<<32) / lapic_per_second;
+ lapic_frac_usec_per_cycle = tmp;
+
+ tmp = (lapic_per_second * (u_int64_t)1<<32) / 1000000;
+
+ lapic_frac_cycle_per_usec = tmp;
+
Home |
Main Index |
Thread Index |
Old Index