diff -ruNp --exclude compile sys.prev/amd64/include/apicvar.h sys/amd64/include/apicvar.h --- sys.prev/amd64/include/apicvar.h 2010-05-24 09:35:29.000000000 +0300 +++ sys/amd64/include/apicvar.h 2010-05-26 22:40:16.000000000 +0300 @@ -156,12 +156,6 @@ #define APIC_BUS_PCI 2 #define APIC_BUS_MAX APIC_BUS_PCI -enum lapic_clock { - LAPIC_CLOCK_NONE, - LAPIC_CLOCK_HARDCLOCK, - LAPIC_CLOCK_ALL -}; - /* * An APIC enumerator is a psuedo bus driver that enumerates APIC's including * CPU's and I/O APIC's. @@ -230,7 +224,6 @@ int lapic_set_lvt_triggermode(u_int apic enum intr_trigger trigger); void lapic_set_tpr(u_int vector); void lapic_setup(int boot); -enum lapic_clock lapic_setup_clock(enum lapic_clock srcsdes); #endif /* !LOCORE */ #endif /* _MACHINE_APICVAR_H_ */ diff -ruNp --exclude compile sys.prev/conf/files sys/conf/files --- sys.prev/conf/files 2010-05-24 07:03:45.000000000 +0300 +++ sys/conf/files 2010-05-26 07:05:49.000000000 +0300 @@ -2060,6 +2060,7 @@ kern/kern_context.c standard kern/kern_descrip.c standard kern/kern_dtrace.c optional kdtrace_hooks kern/kern_environment.c standard +kern/kern_et.c standard kern/kern_event.c standard kern/kern_exec.c standard kern/kern_exit.c standard diff -ruNp --exclude compile sys.prev/conf/files.amd64 sys/conf/files.amd64 --- sys.prev/conf/files.amd64 2010-05-24 07:03:45.000000000 +0300 +++ sys/conf/files.amd64 2010-05-28 08:11:29.000000000 +0300 @@ -301,6 +301,7 @@ x86/cpufreq/p4tcc.c optional cpufreq x86/isa/atpic.c optional atpic isa x86/isa/atrtc.c standard x86/isa/clock.c standard +x86/x86/eventtimers.c standard x86/isa/elcr.c standard x86/isa/isa.c standard x86/isa/isa_dma.c standard diff -ruNp --exclude compile sys.prev/conf/files.i386 sys/conf/files.i386 --- sys.prev/conf/files.i386 2010-05-24 07:03:45.000000000 +0300 +++ sys/conf/files.i386 2010-05-28 08:11:47.000000000 +0300 @@ -385,6 +385,7 @@ x86/cpufreq/smist.c optional cpufreq x86/isa/atpic.c optional atpic x86/isa/atrtc.c optional atpic x86/isa/clock.c optional native +x86/x86/eventtimers.c standard x86/isa/elcr.c standard x86/isa/isa.c optional isa x86/isa/isa_dma.c optional isa diff -ruNp --exclude compile sys.prev/dev/acpica/acpi_hpet.c sys/dev/acpica/acpi_hpet.c --- sys.prev/dev/acpica/acpi_hpet.c 2010-05-23 17:08:40.000000000 +0300 +++ sys/dev/acpica/acpi_hpet.c 2010-05-28 21:57:22.000000000 +0300 @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD: src/sys/dev/acpica/a #include #include #include +#include #include #include @@ -55,22 +56,33 @@ ACPI_MODULE_NAME("HPET") struct acpi_hpet_softc { device_t dev; + int mem_rid, intr_rid, irq; struct resource *mem_res; + struct resource *intr_res; + void *intr_handle; ACPI_HANDLE handle; + uint64_t freq; + struct timecounter tc; + struct hpet_timer { + struct eventtimer et; + struct acpi_hpet_softc *sc; + int num; + int mode; + uint32_t caps; + uint32_t vectors; + uint32_t div; + uint32_t last; + char name[8]; + } t[32]; + int num_timers; }; +static struct acpi_hpet_softc *hpet_sc; static u_int hpet_get_timecount(struct timecounter *tc); static void acpi_hpet_test(struct acpi_hpet_softc *sc); static char *hpet_ids[] = { "PNP0103", NULL }; -struct timecounter hpet_timecounter = { - .tc_get_timecount = hpet_get_timecount, - .tc_counter_mask = ~0u, - .tc_name = "HPET", - .tc_quality = 900, -}; - static u_int hpet_get_timecount(struct timecounter *tc) { @@ -101,6 +113,87 @@ hpet_disable(struct acpi_hpet_softc *sc) bus_write_4(sc->mem_res, HPET_CONFIG, val); } +static int +hpet_periodic(struct eventtimer *et, uint64_t freq) +{ + struct hpet_timer *t = (struct hpet_timer *)et->et_priv; + struct acpi_hpet_softc *sc = t->sc; + + t->mode = 1; + t->div = sc->freq / freq; + t->last = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER); + if (t->caps & HPET_TCAP_PER_INT) { + t->caps |= HPET_TCNF_TYPE; + bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps | HPET_TCNF_VAL_SET); + bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num), t->last + t->div); + bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num)); + bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num), t->div); + } else + bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num), t->last + t->div); + t->caps |= HPET_TCNF_INT_ENB; + bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps); + return (0); +} + +static int +hpet_oneshot(struct eventtimer *et, uint64_t delay) +{ + struct hpet_timer *t = (struct hpet_timer *)et->et_priv; + struct acpi_hpet_softc *sc = t->sc; + + t->mode = 2; + t->div = delay * 1000000 / sc->freq; + t->last = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER); + bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num), t->last + t->div); + t->caps |= HPET_TCNF_INT_ENB; + bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps); + return (0); +} + +static int +hpet_stop(struct eventtimer *et) +{ + struct hpet_timer *t = (struct hpet_timer *)et->et_priv; + struct acpi_hpet_softc *sc = t->sc; + + t->mode = 0; + t->caps &= ~(HPET_TCNF_INT_ENB | HPET_TCNF_TYPE); + bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps); + return (0); +} + + +static int +hpet_intr(void *arg) +{ + struct acpi_hpet_softc *sc = hpet_sc; + struct hpet_timer *t; + int i; + uint32_t val; + + val = bus_read_4(sc->mem_res, HPET_ISR); + if (val) { + bus_write_4(sc->mem_res, HPET_ISR, val); + for (i = 0; i < sc->num_timers; i++) { + if ((val & (1 << i)) == 0) + continue; + t = &sc->t[i]; + if (t->mode == 1 && + (t->caps & HPET_TCAP_PER_INT) == 0) { + t->last += t->div; + bus_write_4(sc->mem_res, + HPET_TIMER_COMPARATOR(t->num), t->last + t->div); + } + if (t->et.et_event_cb != NULL) { + t->et.et_event_cb(&t->et, + t->et.et_arg ? t->et.et_arg : arg); + } + } + return (FILTER_HANDLED); + } + return (FILTER_STRAY); +} + static ACPI_STATUS acpi_hpet_find(ACPI_HANDLE handle, UINT32 level, void *context, void **status) @@ -176,9 +269,9 @@ static int acpi_hpet_attach(device_t dev) { struct acpi_hpet_softc *sc; - int rid, num_timers; - uint32_t val, val2; - uintmax_t freq; + struct hpet_timer *t; + int num_timers = 0, i, j; + uint32_t val, val2, cvectors; uint16_t vendor; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); @@ -187,8 +280,8 @@ acpi_hpet_attach(device_t dev) sc->dev = dev; sc->handle = acpi_get_handle(dev); - rid = 0; - sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) return (ENOMEM); @@ -213,27 +306,47 @@ acpi_hpet_attach(device_t dev) return (ENXIO); } - freq = (1000000000000000LL + val / 2) / val; + sc->freq = (1000000000000000LL + val / 2) / val; + val = bus_read_4(sc->mem_res, HPET_CAPABILITIES); + /* + * ATI/AMD violates IA-PC HPET (High Precision Event Timers) + * Specification and provides an off by one number + * of timers/comparators. + * Additionally, they use unregistered value in VENDOR_ID field. + */ + num_timers = 1 + ((val & HPET_CAP_NUM_TIM) >> 8); + vendor = val >> 16; + if (vendor == HPET_VENDID_AMD && num_timers > 0) + num_timers--; if (bootverbose) { - val = bus_read_4(sc->mem_res, HPET_CAPABILITIES); - - /* - * ATI/AMD violates IA-PC HPET (High Precision Event Timers) - * Specification and provides an off by one number - * of timers/comparators. - * Additionally, they use unregistered value in VENDOR_ID field. - */ - num_timers = 1 + ((val & HPET_CAP_NUM_TIM) >> 8); - vendor = val >> 16; - if (vendor == HPET_VENDID_AMD && num_timers > 0) - num_timers--; device_printf(dev, - "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n", + "vendor 0x%x, rev 0x%x, %jdHz%s, %d timers,%s\n", vendor, val & HPET_CAP_REV_ID, - num_timers, freq, - (val & HPET_CAP_LEG_RT) ? " legacy_route" : "", - (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : ""); + sc->freq, (val & HPET_CAP_COUNT_SIZE) ? " 64bit" : "", + num_timers, (val & HPET_CAP_LEG_RT) ? " legacy route" : ""); + } + cvectors = ~0; + for (i = 0; i < num_timers; i++) { + val = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i)); + val2 = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i) + 4); + if (bootverbose) { + device_printf(dev, + " t%d: irqs 0x%08x%s%s%s\n", + i, + val2, + (val & HPET_TCAP_FSB_INT_DEL) ? ", MSI" : "", + (val & HPET_TCAP_SIZE) ? ", 64bit" : "", + (val & HPET_TCAP_PER_INT) ? ", periodic" : ""); + } + sc->t[i].caps = val; + sc->t[i].vectors = val2; + if ((cvectors & val2) == 0) { + num_timers = i; + break; + } + cvectors &= val2; } + sc->num_timers = num_timers; if (testenv("debug.acpi.hpet_test")) acpi_hpet_test(sc); @@ -251,11 +364,64 @@ acpi_hpet_attach(device_t dev) bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); return (ENXIO); } + /* + * Until we can receive both frame and argument from interrupt - + * use only first HPET's event timers. + */ + if (device_get_unit(dev) == 0) { + hpet_sc = sc; + j = i = ffs(cvectors) - 1; + while ((cvectors & (1 << j)) != 0) + j++; + if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ, + &sc->intr_rid, i, j, 1, RF_SHAREABLE | RF_ACTIVE))) + device_printf(dev,"Warning: Couldn't map Interrupt.\n"); + else if ((bus_setup_intr(dev, sc->intr_res, + INTR_MPSAFE | INTR_TYPE_CLK, + (driver_filter_t *)hpet_intr, NULL, + NULL, &sc->intr_handle))) { + device_printf(dev,"Warning: Couldn't setup Interrupt.\n"); + } else + sc->irq = rman_get_start(sc->intr_res); + if (bootverbose) + device_printf(dev, "Using IRQ %d\n", sc->irq); + for (i = 0; i < num_timers; i++) { + t = &sc->t[i]; + t->sc = sc; + t->num = i; + t->caps &= ~(HPET_TCNF_FSB_EN | HPET_TCNF_INT_ROUTE); + t->caps &= ~(HPET_TCNF_VAL_SET | HPET_TCNF_INT_ENB); + t->caps |= (sc->irq << 9) | HPET_TCNF_32MODE | HPET_TCNF_INT_TYPE; + bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps); + t->mode = 0; + + if (i == 0) + t->et.et_name = "HPET"; + else { + sprintf(t->name, "HPET%d", i); + t->et.et_name = t->name; + } + t->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; + t->et.et_quality = 1000; + if ((t->caps & HPET_TCAP_PER_INT) == 0) + t->et.et_quality -= 10; + t->et.et_frequency = sc->freq; + t->et.et_periodic = hpet_periodic; + t->et.et_oneshot = hpet_oneshot; + t->et.et_stop = hpet_stop; + t->et.et_priv = &sc->t[i]; + et_register(&t->et); + } + } /* Announce first HPET as timecounter. */ if (device_get_unit(dev) == 0) { - hpet_timecounter.tc_frequency = freq; - hpet_timecounter.tc_priv = sc; - tc_init(&hpet_timecounter); + sc->tc.tc_get_timecount = hpet_get_timecount, + sc->tc.tc_counter_mask = ~0u, + sc->tc.tc_name = "HPET", + sc->tc.tc_quality = 900, + sc->tc.tc_frequency = sc->freq; + sc->tc.tc_priv = sc; + tc_init(&sc->tc); } return (0); } diff -ruNp --exclude compile sys.prev/dev/acpica/acpi_hpet.h sys/dev/acpica/acpi_hpet.h --- sys.prev/dev/acpica/acpi_hpet.h 2009-11-01 12:58:51.000000000 +0200 +++ sys/dev/acpica/acpi_hpet.h 2010-05-27 23:09:46.000000000 +0300 @@ -57,7 +57,7 @@ #define HPET_TCAP_PER_INT 0x00000010 /* Supports periodic interrupts */ #define HPET_TCNF_TYPE 0x00000008 /* 1 = periodic, 0 = one-shot */ #define HPET_TCNF_INT_ENB 0x00000004 -#define HPET_TCNT_INT_TYPE 0x00000002 /* 1 = level triggered, 0 = edge */ +#define HPET_TCNF_INT_TYPE 0x00000002 /* 1 = level triggered, 0 = edge */ #define HPET_TIMER_COMPARATOR(x) ((x) * 0x20 + 0x108) #define HPET_TIMER_FSB_VAL(x) ((x) * 0x20 + 0x110) #define HPET_TIMER_FSB_ADDR(x) ((x) * 0x20 + 0x114) diff -ruNp --exclude compile sys.prev/i386/include/apicvar.h sys/i386/include/apicvar.h --- sys.prev/i386/include/apicvar.h 2010-05-24 09:35:52.000000000 +0300 +++ sys/i386/include/apicvar.h 2010-05-26 22:40:21.000000000 +0300 @@ -184,12 +184,6 @@ #define APIC_BUS_PCI 2 #define APIC_BUS_MAX APIC_BUS_PCI -enum lapic_clock { - LAPIC_CLOCK_NONE, - LAPIC_CLOCK_HARDCLOCK, - LAPIC_CLOCK_ALL -}; - /* * An APIC enumerator is a psuedo bus driver that enumerates APIC's including * CPU's and I/O APIC's. @@ -258,7 +252,6 @@ int lapic_set_lvt_triggermode(u_int apic enum intr_trigger trigger); void lapic_set_tpr(u_int vector); void lapic_setup(int boot); -enum lapic_clock lapic_setup_clock(enum lapic_clock srcsdes); #endif /* !LOCORE */ #endif /* _MACHINE_APICVAR_H_ */ diff -ruNp --exclude compile sys.prev/isa/rtc.h sys/isa/rtc.h --- sys.prev/isa/rtc.h 2009-11-01 13:32:33.000000000 +0200 +++ sys/isa/rtc.h 2010-05-26 14:07:49.000000000 +0300 @@ -114,11 +114,7 @@ #ifdef _KERNEL extern struct mtx clock_lock; extern int atrtcclock_disable; -int atrtc_setup_clock(void); int rtcin(int reg); -void atrtc_start(void); -void atrtc_rate(unsigned rate); -void atrtc_enable_intr(void); void atrtc_restore(void); void writertc(int reg, u_char val); #endif diff -ruNp --exclude compile sys.prev/kern/kern_et.c sys/kern/kern_et.c --- sys.prev/kern/kern_et.c 1970-01-01 03:00:00.000000000 +0300 +++ sys/kern/kern_et.c 2010-05-27 17:00:15.000000000 +0300 @@ -0,0 +1,206 @@ +/*- + * Copyright (c) 2010 Alexander Motin + * All rights reserved. + * + * 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, + * without modification, immediately at the beginning of the file. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +struct eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(eventtimers); + +SYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, ""); +SYSCTL_NODE(_kern_eventtimer, OID_AUTO, et, CTLFLAG_RW, 0, ""); + +/* + * Register a new event timer hardware. + */ +int +et_register(struct eventtimer *et) +{ + struct sysctl_oid *et_root; + struct eventtimer *tmp, *next; + + if (bootverbose) { + printf("Event timer \"%s\" frequency %ju Hz quality %d\n", + et->et_name, (uintmax_t)et->et_frequency, + et->et_quality); + } + et_root = SYSCTL_ADD_NODE(NULL, + SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name, + CTLFLAG_RW, 0, "event timer description"); + SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(et_root), OID_AUTO, + "flags", CTLFLAG_RD, &(et->et_flags), 0, + "capabilities flags"); + SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et_root), OID_AUTO, + "frequency", CTLFLAG_RD, &(et->et_frequency), 0, + "timer base frequency"); + SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et_root), OID_AUTO, + "quality", CTLFLAG_RD, &(et->et_quality), 0, + "goodness of time counter"); + if (SLIST_EMPTY(&eventtimers) || + SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) { + SLIST_INSERT_HEAD(&eventtimers, et, et_all); + } else { + SLIST_FOREACH(tmp, &eventtimers, et_all) { + next = SLIST_NEXT(tmp, et_all); + if (next == NULL || next->et_quality < et->et_quality) { + SLIST_INSERT_AFTER(tmp, et, et_all); + break; + } + } + } + return (0); +} + +/* + * Deregister event timer hardware. + */ +int +et_deregister(struct eventtimer *et) +{ + int err = 0; + + if (et->et_deregister_cb != NULL) { + if ((err = et->et_deregister_cb(et, et->et_arg)) != 0) + return (err); + } + + SLIST_REMOVE(&eventtimers, et, eventtimer, et_all); + return (0); +} + +struct eventtimer * +et_find(const char *name, int flags) +{ + struct eventtimer *et = NULL; + + SLIST_FOREACH(et, &eventtimers, et_all) { + if (et->et_event_cb != NULL) + continue; + if (name != NULL && strcasecmp(et->et_name, name) != 0) + continue; + if ((et->et_flags & flags) != flags) + continue; + break; + } + return (et); +} + +int +et_init(struct eventtimer *et, et_event_cb_t *event, + et_deregister_cb_t *deregister, void *arg) +{ + + if (event == NULL) + return (EINVAL); + if (et->et_event_cb != NULL) + return (EBUSY); + + et->et_event_cb = event; + et->et_deregister_cb = deregister; + et->et_arg = arg; + return (0); +} + +int +et_periodic(struct eventtimer *et, uint64_t freq) +{ + + if (et->et_event_cb == NULL) + return (ENXIO); + if ((et->et_flags & ET_FLAGS_PERIODIC) == 0) + return (ENODEV); + if (et->et_periodic) + return (et->et_periodic(et, freq)); + return (0); +} + +int +et_oneshot(struct eventtimer *et, uint64_t delay) +{ + + if (et->et_event_cb == NULL) + return (ENXIO); + if ((et->et_flags & ET_FLAGS_ONESHOT) == 0) + return (ENODEV); + if (et->et_oneshot) + return (et->et_oneshot(et, delay)); + return (0); +} + +int +et_stop(struct eventtimer *et) +{ + + if (et->et_event_cb == NULL) + return (ENXIO); + if (et->et_stop) + return (et->et_stop(et)); + return (0); +} + +int +et_free(struct eventtimer *et) +{ + + if (et->et_event_cb == NULL) + return (ENXIO); + + et->et_event_cb = NULL; + et->et_deregister_cb = NULL; + et->et_arg = NULL; + return (0); +} + +/* Report or change the active event timers hardware. */ +static int +sysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS) +{ + char buf[32], *spc; + struct eventtimer *et; + int error; + + spc = ""; + error = 0; + SLIST_FOREACH(et, &eventtimers, et_all) { + sprintf(buf, "%s%s(%d)", + spc, et->et_name, et->et_quality); + error = SYSCTL_OUT(req, buf, strlen(buf)); + spc = " "; + } + return (error); +} + +SYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD, + 0, 0, sysctl_kern_eventtimer_choice, "A", ""); + + diff -ruNp --exclude compile sys.prev/pc98/cbus/clock.c sys/pc98/cbus/clock.c --- sys.prev/pc98/cbus/clock.c 2010-05-24 10:26:09.000000000 +0300 +++ sys/pc98/cbus/clock.c 2010-05-28 10:41:37.000000000 +0300 @@ -93,16 +93,12 @@ TUNABLE_INT("hw.i8254.freq", &i8254_freq int i8254_max_count; static int i8254_real_max_count; -static int lapic_allclocks = 1; -TUNABLE_INT("machdep.lapic_allclocks", &lapic_allclocks); - static struct mtx clock_lock; static struct intsrc *i8254_intsrc; static u_int32_t i8254_lastcount; static u_int32_t i8254_offset; static int (*i8254_pending)(struct intsrc *); static int i8254_ticked; -static enum lapic_clock using_lapic_timer = LAPIC_CLOCK_NONE; /* Values for timerX_state: */ #define RELEASED 0 @@ -113,7 +109,9 @@ static enum lapic_clock using_lapic_time static u_char timer1_state; static unsigned i8254_get_timecount(struct timecounter *tc); +#if 0 static unsigned i8254_simple_get_timecount(struct timecounter *tc); +#endif static void set_i8254_freq(u_int freq, int intr_freq); static struct timecounter i8254_timecounter = { @@ -155,8 +153,6 @@ clkintr(struct trapframe *frame) clkintr_pending = 0; mtx_unlock_spin(&clock_lock); } - KASSERT(using_lapic_timer == LAPIC_CLOCK_NONE, - ("clk interrupt enabled with lapic timer")); #ifdef KDTRACE_HOOKS /* @@ -352,7 +348,7 @@ set_i8254_freq(u_int freq, int intr_freq i8254_timecounter.tc_frequency = freq; mtx_lock_spin(&clock_lock); i8254_freq = freq; - if (using_lapic_timer != LAPIC_CLOCK_NONE) + if (intr_freq == 0) new_i8254_real_max_count = 0x10000; else new_i8254_real_max_count = TIMER_DIV(intr_freq); @@ -425,39 +421,19 @@ startrtclock() void cpu_initclocks() { -#if defined(DEV_APIC) - enum lapic_clock tlsca; - tlsca = lapic_allclocks == 0 ? LAPIC_CLOCK_HARDCLOCK : LAPIC_CLOCK_ALL; - using_lapic_timer = lapic_setup_clock(tlsca); -#endif - /* - * If we aren't using the local APIC timer to drive the kernel - * clocks, setup the interrupt handler for the 8254 timer 0 so - * that it can drive hardclock(). Otherwise, change the 8254 - * timecounter to user a simpler algorithm. - */ - if (using_lapic_timer == LAPIC_CLOCK_NONE) { - timer1hz = hz; - intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL, - NULL, INTR_TYPE_CLK, NULL); - i8254_intsrc = intr_lookup_source(0); - if (i8254_intsrc != NULL) - i8254_pending = - i8254_intsrc->is_pic->pic_source_pending; - } else { - i8254_timecounter.tc_get_timecount = - i8254_simple_get_timecount; - i8254_timecounter.tc_counter_mask = 0xffff; - set_i8254_freq(i8254_freq, hz); - } - if (using_lapic_timer != LAPIC_CLOCK_ALL) { - profhz = hz; - if (hz < 128) - stathz = hz; - else - stathz = hz / (hz / 128); - } + timer1hz = hz; + intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL, + NULL, INTR_TYPE_CLK, NULL); + i8254_intsrc = intr_lookup_source(0); + if (i8254_intsrc != NULL) + i8254_pending = + i8254_intsrc->is_pic->pic_source_pending; + profhz = hz; + if (hz < 128) + stathz = hz; + else + stathz = hz / (hz / 128); timer2hz = 0; init_TSC_tc(); @@ -493,12 +469,14 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(u_int), sysctl_machdep_i8254_freq, "IU", ""); +#if 0 static unsigned i8254_simple_get_timecount(struct timecounter *tc) { return (i8254_max_count - getit()); } +#endif static unsigned i8254_get_timecount(struct timecounter *tc) diff -ruNp --exclude compile sys.prev/sys/timeet.h sys/sys/timeet.h --- sys.prev/sys/timeet.h 1970-01-01 03:00:00.000000000 +0300 +++ sys/sys/timeet.h 2010-05-28 15:00:30.000000000 +0300 @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2010 Alexander Motin + * All rights reserved. + * + * 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, + * without modification, immediately at the beginning of the file. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TIMEEC_H_ +#define _SYS_TIMEEC_H_ + +#ifndef _KERNEL +#error "no user-serviceable parts inside" +#endif + +#include + +/* + * `struct eventtimer' is the interface between the hardware which implements + * a event timer and the MI code which uses this to receive time events. + */ + +struct eventtimer; +typedef int et_periodic_t(struct eventtimer *et, uint64_t freq); +typedef int et_oneshot_t(struct eventtimer *et, uint64_t delay); +typedef int et_stop_t(struct eventtimer *et); +typedef void et_event_cb_t(struct eventtimer *et, void *arg); +typedef int et_deregister_cb_t(struct eventtimer *et, void *arg); + +struct eventtimer { + SLIST_ENTRY(eventtimer) et_all; + /* Pointer to the next event timer. */ + char *et_name; + /* Name of the event timer. */ + int et_flags; + /* Set of capabilities flags: */ +#define ET_FLAGS_PERIODIC 1 +#define ET_FLAGS_ONESHOT 2 +#define ET_FLAGS_PERCPU 4 +#define ET_FLAGS_NOCHANGE 8 + int et_quality; + /* + * Used to determine if this timecounter is better than + * another timecounter. Higher means better. + */ + u_int64_t et_frequency; + /* Base frequency in Hz. */ + et_periodic_t *et_periodic; + et_oneshot_t *et_oneshot; + et_stop_t *et_stop; + et_event_cb_t *et_event_cb; + et_deregister_cb_t *et_deregister_cb; + void *et_arg; + void *et_priv; + /* Pointer to the event timer's private parts. */ +}; + +SLIST_HEAD(eventtimers_list, eventtimer); +extern struct eventtimers_list eventtimers; + +/* Driver API */ +int et_register(struct eventtimer *et); +int et_deregister(struct eventtimer *et); +/* Consumer API */ +struct eventtimer *et_find(const char *name, int flags); + +int et_init(struct eventtimer *et, et_event_cb_t *event, + et_deregister_cb_t *deregister, void *arg); +int et_periodic(struct eventtimer *et, uint64_t freq); +int et_oneshot(struct eventtimer *et, uint64_t delay); +int et_stop(struct eventtimer *et); +int et_free(struct eventtimer *et); + +#ifdef SYSCTL_DECL +SYSCTL_DECL(_kern_eventtimer); +#endif + +#endif /* !_SYS_TIMETC_H_ */ diff -ruNp --exclude compile sys.prev/x86/isa/atrtc.c sys/x86/isa/atrtc.c --- sys.prev/x86/isa/atrtc.c 2010-03-20 22:05:08.000000000 +0200 +++ sys/x86/isa/atrtc.c 2010-05-27 16:35:09.000000000 +0300 @@ -1,5 +1,6 @@ /*- * Copyright (c) 2008 Poul-Henning Kamp + * Copyright (c) 2010 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,18 +40,20 @@ __FBSDID("$FreeBSD: src/sys/x86/isa/atrt #include #include #include +#include +#include #include #ifdef DEV_ISA #include #include #endif +#include +#include "clock_if.h" #define RTC_LOCK mtx_lock_spin(&clock_lock) #define RTC_UNLOCK mtx_unlock_spin(&clock_lock) -int atrtcclock_disable = 0; - static int rtc_reg = -1; static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; static u_char rtc_statusb = RTCSB_24HR; @@ -98,7 +101,7 @@ readrtc(int port) return(bcd2bin(rtcin(port))); } -void +static void atrtc_start(void) { @@ -106,7 +109,7 @@ atrtc_start(void) writertc(RTC_STATUSB, RTCSB_24HR); } -void +static void atrtc_rate(unsigned rate) { @@ -114,7 +117,7 @@ atrtc_rate(unsigned rate) writertc(RTC_STATUSA, rtc_statusa); } -void +static void atrtc_enable_intr(void) { @@ -123,6 +126,15 @@ atrtc_enable_intr(void) rtcin(RTC_INTR); } +static void +atrtc_disable_intr(void) +{ + + rtc_statusb &= ~RTCSB_PINTR; + writertc(RTC_STATUSB, rtc_statusb); + rtcin(RTC_INTR); +} + void atrtc_restore(void) { @@ -135,14 +147,12 @@ atrtc_restore(void) rtcin(RTC_INTR); } +/* int atrtc_setup_clock(void) { int diag; - if (atrtcclock_disable) - return (0); - diag = rtcin(RTC_DIAG); if (diag != 0) { printf("RTC BIOS diagnostic error %b\n", @@ -155,20 +165,70 @@ atrtc_setup_clock(void) return (1); } - +*/ /********************************************************************** * RTC driver for subr_rtc */ -#include "clock_if.h" - -#include - struct atrtc_softc { int port_rid, intr_rid; struct resource *port_res; struct resource *intr_res; + struct eventtimer et; }; +static struct atrtc_softc *rtc_sc = NULL; + +static int +rtc_periodic(struct eventtimer *et, uint64_t freq) +{ + + atrtc_rate(17 - fls(freq + freq/2)); + atrtc_enable_intr(); + return (0); +} + +static int +rtc_stop(struct eventtimer *et) +{ + + atrtc_disable_intr(); + return (0); +} + +/* + * This routine receives statistical clock interrupts from the RTC. + * As explained above, these occur at 128 interrupts per second. + * When profiling, we receive interrupts at a rate of 1024 Hz. + * + * This does not actually add as much overhead as it sounds, because + * when the statistical clock is active, the hardclock driver no longer + * needs to keep (inaccurate) statistics on its own. This decouples + * statistics gathering from scheduling interrupts. + * + * The RTC chip requires that we read status register C (RTC_INTR) + * to acknowledge an interrupt, before it will generate the next one. + * Under high interrupt load, rtcintr() can be indefinitely delayed and + * the clock can tick immediately after the read from RTC_INTR. In this + * case, the mc146818A interrupt signal will not drop for long enough + * to register with the 8259 PIC. If an interrupt is missed, the stat + * clock will halt, considerably degrading system performance. This is + * why we use 'while' rather than a more straightforward 'if' below. + * Stat clock ticks can still be lost, causing minor loss of accuracy + * in the statistics, but the stat clock will no longer stop. + */ +static int +rtc_intr(void *arg) +{ + struct atrtc_softc *sc = rtc_sc; + int flag = 0; + + while (rtcin(RTC_INTR) & RTCIR_PERIOD) { + flag = 1; + if (sc && sc->et.et_event_cb) + sc->et.et_event_cb(&sc->et, sc->et.et_arg ? sc->et.et_arg : arg); + } + return(flag ? FILTER_HANDLED : FILTER_STRAY); +} /* * Attach to the ISA PnP descriptors for the timer and realtime clock. @@ -203,16 +263,30 @@ atrtc_attach(device_t dev) * so they show up, correctly attributed, in the big picture. */ - sc = device_get_softc(dev); + rtc_sc = sc = device_get_softc(dev); if (!(sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, IO_RTC, IO_RTC + 1, 2, RF_ACTIVE))) device_printf(dev,"Warning: Couldn't map I/O.\n"); if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->intr_rid, 8, 8, 1, RF_ACTIVE))) device_printf(dev,"Warning: Couldn't map Interrupt.\n"); + intr_add_handler("rtc", 8, + (driver_filter_t *)rtc_intr, NULL, NULL, + INTR_TYPE_CLK, NULL); + atrtc_start(); clock_register(dev, 1000000); - if (resource_int_value("atrtc", 0, "clock", &i) == 0 && i == 0) - atrtcclock_disable = 1; + bzero(&sc->et, sizeof(struct eventtimer)); + if (resource_int_value("atrtc", 0, "clock", &i) != 0 || i != 0) { + sc->et.et_name = "RTC"; + sc->et.et_flags = ET_FLAGS_PERIODIC; + sc->et.et_quality = 0; + sc->et.et_frequency = 32768; + sc->et.et_periodic = rtc_periodic; + sc->et.et_oneshot = NULL; + sc->et.et_stop = rtc_stop; + sc->et.et_priv = dev; + et_register(&sc->et); + } return(0); } diff -ruNp --exclude compile sys.prev/x86/isa/clock.c sys/x86/isa/clock.c --- sys.prev/x86/isa/clock.c 2010-05-24 11:49:21.000000000 +0300 +++ sys/x86/isa/clock.c 2010-05-28 13:55:07.000000000 +0300 @@ -1,5 +1,6 @@ /*- * Copyright (c) 1990 The Regents of the University of California. + * Copyright (c) 2010 Alexander Motin * All rights reserved. * * This code is derived from software contributed to Berkeley by @@ -43,7 +44,6 @@ __FBSDID("$FreeBSD: src/sys/x86/isa/cloc #include "opt_apic.h" #endif #include "opt_clock.h" -#include "opt_kdtrace.h" #include "opt_isa.h" #include "opt_mca.h" @@ -54,21 +54,20 @@ __FBSDID("$FreeBSD: src/sys/x86/isa/cloc #include #include #include -#include #include #include +#include #include #include #include +#include +#include #include #include #include -#include -#include #include #include -#include #include #ifdef DEV_ISA @@ -80,10 +79,6 @@ __FBSDID("$FreeBSD: src/sys/x86/isa/cloc #include #endif -#ifdef KDTRACE_HOOKS -#include -#endif - #define TIMER_DIV(x) ((i8254_freq + (x) / 2) / (x)) int clkintr_pending; @@ -95,17 +90,22 @@ TUNABLE_INT("hw.i8254.freq", &i8254_freq int i8254_max_count; static int i8254_real_max_count; -static int lapic_allclocks = 1; -TUNABLE_INT("machdep.lapic_allclocks", &lapic_allclocks); - struct mtx clock_lock; static struct intsrc *i8254_intsrc; -static u_int32_t i8254_lastcount; -static u_int32_t i8254_offset; +static uint16_t i8254_lastcount; +static uint16_t i8254_offset; static int (*i8254_pending)(struct intsrc *); static int i8254_ticked; -static int using_atrtc_timer; -static enum lapic_clock using_lapic_timer = LAPIC_CLOCK_NONE; + +struct attimer_softc { + int intr_rid; + struct resource *intr_res; + void *intr_handler; + struct timecounter tc; + struct eventtimer et; + u_int freq; +}; +static struct attimer_softc *attimer_sc = NULL; /* Values for timerX_state: */ #define RELEASED 0 @@ -116,39 +116,14 @@ static enum lapic_clock using_lapic_time static u_char timer2_state; static unsigned i8254_get_timecount(struct timecounter *tc); -static unsigned i8254_simple_get_timecount(struct timecounter *tc); static void set_i8254_freq(u_int freq, int intr_freq); -static struct timecounter i8254_timecounter = { - i8254_get_timecount, /* get_timecount */ - 0, /* no poll_pps */ - ~0u, /* counter_mask */ - 0, /* frequency */ - "i8254", /* name */ - 0 /* quality */ -}; - -int -hardclockintr(struct trapframe *frame) -{ - - timer1clock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); - return (FILTER_HANDLED); -} - -int -statclockintr(struct trapframe *frame) -{ - - timer2clock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); - return (FILTER_HANDLED); -} - static int clkintr(struct trapframe *frame) { + struct attimer_softc *sc = attimer_sc; - if (timecounter->tc_get_timecount == i8254_get_timecount) { + if (sc->freq != 0) { mtx_lock_spin(&clock_lock); if (i8254_ticked) i8254_ticked = 0; @@ -159,25 +134,9 @@ clkintr(struct trapframe *frame) clkintr_pending = 0; mtx_unlock_spin(&clock_lock); } - KASSERT(using_lapic_timer == LAPIC_CLOCK_NONE, - ("clk interrupt enabled with lapic timer")); -#ifdef KDTRACE_HOOKS - /* - * If the DTrace hooks are configured and a callback function - * has been registered, then call it to process the high speed - * timers. - */ - int cpu = PCPU_GET(cpuid); - if (cyclic_clock_func[cpu] != NULL) - (*cyclic_clock_func[cpu])(frame); -#endif - -#ifdef SMP - if (smp_started) - ipi_all_but_self(IPI_HARDCLOCK); -#endif - hardclockintr(frame); + if (sc && sc->et.et_event_cb) + sc->et.et_event_cb(&sc->et, sc->et.et_arg ? sc->et.et_arg : frame); #ifdef DEV_MCA /* Reset clock interrupt by asserting bit 7 of port 0x61 */ @@ -233,43 +192,6 @@ timer_spkr_setfreq(int freq) mtx_unlock_spin(&clock_lock); } -/* - * This routine receives statistical clock interrupts from the RTC. - * As explained above, these occur at 128 interrupts per second. - * When profiling, we receive interrupts at a rate of 1024 Hz. - * - * This does not actually add as much overhead as it sounds, because - * when the statistical clock is active, the hardclock driver no longer - * needs to keep (inaccurate) statistics on its own. This decouples - * statistics gathering from scheduling interrupts. - * - * The RTC chip requires that we read status register C (RTC_INTR) - * to acknowledge an interrupt, before it will generate the next one. - * Under high interrupt load, rtcintr() can be indefinitely delayed and - * the clock can tick immediately after the read from RTC_INTR. In this - * case, the mc146818A interrupt signal will not drop for long enough - * to register with the 8259 PIC. If an interrupt is missed, the stat - * clock will halt, considerably degrading system performance. This is - * why we use 'while' rather than a more straightforward 'if' below. - * Stat clock ticks can still be lost, causing minor loss of accuracy - * in the statistics, but the stat clock will no longer stop. - */ -static int -rtcintr(struct trapframe *frame) -{ - int flag = 0; - - while (rtcin(RTC_INTR) & RTCIR_PERIOD) { - flag = 1; -#ifdef SMP - if (smp_started) - ipi_all_but_self(IPI_STATCLOCK); -#endif - statclockintr(frame); - } - return(flag ? FILTER_HANDLED : FILTER_STRAY); -} - static int getit(void) { @@ -407,12 +329,13 @@ DELAY(int n) static void set_i8254_freq(u_int freq, int intr_freq) { + struct attimer_softc *sc = attimer_sc; int new_i8254_real_max_count; - i8254_timecounter.tc_frequency = freq; + sc->tc.tc_frequency = freq; mtx_lock_spin(&clock_lock); i8254_freq = freq; - if (using_lapic_timer != LAPIC_CLOCK_NONE) + if (intr_freq == 0) new_i8254_real_max_count = 0x10000; else new_i8254_real_max_count = TIMER_DIV(intr_freq); @@ -465,115 +388,16 @@ i8254_init(void) { mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE); - set_i8254_freq(i8254_freq, hz); + set_i8254_freq(i8254_freq, 0); } void startrtclock() { - atrtc_start(); - - set_i8254_freq(i8254_freq, hz); - tc_init(&i8254_timecounter); - init_TSC(); } -/* - * Start both clocks running. - */ -void -cpu_initclocks() -{ -#if defined(__amd64__) || defined(DEV_APIC) - enum lapic_clock tlsca; -#endif - int tasc; - - /* Initialize RTC. */ - atrtc_start(); - tasc = atrtc_setup_clock(); - - /* - * If the atrtc successfully initialized and the users didn't force - * otherwise use the LAPIC in order to cater hardclock only, otherwise - * take in charge all the clock sources. - */ -#if defined(__amd64__) || defined(DEV_APIC) - tlsca = (lapic_allclocks == 0 && tasc != 0) ? LAPIC_CLOCK_HARDCLOCK : - LAPIC_CLOCK_ALL; - using_lapic_timer = lapic_setup_clock(tlsca); -#endif - /* - * If we aren't using the local APIC timer to drive the kernel - * clocks, setup the interrupt handler for the 8254 timer 0 so - * that it can drive hardclock(). Otherwise, change the 8254 - * timecounter to user a simpler algorithm. - */ - if (using_lapic_timer == LAPIC_CLOCK_NONE) { - timer1hz = hz; - intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL, - NULL, INTR_TYPE_CLK, NULL); - i8254_intsrc = intr_lookup_source(0); - if (i8254_intsrc != NULL) - i8254_pending = - i8254_intsrc->is_pic->pic_source_pending; - } else { - i8254_timecounter.tc_get_timecount = - i8254_simple_get_timecount; - i8254_timecounter.tc_counter_mask = 0xffff; - set_i8254_freq(i8254_freq, hz); - } - - /* - * If the separate statistics clock hasn't been explicility disabled - * and we aren't already using the local APIC timer to drive the - * kernel clocks, then setup the RTC to periodically interrupt to - * drive statclock() and profclock(). - */ - if (using_lapic_timer != LAPIC_CLOCK_ALL) { - using_atrtc_timer = tasc; - if (using_atrtc_timer) { - timer2hz = RTC_NOPROFRATE; - /* Enable periodic interrupts from the RTC. */ - intr_add_handler("rtc", 8, - (driver_filter_t *)rtcintr, NULL, NULL, - INTR_TYPE_CLK, NULL); - atrtc_enable_intr(); - } else { - profhz = hz; - if (hz < 128) - stathz = hz; - else - stathz = hz / (hz / 128); - timer2hz = 0; - } - } - - init_TSC_tc(); -} - -void -cpu_startprofclock(void) -{ - - if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer) - return; - atrtc_rate(RTCSA_PROF); - timer2hz = RTC_PROFRATE; -} - -void -cpu_stopprofclock(void) -{ - - if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer) - return; - atrtc_rate(RTCSA_NOPROF); - timer2hz = RTC_NOPROFRATE; -} - static int sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS) { @@ -587,7 +411,7 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER freq = i8254_freq; error = sysctl_handle_int(oidp, &freq, 0, req); if (error == 0 && req->newptr != NULL) - set_i8254_freq(freq, hz); + set_i8254_freq(freq, attimer_sc ? attimer_sc->freq : 0); return (error); } @@ -595,19 +419,16 @@ SYSCTL_PROC(_machdep, OID_AUTO, i8254_fr 0, sizeof(u_int), sysctl_machdep_i8254_freq, "IU", ""); static unsigned -i8254_simple_get_timecount(struct timecounter *tc) -{ - - return (i8254_max_count - getit()); -} - -static unsigned i8254_get_timecount(struct timecounter *tc) { + struct attimer_softc *sc = attimer_sc; register_t flags; - u_int count; + uint16_t count; u_int high, low; + if (sc->freq == 0) + return (i8254_max_count - getit()); + #ifdef __amd64__ flags = read_rflags(); #else @@ -635,6 +456,36 @@ i8254_get_timecount(struct timecounter * return (count); } +static int +attimer_periodic(struct eventtimer *et, uint64_t freq) +{ + device_t dev; + struct attimer_softc *sc; + + dev = (device_t)et->et_priv; + sc = device_get_softc(dev); + sc->freq = freq; + set_i8254_freq(i8254_freq, sc->freq); + intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL, + NULL, INTR_TYPE_CLK, &sc->intr_handler); + return (0); +} + +static int +attimer_stop(struct eventtimer *et) +{ + + device_t dev; + struct attimer_softc *sc; + + dev = (device_t)et->et_priv; + sc = device_get_softc(dev); + sc->freq = 0; + set_i8254_freq(i8254_freq, sc->freq); + intr_remove_handler(sc->intr_handler); + return (0); +} + #ifdef DEV_ISA /* * Attach to the ISA PnP descriptors for the timer @@ -658,6 +509,35 @@ attimer_probe(device_t dev) static int attimer_attach(device_t dev) { + struct attimer_softc *sc; + int i; + + attimer_sc = sc = device_get_softc(dev); + bzero(sc, sizeof(struct attimer_softc)); + if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ, + &sc->intr_rid, 0, 0, 1, RF_ACTIVE))) + device_printf(dev,"Warning: Couldn't map Interrupt.\n"); + i8254_intsrc = intr_lookup_source(0); + if (i8254_intsrc != NULL) + i8254_pending = i8254_intsrc->is_pic->pic_source_pending; + set_i8254_freq(i8254_freq, 0); + sc->tc.tc_get_timecount = i8254_get_timecount; + sc->tc.tc_counter_mask = 0xffff; + sc->tc.tc_name = "i8254"; + sc->tc.tc_quality = 0; + sc->tc.tc_priv = dev; + tc_init(&sc->tc); + if (resource_int_value("attimer", 0, "clock", &i) != 0 || i != 0) { + sc->et.et_name = "i8254"; + sc->et.et_flags = ET_FLAGS_PERIODIC; + sc->et.et_quality = 100; + sc->et.et_frequency = i8254_freq; + sc->et.et_periodic = attimer_periodic; + sc->et.et_oneshot = NULL; + sc->et.et_stop = attimer_stop; + sc->et.et_priv = dev; + et_register(&sc->et); + } return(0); } @@ -683,7 +563,7 @@ static device_method_t attimer_methods[] static driver_t attimer_driver = { "attimer", attimer_methods, - 1, /* no softc */ + sizeof(struct attimer_softc), }; static devclass_t attimer_devclass; diff -ruNp --exclude compile sys.prev/x86/x86/eventtimers.c sys/x86/x86/eventtimers.c --- sys.prev/x86/x86/eventtimers.c 1970-01-01 03:00:00.000000000 +0300 +++ sys/x86/x86/eventtimers.c 2010-05-28 18:59:19.000000000 +0300 @@ -0,0 +1,305 @@ +/*- + * Copyright (c) 2010 Alexander Motin + * All rights reserved. + * + * 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, + * without modification, immediately at the beginning of the file. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +/* + * Common routines to manage event timers hardware. + */ + +/* XEN now has own timer routines */ +#ifndef XEN + +#include "opt_clock.h" +#include "opt_kdtrace.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef KDTRACE_HOOKS +#include +cyclic_clock_func_t cyclic_clock_func[MAXCPU]; +#endif + +static void cpu_restartclocks(void); + +static struct eventtimer *timer1 = NULL; +static struct eventtimer *timer2 = NULL; +static char timer1name[32]; +static char timer2name[32]; +static int profiling_on = 0; +static int minirq = 0; + +TUNABLE_INT("kern.eventtimer.minirq", &minirq); +SYSCTL_INT(_kern_eventtimer, OID_AUTO, minirq, CTLFLAG_RDTUN, &minirq, + 0, "Generate as few interrupts as possible"); + +/* Per-CPU timer1 handler. */ +int +hardclockintr(struct trapframe *frame) +{ + +#ifdef KDTRACE_HOOKS + /* + * If the DTrace hooks are configured and a callback function + * has been registered, then call it to process the high speed + * timers. + */ + int cpu = PCPU_GET(cpuid); + if (cyclic_clock_func[cpu] != NULL) + (*cyclic_clock_func[cpu])(frame); +#endif + + timer1clock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + return (FILTER_HANDLED); +} + +/* Per-CPU timer2 handler. */ +int +statclockintr(struct trapframe *frame) +{ + + timer2clock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + return (FILTER_HANDLED); +} + +/* timer1 callback. */ +static void +timer1intr(struct eventtimer *et, void *arg) +{ + +#ifdef SMP + if (smp_started && (et->et_flags & ET_FLAGS_PERCPU) == 0) + ipi_all_but_self(IPI_HARDCLOCK); +#endif + hardclockintr((struct trapframe *)arg); +} + +/* timer2 callback. */ +static void +timer2intr(struct eventtimer *et, void *arg) +{ + +#ifdef SMP + if (smp_started && (et->et_flags & ET_FLAGS_PERCPU) == 0) + ipi_all_but_self(IPI_STATCLOCK); +#endif + statclockintr((struct trapframe *)arg); +} + +/* + * Configure and start both clocks running. + */ +void +cpu_initclocks() +{ + int base, div; + + timer1 = et_find(timer1name, ET_FLAGS_PERIODIC); + if (timer1 == NULL) + timer1 = et_find(NULL, ET_FLAGS_PERIODIC); + et_init(timer1, timer1intr, NULL, NULL); + timer2 = et_find(timer2name[0] ? timer2name : NULL, ET_FLAGS_PERIODIC); + if (timer2) + et_init(timer2, timer2intr, NULL, NULL); + /* + * We honor the requested 'hz' value. + * We want to run stathz in the neighborhood of 128hz. + * We would like profhz to run as often as possible. + */ + if (timer2 == NULL) { + if (hz >= 1500 || minirq) + base = hz; + else if (hz >= 750) + base = hz * 2; + else + base = hz * 4; + if (base < 128) + stathz = base; + else { + div = base / 128; + if (div % 2 == 0) + div++; + stathz = base / div; + } + profhz = stathz; + if (timer1->et_flags & ET_FLAGS_NOCHANGE) { + while ((profhz + stathz) <= base) + profhz += stathz; + } else { + while ((profhz + stathz) <= 8192) + profhz += stathz; + } + } else { + stathz = 128; + if (timer1->et_flags & ET_FLAGS_NOCHANGE) + profhz = stathz; + else + profhz = 8192; + } + cpu_restartclocks(); + init_TSC_tc(); +} + +static void +cpu_restartclocks(void) +{ + + if (timer1hz) + et_stop(timer1); + if (timer2 && timer2hz) + et_stop(timer2); + + if (timer2 == NULL) { + if ((timer1->et_flags & ET_FLAGS_NOCHANGE) == 0 || + timer1hz == 0) { + if (hz >= 1500 || hz == stathz || minirq) + timer1hz = hz; + else if (hz >= 750) + timer1hz = hz * 2; + else + timer1hz = hz * 4; + while (timer1hz < (profiling_on ? profhz : stathz)) + timer1hz += hz; + } + timer2hz = 0; + } else { + if ((timer1->et_flags & ET_FLAGS_NOCHANGE) == 0 || + timer1hz == 0) + timer1hz = hz; + if ((timer2->et_flags & ET_FLAGS_NOCHANGE) == 0 || + timer2hz == 0) { + if (timer2->et_flags & ET_FLAGS_NOCHANGE) { + timer2hz = max(profhz, stathz); + } else + timer2hz = profiling_on ? profhz : stathz; + } + } + et_periodic(timer1, timer1hz); + if (timer2) + et_periodic(timer2, timer2hz); +} + +/* Switch to profiling clock rates. */ +void +cpu_startprofclock(void) +{ + + profiling_on = 1; + cpu_restartclocks(); +} + +/* Switch to regular clock rates. */ +void +cpu_stopprofclock(void) +{ + + profiling_on = 0; + cpu_restartclocks(); +} + +/* Report or change the active event timers hardware. */ +static int +sysctl_kern_eventtimer_timer1(SYSCTL_HANDLER_ARGS) +{ + char buf[32]; + struct eventtimer *et = timer1; + int error; + + snprintf(buf, sizeof(buf), "%s", et->et_name); + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL || + strcmp(buf, et->et_name) == 0) + return (error); + et = et_find(buf, ET_FLAGS_PERIODIC); + if (et == NULL) + return (ENOENT); + et_stop(timer1); + et_free(timer1); + timer1 = et; + et_init(timer1, timer1intr, NULL, NULL); + cpu_restartclocks(); + return (error); +} + +TUNABLE_STR("kern.eventtimer.timer1", timer1name, sizeof(timer1name)); +SYSCTL_PROC(_kern_eventtimer, OID_AUTO, timer1, CTLTYPE_STRING | CTLFLAG_RW, + 0, 0, sysctl_kern_eventtimer_timer1, "A", ""); + +static int +sysctl_kern_eventtimer_timer2(SYSCTL_HANDLER_ARGS) +{ + char buf[32]; + struct eventtimer *et = timer2; + int error; + + if (et == NULL) + snprintf(buf, sizeof(buf), "NONE"); + else + snprintf(buf, sizeof(buf), "%s", et->et_name); + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL || + strcmp(buf, et ? et->et_name : "NONE") == 0) + return (error); + et = et_find(buf, ET_FLAGS_PERIODIC); + if (et == NULL && strcasecmp(buf, "NONE") != 0) + return (ENOENT); + if (timer2 != NULL) { + et_stop(timer2); + et_free(timer2); + } + timer2 = et; + if (timer2 != NULL) + et_init(timer2, timer2intr, NULL, NULL); + cpu_restartclocks(); + return (error); +} + +TUNABLE_STR("kern.eventtimer.timer2", timer2name, sizeof(timer2name)); +SYSCTL_PROC(_kern_eventtimer, OID_AUTO, timer2, CTLTYPE_STRING | CTLFLAG_RW, + 0, 0, sysctl_kern_eventtimer_timer2, "A", ""); + +#endif + diff -ruNp --exclude compile sys.prev/x86/x86/local_apic.c sys/x86/x86/local_apic.c --- sys.prev/x86/x86/local_apic.c 2010-05-24 09:17:17.000000000 +0300 +++ sys/x86/x86/local_apic.c 2010-05-28 21:33:02.000000000 +0300 @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD: head/sys/x86/x86/loc #include #include #include +#include #include #include @@ -78,11 +79,6 @@ __FBSDID("$FreeBSD: head/sys/x86/x86/loc #define GSEL_APIC GSEL(GCODE_SEL, SEL_KPL) #endif -#ifdef KDTRACE_HOOKS -#include -cyclic_clock_func_t cyclic_clock_func[MAXCPU]; -#endif - /* Sanity checks on IDT vectors. */ CTASSERT(APIC_IO_INTS + APIC_NUM_IOINTS == APIC_TIMER_INT); CTASSERT(APIC_TIMER_INT < APIC_LOCAL_INTS); @@ -154,7 +150,7 @@ extern inthand_t IDTVEC(rsvd); volatile lapic_t *lapic; vm_paddr_t lapic_paddr; static u_long lapic_timer_divisor, lapic_timer_period, lapic_timer_hz; -static enum lapic_clock clockcoverage; +static struct eventtimer lapic_et; static void lapic_enable(void); static void lapic_resume(struct pic *pic); @@ -163,6 +159,7 @@ static void lapic_timer_oneshot(u_int co static void lapic_timer_periodic(u_int count); static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); +static int lapic_periodic(struct eventtimer *et, uint64_t freq); struct pic lapic_pic = { .pic_resume = lapic_resume }; @@ -213,6 +210,7 @@ lvt_mode(struct lapic *la, u_int pin, ui void lapic_init(vm_paddr_t addr) { + int i; /* Map the local APIC and setup the spurious interrupt handler. */ KASSERT(trunc_page(addr) == addr, @@ -235,6 +233,20 @@ lapic_init(vm_paddr_t addr) setidt(APIC_ERROR_INT, IDTVEC(errorint), SDT_APIC, SEL_KPL, GSEL_APIC); /* XXX: Thermal interrupt */ + + if ((resource_int_value("apic", 0, "clock", &i) != 0 || i != 0)) { + bzero(&lapic_et, sizeof(lapic_et)); + lapic_et.et_name = "LAPIC"; + lapic_et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_PERCPU | + ET_FLAGS_NOCHANGE; + lapic_et.et_quality = 500; + lapic_et.et_frequency = 0; + lapic_et.et_periodic = lapic_periodic; + lapic_et.et_oneshot = NULL; + lapic_et.et_stop = NULL; + lapic_et.et_priv = NULL; + et_register(&lapic_et); + } } /* @@ -426,88 +438,40 @@ lapic_disable_pmc(void) #endif } -/* - * Called by cpu_initclocks() on the BSP to setup the local APIC timer so - * that it can drive hardclock, statclock, and profclock. - */ -enum lapic_clock -lapic_setup_clock(enum lapic_clock srcsdes) +static int +lapic_periodic(struct eventtimer *et, uint64_t freq) { u_long value; - int i; - - /* lapic_setup_clock() should not be called with LAPIC_CLOCK_NONE. */ - MPASS(srcsdes != LAPIC_CLOCK_NONE); - - /* Can't drive the timer without a local APIC. */ - if (lapic == NULL || - (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0)) { - clockcoverage = LAPIC_CLOCK_NONE; - return (clockcoverage); - } - - /* Start off with a divisor of 2 (power on reset default). */ - lapic_timer_divisor = 2; - - /* Try to calibrate the local APIC timer. */ - do { - lapic_timer_set_divisor(lapic_timer_divisor); - lapic_timer_oneshot(APIC_TIMER_MAX_COUNT); - DELAY(2000000); - value = APIC_TIMER_MAX_COUNT - lapic->ccr_timer; - if (value != APIC_TIMER_MAX_COUNT) - break; - lapic_timer_divisor <<= 1; - } while (lapic_timer_divisor <= 128); - if (lapic_timer_divisor > 128) - panic("lapic: Divisor too big"); - value /= 2; - if (bootverbose) - printf("lapic: Divisor %lu, Frequency %lu Hz\n", - lapic_timer_divisor, value); - /* - * We want to run stathz in the neighborhood of 128hz. We would - * like profhz to run as often as possible, so we let it run on - * each clock tick. We try to honor the requested 'hz' value as - * much as possible. - * - * If 'hz' is above 1500, then we just let the lapic timer - * (and profhz) run at hz. If 'hz' is below 1500 but above - * 750, then we let the lapic timer run at 2 * 'hz'. If 'hz' - * is below 750 then we let the lapic timer run at 4 * 'hz'. - * - * Please note that stathz and profhz are set only if all the - * clocks are handled through the local APIC. - */ - if (srcsdes == LAPIC_CLOCK_ALL) { - if (hz >= 1500) - lapic_timer_hz = hz; - else if (hz >= 750) - lapic_timer_hz = hz * 2; - else - lapic_timer_hz = hz * 4; - } else - lapic_timer_hz = hz; - lapic_timer_period = value / lapic_timer_hz; - timer1hz = lapic_timer_hz; - if (srcsdes == LAPIC_CLOCK_ALL) { - if (lapic_timer_hz < 128) - stathz = lapic_timer_hz; - else - stathz = lapic_timer_hz / (lapic_timer_hz / 128); - profhz = lapic_timer_hz; - timer2hz = 0; + if (et->et_frequency == 0) { + /* Start off with a divisor of 2 (power on reset default). */ + lapic_timer_divisor = 2; + /* Try to calibrate the local APIC timer. */ + do { + lapic_timer_set_divisor(lapic_timer_divisor); + lapic_timer_oneshot(APIC_TIMER_MAX_COUNT); + DELAY(1000000); + value = APIC_TIMER_MAX_COUNT - lapic->ccr_timer; + if (value != APIC_TIMER_MAX_COUNT) + break; + lapic_timer_divisor <<= 1; + } while (lapic_timer_divisor <= 128); + if (lapic_timer_divisor > 128) + panic("lapic: Divisor too big"); + if (bootverbose) + printf("lapic: Divisor %lu, Frequency %lu Hz\n", + lapic_timer_divisor, value); + lapic_et.et_frequency = value; } - + lapic_timer_hz = freq; + lapic_timer_period = et->et_frequency / lapic_timer_hz; /* * Start up the timer on the BSP. The APs will kick off their * timer during lapic_setup(). */ lapic_timer_periodic(lapic_timer_period); lapic_timer_enable_intr(); - clockcoverage = srcsdes; - return (srcsdes); + return (0); } void @@ -777,19 +741,9 @@ lapic_handle_timer(struct trapframe *fra la = &lapics[PCPU_GET(apic_id)]; (*la->la_timer_count)++; critical_enter(); - -#ifdef KDTRACE_HOOKS - /* - * If the DTrace hooks are configured and a callback function - * has been registered, then call it to process the high speed - * timers. - */ - int cpu = PCPU_GET(cpuid); - if (cyclic_clock_func[cpu] != NULL) - (*cyclic_clock_func[cpu])(frame); -#endif - - timer1clock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + if (lapic_et.et_event_cb) + lapic_et.et_event_cb(&lapic_et, + lapic_et.et_arg ? lapic_et.et_arg : frame); critical_exit(); }