14fa7241fSPoul-Henning Kamp /*-
24fa7241fSPoul-Henning Kamp * Copyright (c) 2005 Poul-Henning Kamp
349ed68bbSAlexander Motin * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
44fa7241fSPoul-Henning Kamp * All rights reserved.
54fa7241fSPoul-Henning Kamp *
64fa7241fSPoul-Henning Kamp * Redistribution and use in source and binary forms, with or without
74fa7241fSPoul-Henning Kamp * modification, are permitted provided that the following conditions
84fa7241fSPoul-Henning Kamp * are met:
94fa7241fSPoul-Henning Kamp * 1. Redistributions of source code must retain the above copyright
104fa7241fSPoul-Henning Kamp * notice, this list of conditions and the following disclaimer.
114fa7241fSPoul-Henning Kamp * 2. Redistributions in binary form must reproduce the above copyright
124fa7241fSPoul-Henning Kamp * notice, this list of conditions and the following disclaimer in the
134fa7241fSPoul-Henning Kamp * documentation and/or other materials provided with the distribution.
144fa7241fSPoul-Henning Kamp *
154fa7241fSPoul-Henning Kamp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164fa7241fSPoul-Henning Kamp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174fa7241fSPoul-Henning Kamp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184fa7241fSPoul-Henning Kamp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194fa7241fSPoul-Henning Kamp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204fa7241fSPoul-Henning Kamp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214fa7241fSPoul-Henning Kamp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224fa7241fSPoul-Henning Kamp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234fa7241fSPoul-Henning Kamp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244fa7241fSPoul-Henning Kamp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254fa7241fSPoul-Henning Kamp * SUCH DAMAGE.
264fa7241fSPoul-Henning Kamp */
274fa7241fSPoul-Henning Kamp
284fa7241fSPoul-Henning Kamp #include <sys/cdefs.h>
294fa7241fSPoul-Henning Kamp #include "opt_acpi.h"
3016808549SKonstantin Belousov
31e7d939bdSMarcel Moolenaar #if defined(__amd64__)
32875b8844SAlexander Motin #define DEV_APIC
33875b8844SAlexander Motin #else
34875b8844SAlexander Motin #include "opt_apic.h"
35875b8844SAlexander Motin #endif
364fa7241fSPoul-Henning Kamp #include <sys/param.h>
373149cc9dSRui Paulo #include <sys/conf.h>
38c73930f3SNate Lawson #include <sys/bus.h>
394fa7241fSPoul-Henning Kamp #include <sys/kernel.h>
404fa7241fSPoul-Henning Kamp #include <sys/module.h>
41875b8844SAlexander Motin #include <sys/proc.h>
424fa7241fSPoul-Henning Kamp #include <sys/rman.h>
433149cc9dSRui Paulo #include <sys/mman.h>
444fa7241fSPoul-Henning Kamp #include <sys/time.h>
45875b8844SAlexander Motin #include <sys/smp.h>
46875b8844SAlexander Motin #include <sys/sysctl.h>
47875b8844SAlexander Motin #include <sys/timeet.h>
484fa7241fSPoul-Henning Kamp #include <sys/timetc.h>
4916808549SKonstantin Belousov #include <sys/vdso.h>
50c73930f3SNate Lawson
51129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
52129d3046SJung-uk Kim #include <contrib/dev/acpica/include/accommon.h>
53129d3046SJung-uk Kim
544fa7241fSPoul-Henning Kamp #include <dev/acpica/acpivar.h>
55f831d6e0SJohn Baldwin #include <dev/acpica/acpi_hpet.h>
564fa7241fSPoul-Henning Kamp
57875b8844SAlexander Motin #ifdef DEV_APIC
58875b8844SAlexander Motin #include "pcib_if.h"
59875b8844SAlexander Motin #endif
60875b8844SAlexander Motin
619a6a6ecbSAndriy Gapon #define HPET_VENDID_AMD 0x4353
62df980c68SAlexander Motin #define HPET_VENDID_AMD2 0x1022
63fc913424SKonstantin Belousov #define HPET_VENDID_HYGON 0x1d94
649a6a6ecbSAndriy Gapon #define HPET_VENDID_INTEL 0x8086
653a2c9a26SAlexander Motin #define HPET_VENDID_NVIDIA 0x10de
66fd94de5cSAlexander Motin #define HPET_VENDID_SW 0x1166
679a6a6ecbSAndriy Gapon
684fa7241fSPoul-Henning Kamp ACPI_SERIAL_DECL(hpet, "ACPI HPET support");
694fa7241fSPoul-Henning Kamp
705be4c55fSScott Long /* ACPI CA debugging */
716a4810bdSScott Long #define _COMPONENT ACPI_TIMER
725be4c55fSScott Long ACPI_MODULE_NAME("HPET")
735be4c55fSScott Long
74875b8844SAlexander Motin struct hpet_softc {
754fa7241fSPoul-Henning Kamp device_t dev;
76875b8844SAlexander Motin int mem_rid;
77875b8844SAlexander Motin int intr_rid;
78875b8844SAlexander Motin int irq;
79875b8844SAlexander Motin int useirq;
8049ed68bbSAlexander Motin int legacy_route;
81373d257eSAlexander Motin int per_cpu;
8209538b10SAlexander Motin uint32_t allowed_irqs;
83c73930f3SNate Lawson struct resource *mem_res;
84875b8844SAlexander Motin struct resource *intr_res;
85875b8844SAlexander Motin void *intr_handle;
864fa7241fSPoul-Henning Kamp ACPI_HANDLE handle;
872fe1339eSKonstantin Belousov uint32_t acpi_uid;
88875b8844SAlexander Motin uint64_t freq;
8949ed68bbSAlexander Motin uint32_t caps;
90875b8844SAlexander Motin struct timecounter tc;
91875b8844SAlexander Motin struct hpet_timer {
92875b8844SAlexander Motin struct eventtimer et;
93875b8844SAlexander Motin struct hpet_softc *sc;
94875b8844SAlexander Motin int num;
95875b8844SAlexander Motin int mode;
967bf91e9aSAndriy Gapon #define TIMER_STOPPED 0
977bf91e9aSAndriy Gapon #define TIMER_PERIODIC 1
987bf91e9aSAndriy Gapon #define TIMER_ONESHOT 2
99875b8844SAlexander Motin int intr_rid;
100875b8844SAlexander Motin int irq;
101b28fc1b5SAlexander Motin int pcpu_cpu;
102b28fc1b5SAlexander Motin int pcpu_misrouted;
103875b8844SAlexander Motin int pcpu_master;
104875b8844SAlexander Motin int pcpu_slaves[MAXCPU];
105875b8844SAlexander Motin struct resource *intr_res;
106875b8844SAlexander Motin void *intr_handle;
107875b8844SAlexander Motin uint32_t caps;
108875b8844SAlexander Motin uint32_t vectors;
109875b8844SAlexander Motin uint32_t div;
1106184f8d6SAlexander Motin uint32_t next;
111875b8844SAlexander Motin char name[8];
112875b8844SAlexander Motin } t[32];
113875b8844SAlexander Motin int num_timers;
1143149cc9dSRui Paulo struct cdev *pdev;
1153149cc9dSRui Paulo int mmap_allow;
1163149cc9dSRui Paulo int mmap_allow_write;
1173149cc9dSRui Paulo };
1183149cc9dSRui Paulo
1193149cc9dSRui Paulo static d_open_t hpet_open;
1203149cc9dSRui Paulo static d_mmap_t hpet_mmap;
1213149cc9dSRui Paulo
1223149cc9dSRui Paulo static struct cdevsw hpet_cdevsw = {
1233149cc9dSRui Paulo .d_version = D_VERSION,
1243149cc9dSRui Paulo .d_name = "hpet",
1253149cc9dSRui Paulo .d_open = hpet_open,
1263149cc9dSRui Paulo .d_mmap = hpet_mmap,
1274fa7241fSPoul-Henning Kamp };
1284fa7241fSPoul-Henning Kamp
129c73930f3SNate Lawson static u_int hpet_get_timecount(struct timecounter *tc);
130875b8844SAlexander Motin static void hpet_test(struct hpet_softc *sc);
131c73930f3SNate Lawson
132c73930f3SNate Lawson static char *hpet_ids[] = { "PNP0103", NULL };
133c73930f3SNate Lawson
134c2641d23SRoger Pau Monné /* Knob to disable acpi_hpet device */
135c2641d23SRoger Pau Monné bool acpi_hpet_disabled = false;
136c2641d23SRoger Pau Monné
137c73930f3SNate Lawson static u_int
hpet_get_timecount(struct timecounter * tc)1384fa7241fSPoul-Henning Kamp hpet_get_timecount(struct timecounter *tc)
1394fa7241fSPoul-Henning Kamp {
140875b8844SAlexander Motin struct hpet_softc *sc;
1414fa7241fSPoul-Henning Kamp
1424fa7241fSPoul-Henning Kamp sc = tc->tc_priv;
143f831d6e0SJohn Baldwin return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
1444fa7241fSPoul-Henning Kamp }
1454fa7241fSPoul-Henning Kamp
14616808549SKonstantin Belousov uint32_t
hpet_vdso_timehands(struct vdso_timehands * vdso_th,struct timecounter * tc)14716808549SKonstantin Belousov hpet_vdso_timehands(struct vdso_timehands *vdso_th, struct timecounter *tc)
14816808549SKonstantin Belousov {
14916808549SKonstantin Belousov struct hpet_softc *sc;
15016808549SKonstantin Belousov
15116808549SKonstantin Belousov sc = tc->tc_priv;
15216808549SKonstantin Belousov vdso_th->th_algo = VDSO_TH_ALGO_X86_HPET;
15316808549SKonstantin Belousov vdso_th->th_x86_shift = 0;
15416808549SKonstantin Belousov vdso_th->th_x86_hpet_idx = device_get_unit(sc->dev);
155d4b2d303SAdam Fenn vdso_th->th_x86_pvc_last_systime = 0;
156d4b2d303SAdam Fenn vdso_th->th_x86_pvc_stable_mask = 0;
15716808549SKonstantin Belousov bzero(vdso_th->th_res, sizeof(vdso_th->th_res));
15816808549SKonstantin Belousov return (sc->mmap_allow != 0);
15916808549SKonstantin Belousov }
16016808549SKonstantin Belousov
16116808549SKonstantin Belousov #ifdef COMPAT_FREEBSD32
16216808549SKonstantin Belousov uint32_t
hpet_vdso_timehands32(struct vdso_timehands32 * vdso_th32,struct timecounter * tc)16316808549SKonstantin Belousov hpet_vdso_timehands32(struct vdso_timehands32 *vdso_th32,
16416808549SKonstantin Belousov struct timecounter *tc)
16516808549SKonstantin Belousov {
16616808549SKonstantin Belousov struct hpet_softc *sc;
16716808549SKonstantin Belousov
16816808549SKonstantin Belousov sc = tc->tc_priv;
16916808549SKonstantin Belousov vdso_th32->th_algo = VDSO_TH_ALGO_X86_HPET;
17016808549SKonstantin Belousov vdso_th32->th_x86_shift = 0;
17116808549SKonstantin Belousov vdso_th32->th_x86_hpet_idx = device_get_unit(sc->dev);
17293626d54SKonstantin Belousov vdso_th32->th_x86_pvc_last_systime[0] = 0;
17393626d54SKonstantin Belousov vdso_th32->th_x86_pvc_last_systime[1] = 0;
174d4b2d303SAdam Fenn vdso_th32->th_x86_pvc_stable_mask = 0;
17516808549SKonstantin Belousov bzero(vdso_th32->th_res, sizeof(vdso_th32->th_res));
17616808549SKonstantin Belousov return (sc->mmap_allow != 0);
17716808549SKonstantin Belousov }
17816808549SKonstantin Belousov #endif
17916808549SKonstantin Belousov
180572f347dSJohn Baldwin static void
hpet_enable(struct hpet_softc * sc)181875b8844SAlexander Motin hpet_enable(struct hpet_softc *sc)
182572f347dSJohn Baldwin {
183572f347dSJohn Baldwin uint32_t val;
184572f347dSJohn Baldwin
185f831d6e0SJohn Baldwin val = bus_read_4(sc->mem_res, HPET_CONFIG);
18649ed68bbSAlexander Motin if (sc->legacy_route)
18749ed68bbSAlexander Motin val |= HPET_CNF_LEG_RT;
18849ed68bbSAlexander Motin else
189ab9df4d7SJung-uk Kim val &= ~HPET_CNF_LEG_RT;
190ab9df4d7SJung-uk Kim val |= HPET_CNF_ENABLE;
191ab9df4d7SJung-uk Kim bus_write_4(sc->mem_res, HPET_CONFIG, val);
192572f347dSJohn Baldwin }
193572f347dSJohn Baldwin
194572f347dSJohn Baldwin static void
hpet_disable(struct hpet_softc * sc)195875b8844SAlexander Motin hpet_disable(struct hpet_softc *sc)
196572f347dSJohn Baldwin {
197572f347dSJohn Baldwin uint32_t val;
198572f347dSJohn Baldwin
199f831d6e0SJohn Baldwin val = bus_read_4(sc->mem_res, HPET_CONFIG);
200ab9df4d7SJung-uk Kim val &= ~HPET_CNF_ENABLE;
201ab9df4d7SJung-uk Kim bus_write_4(sc->mem_res, HPET_CONFIG, val);
202572f347dSJohn Baldwin }
203572f347dSJohn Baldwin
204875b8844SAlexander Motin static int
hpet_start(struct eventtimer * et,sbintime_t first,sbintime_t period)205fdc5dd2dSAlexander Motin hpet_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
206875b8844SAlexander Motin {
207875b8844SAlexander Motin struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
208875b8844SAlexander Motin struct hpet_timer *t;
209875b8844SAlexander Motin struct hpet_softc *sc = mt->sc;
2106184f8d6SAlexander Motin uint32_t fdiv, now;
211875b8844SAlexander Motin
212875b8844SAlexander Motin t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
213fdc5dd2dSAlexander Motin if (period != 0) {
2147bf91e9aSAndriy Gapon t->mode = TIMER_PERIODIC;
215fdc5dd2dSAlexander Motin t->div = (sc->freq * period) >> 32;
216875b8844SAlexander Motin } else {
2177bf91e9aSAndriy Gapon t->mode = TIMER_ONESHOT;
218875b8844SAlexander Motin t->div = 0;
219875b8844SAlexander Motin }
220fdc5dd2dSAlexander Motin if (first != 0)
221fdc5dd2dSAlexander Motin fdiv = (sc->freq * first) >> 32;
222fdc5dd2dSAlexander Motin else
22351636352SAlexander Motin fdiv = t->div;
22409538b10SAlexander Motin if (t->irq < 0)
22509538b10SAlexander Motin bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
22609538b10SAlexander Motin t->caps |= HPET_TCNF_INT_ENB;
2276184f8d6SAlexander Motin now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
22809538b10SAlexander Motin restart:
2296184f8d6SAlexander Motin t->next = now + fdiv;
2307bf91e9aSAndriy Gapon if (t->mode == TIMER_PERIODIC && (t->caps & HPET_TCAP_PER_INT)) {
231875b8844SAlexander Motin t->caps |= HPET_TCNF_TYPE;
232875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
233875b8844SAlexander Motin t->caps | HPET_TCNF_VAL_SET);
2346184f8d6SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
2356184f8d6SAlexander Motin t->next);
2366184f8d6SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
2376184f8d6SAlexander Motin t->div);
238875b8844SAlexander Motin } else {
23909538b10SAlexander Motin t->caps &= ~HPET_TCNF_TYPE;
2406184f8d6SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
2416184f8d6SAlexander Motin t->caps);
2426184f8d6SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
2436184f8d6SAlexander Motin t->next);
24409538b10SAlexander Motin }
2456184f8d6SAlexander Motin now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
24698393876SAlexander Motin if ((int32_t)(now - t->next + HPET_MIN_CYCLES) >= 0) {
24709538b10SAlexander Motin fdiv *= 2;
24809538b10SAlexander Motin goto restart;
24909538b10SAlexander Motin }
250875b8844SAlexander Motin return (0);
251875b8844SAlexander Motin }
252875b8844SAlexander Motin
253875b8844SAlexander Motin static int
hpet_stop(struct eventtimer * et)254875b8844SAlexander Motin hpet_stop(struct eventtimer *et)
255875b8844SAlexander Motin {
256875b8844SAlexander Motin struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
257875b8844SAlexander Motin struct hpet_timer *t;
258875b8844SAlexander Motin struct hpet_softc *sc = mt->sc;
259875b8844SAlexander Motin
260875b8844SAlexander Motin t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
2617bf91e9aSAndriy Gapon t->mode = TIMER_STOPPED;
262875b8844SAlexander Motin t->caps &= ~(HPET_TCNF_INT_ENB | HPET_TCNF_TYPE);
263875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
264875b8844SAlexander Motin return (0);
265875b8844SAlexander Motin }
266875b8844SAlexander Motin
267875b8844SAlexander Motin static int
hpet_intr_single(void * arg)268875b8844SAlexander Motin hpet_intr_single(void *arg)
269875b8844SAlexander Motin {
270875b8844SAlexander Motin struct hpet_timer *t = (struct hpet_timer *)arg;
271875b8844SAlexander Motin struct hpet_timer *mt;
272875b8844SAlexander Motin struct hpet_softc *sc = t->sc;
273875b8844SAlexander Motin uint32_t now;
274875b8844SAlexander Motin
2757bf91e9aSAndriy Gapon if (t->mode == TIMER_STOPPED)
2766184f8d6SAlexander Motin return (FILTER_STRAY);
277b28fc1b5SAlexander Motin /* Check that per-CPU timer interrupt reached right CPU. */
278b28fc1b5SAlexander Motin if (t->pcpu_cpu >= 0 && t->pcpu_cpu != curcpu) {
279b28fc1b5SAlexander Motin if ((++t->pcpu_misrouted) % 32 == 0) {
280b28fc1b5SAlexander Motin printf("HPET interrupt routed to the wrong CPU"
281b28fc1b5SAlexander Motin " (timer %d CPU %d -> %d)!\n",
282b28fc1b5SAlexander Motin t->num, t->pcpu_cpu, curcpu);
283b28fc1b5SAlexander Motin }
284b28fc1b5SAlexander Motin
285b28fc1b5SAlexander Motin /*
286b28fc1b5SAlexander Motin * Reload timer, hoping that next time may be more lucky
287b28fc1b5SAlexander Motin * (system will manage proper interrupt binding).
288b28fc1b5SAlexander Motin */
2897bf91e9aSAndriy Gapon if ((t->mode == TIMER_PERIODIC &&
2907bf91e9aSAndriy Gapon (t->caps & HPET_TCAP_PER_INT) == 0) ||
2917bf91e9aSAndriy Gapon t->mode == TIMER_ONESHOT) {
2926184f8d6SAlexander Motin t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER) +
2936184f8d6SAlexander Motin sc->freq / 8;
294b28fc1b5SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
2956184f8d6SAlexander Motin t->next);
296b28fc1b5SAlexander Motin }
297b28fc1b5SAlexander Motin return (FILTER_HANDLED);
298b28fc1b5SAlexander Motin }
2997bf91e9aSAndriy Gapon if (t->mode == TIMER_PERIODIC &&
300875b8844SAlexander Motin (t->caps & HPET_TCAP_PER_INT) == 0) {
3016184f8d6SAlexander Motin t->next += t->div;
302875b8844SAlexander Motin now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
3036184f8d6SAlexander Motin if ((int32_t)((now + t->div / 2) - t->next) > 0)
3046184f8d6SAlexander Motin t->next = now + t->div / 2;
305875b8844SAlexander Motin bus_write_4(sc->mem_res,
3066184f8d6SAlexander Motin HPET_TIMER_COMPARATOR(t->num), t->next);
3077bf91e9aSAndriy Gapon } else if (t->mode == TIMER_ONESHOT)
3087bf91e9aSAndriy Gapon t->mode = TIMER_STOPPED;
309875b8844SAlexander Motin mt = (t->pcpu_master < 0) ? t : &sc->t[t->pcpu_master];
3108a687080SAlexander Motin if (mt->et.et_active)
3118a687080SAlexander Motin mt->et.et_event_cb(&mt->et, mt->et.et_arg);
312875b8844SAlexander Motin return (FILTER_HANDLED);
313875b8844SAlexander Motin }
314875b8844SAlexander Motin
315875b8844SAlexander Motin static int
hpet_intr(void * arg)316875b8844SAlexander Motin hpet_intr(void *arg)
317875b8844SAlexander Motin {
318875b8844SAlexander Motin struct hpet_softc *sc = (struct hpet_softc *)arg;
319875b8844SAlexander Motin int i;
320875b8844SAlexander Motin uint32_t val;
321875b8844SAlexander Motin
322875b8844SAlexander Motin val = bus_read_4(sc->mem_res, HPET_ISR);
323875b8844SAlexander Motin if (val) {
324875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_ISR, val);
325875b8844SAlexander Motin val &= sc->useirq;
326875b8844SAlexander Motin for (i = 0; i < sc->num_timers; i++) {
327875b8844SAlexander Motin if ((val & (1 << i)) == 0)
328875b8844SAlexander Motin continue;
329875b8844SAlexander Motin hpet_intr_single(&sc->t[i]);
330875b8844SAlexander Motin }
331875b8844SAlexander Motin return (FILTER_HANDLED);
332875b8844SAlexander Motin }
333875b8844SAlexander Motin return (FILTER_STRAY);
334875b8844SAlexander Motin }
335875b8844SAlexander Motin
3362fe1339eSKonstantin Belousov uint32_t
hpet_get_uid(device_t dev)3372fe1339eSKonstantin Belousov hpet_get_uid(device_t dev)
3382fe1339eSKonstantin Belousov {
3392fe1339eSKonstantin Belousov struct hpet_softc *sc;
3402fe1339eSKonstantin Belousov
3412fe1339eSKonstantin Belousov sc = device_get_softc(dev);
3422fe1339eSKonstantin Belousov return (sc->acpi_uid);
3432fe1339eSKonstantin Belousov }
3442fe1339eSKonstantin Belousov
3453c4c08dcSAlexander Motin static ACPI_STATUS
hpet_find(ACPI_HANDLE handle,UINT32 level,void * context,void ** status)346875b8844SAlexander Motin hpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
3473c4c08dcSAlexander Motin void **status)
3483c4c08dcSAlexander Motin {
3493c4c08dcSAlexander Motin char **ids;
3503c4c08dcSAlexander Motin uint32_t id = (uint32_t)(uintptr_t)context;
351b169c85eSAlexander Motin uint32_t uid = 0;
3523c4c08dcSAlexander Motin
3533c4c08dcSAlexander Motin for (ids = hpet_ids; *ids != NULL; ids++) {
3543c4c08dcSAlexander Motin if (acpi_MatchHid(handle, *ids))
3553c4c08dcSAlexander Motin break;
3563c4c08dcSAlexander Motin }
3573c4c08dcSAlexander Motin if (*ids == NULL)
3583c4c08dcSAlexander Motin return (AE_OK);
359875b8844SAlexander Motin if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)) ||
360875b8844SAlexander Motin id == uid)
361d46bdcabSAlexander Motin *status = acpi_get_device(handle);
3623c4c08dcSAlexander Motin return (AE_OK);
3633c4c08dcSAlexander Motin }
3643c4c08dcSAlexander Motin
3654a588c1bSJohn Baldwin /*
3664a588c1bSJohn Baldwin * Find an existing IRQ resource that matches the requested IRQ range
3674a588c1bSJohn Baldwin * and return its RID. If one is not found, use a new RID.
3684a588c1bSJohn Baldwin */
3694a588c1bSJohn Baldwin static int
hpet_find_irq_rid(device_t dev,u_long start,u_long end)3704a588c1bSJohn Baldwin hpet_find_irq_rid(device_t dev, u_long start, u_long end)
3714a588c1bSJohn Baldwin {
3722dd1bdf1SJustin Hibbits rman_res_t irq;
3734a588c1bSJohn Baldwin int error, rid;
3744a588c1bSJohn Baldwin
3754a588c1bSJohn Baldwin for (rid = 0;; rid++) {
3764a588c1bSJohn Baldwin error = bus_get_resource(dev, SYS_RES_IRQ, rid, &irq, NULL);
3774a588c1bSJohn Baldwin if (error != 0 || (start <= irq && irq <= end))
3784a588c1bSJohn Baldwin return (rid);
3794a588c1bSJohn Baldwin }
3804a588c1bSJohn Baldwin }
3814a588c1bSJohn Baldwin
3823149cc9dSRui Paulo static int
hpet_open(struct cdev * cdev,int oflags,int devtype,struct thread * td)3833149cc9dSRui Paulo hpet_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
3843149cc9dSRui Paulo {
3853149cc9dSRui Paulo struct hpet_softc *sc;
3863149cc9dSRui Paulo
3873149cc9dSRui Paulo sc = cdev->si_drv1;
3883149cc9dSRui Paulo if (!sc->mmap_allow)
3893149cc9dSRui Paulo return (EPERM);
3903149cc9dSRui Paulo else
3913149cc9dSRui Paulo return (0);
3923149cc9dSRui Paulo }
3933149cc9dSRui Paulo
3943149cc9dSRui Paulo static int
hpet_mmap(struct cdev * cdev,vm_ooffset_t offset,vm_paddr_t * paddr,int nprot,vm_memattr_t * memattr)3953149cc9dSRui Paulo hpet_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
3963149cc9dSRui Paulo int nprot, vm_memattr_t *memattr)
3973149cc9dSRui Paulo {
3983149cc9dSRui Paulo struct hpet_softc *sc;
3993149cc9dSRui Paulo
4003149cc9dSRui Paulo sc = cdev->si_drv1;
40183fb1d62SKonstantin Belousov if (offset >= rman_get_size(sc->mem_res))
4023149cc9dSRui Paulo return (EINVAL);
4033149cc9dSRui Paulo if (!sc->mmap_allow_write && (nprot & PROT_WRITE))
4043149cc9dSRui Paulo return (EPERM);
4053149cc9dSRui Paulo *paddr = rman_get_start(sc->mem_res) + offset;
4063f6732e8SKonstantin Belousov *memattr = VM_MEMATTR_UNCACHEABLE;
4073149cc9dSRui Paulo
4083149cc9dSRui Paulo return (0);
4093149cc9dSRui Paulo }
4103149cc9dSRui Paulo
411f1d16a11SNate Lawson /* Discover the HPET via the ACPI table of the same name. */
412f74e3c98SNate Lawson static void
hpet_identify(driver_t * driver,device_t parent)413875b8844SAlexander Motin hpet_identify(driver_t *driver, device_t parent)
414fffe371dSTakanori Watanabe {
415fffe371dSTakanori Watanabe ACPI_TABLE_HPET *hpet;
416fffe371dSTakanori Watanabe ACPI_STATUS status;
417fffe371dSTakanori Watanabe device_t child;
418d46bdcabSAlexander Motin int i;
419fffe371dSTakanori Watanabe
420f74e3c98SNate Lawson /* Only one HPET device can be added. */
42133883cdcSJohn Baldwin if (devclass_get_device(devclass_find("hpet"), 0))
422f74e3c98SNate Lawson return;
4233c4c08dcSAlexander Motin for (i = 1; ; i++) {
4243c4c08dcSAlexander Motin /* Search for HPET table. */
4253c4c08dcSAlexander Motin status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
426fffe371dSTakanori Watanabe if (ACPI_FAILURE(status))
427fffe371dSTakanori Watanabe return;
4283c4c08dcSAlexander Motin /* Search for HPET device with same ID. */
429d46bdcabSAlexander Motin child = NULL;
4303c4c08dcSAlexander Motin AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
431d46bdcabSAlexander Motin 100, hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence,
432d46bdcabSAlexander Motin (void *)&child);
4333c4c08dcSAlexander Motin /* If found - let it be probed in normal way. */
434d46bdcabSAlexander Motin if (child) {
435d46bdcabSAlexander Motin if (bus_get_resource(child, SYS_RES_MEMORY, 0,
436d46bdcabSAlexander Motin NULL, NULL) != 0)
437d46bdcabSAlexander Motin bus_set_resource(child, SYS_RES_MEMORY, 0,
438d46bdcabSAlexander Motin hpet->Address.Address, HPET_MEM_WIDTH);
4393c4c08dcSAlexander Motin continue;
440d46bdcabSAlexander Motin }
4413c4c08dcSAlexander Motin /* If not - create it from table info. */
442404b0d10SJung-uk Kim child = BUS_ADD_CHILD(parent, 2, "hpet", 0);
443fffe371dSTakanori Watanabe if (child == NULL) {
444fffe371dSTakanori Watanabe printf("%s: can't add child\n", __func__);
4453c4c08dcSAlexander Motin continue;
446fffe371dSTakanori Watanabe }
447f1d16a11SNate Lawson bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
448f1d16a11SNate Lawson HPET_MEM_WIDTH);
449fffe371dSTakanori Watanabe }
4503c4c08dcSAlexander Motin }
451fffe371dSTakanori Watanabe
4524fa7241fSPoul-Henning Kamp static int
hpet_probe(device_t dev)453875b8844SAlexander Motin hpet_probe(device_t dev)
4544fa7241fSPoul-Henning Kamp {
4555efca36fSTakanori Watanabe int rv;
456c73930f3SNate Lawson
4575efca36fSTakanori Watanabe ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
458c2641d23SRoger Pau Monné if (acpi_disabled("hpet") || acpi_hpet_disabled)
459f1d16a11SNate Lawson return (ENXIO);
460804838e1SAndriy Gapon if (acpi_get_handle(dev) != NULL)
4615efca36fSTakanori Watanabe rv = ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids, NULL);
462804838e1SAndriy Gapon else
463804838e1SAndriy Gapon rv = 0;
4645efca36fSTakanori Watanabe if (rv <= 0)
465c73930f3SNate Lawson device_set_desc(dev, "High Precision Event Timer");
4665efca36fSTakanori Watanabe return (rv);
4674fa7241fSPoul-Henning Kamp }
4684fa7241fSPoul-Henning Kamp
4694fa7241fSPoul-Henning Kamp static int
hpet_attach(device_t dev)470875b8844SAlexander Motin hpet_attach(device_t dev)
4714fa7241fSPoul-Henning Kamp {
472875b8844SAlexander Motin struct hpet_softc *sc;
473875b8844SAlexander Motin struct hpet_timer *t;
47494a4ee3bSKonstantin Belousov struct make_dev_args mda;
475875b8844SAlexander Motin int i, j, num_msi, num_timers, num_percpu_et, num_percpu_t, cur_cpu;
47694a4ee3bSKonstantin Belousov int pcpu_master, error;
477964bf2f9SJohn F. Carr rman_res_t hpet_region_size;
478875b8844SAlexander Motin static int maxhpetet = 0;
47909538b10SAlexander Motin uint32_t val, val2, cvectors, dvectors;
480875b8844SAlexander Motin uint16_t vendor, rev;
4814fa7241fSPoul-Henning Kamp
4824fa7241fSPoul-Henning Kamp ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
4834fa7241fSPoul-Henning Kamp
4844fa7241fSPoul-Henning Kamp sc = device_get_softc(dev);
4854fa7241fSPoul-Henning Kamp sc->dev = dev;
4864fa7241fSPoul-Henning Kamp sc->handle = acpi_get_handle(dev);
4874fa7241fSPoul-Henning Kamp
488875b8844SAlexander Motin sc->mem_rid = 0;
489875b8844SAlexander Motin sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
490c73930f3SNate Lawson RF_ACTIVE);
491c73930f3SNate Lawson if (sc->mem_res == NULL)
492c73930f3SNate Lawson return (ENOMEM);
4934fa7241fSPoul-Henning Kamp
494964bf2f9SJohn F. Carr hpet_region_size = rman_get_size(sc->mem_res);
495964bf2f9SJohn F. Carr /* Validate that the region is big enough for the control registers. */
496964bf2f9SJohn F. Carr if (hpet_region_size < HPET_MEM_MIN_WIDTH) {
497da1b038aSJustin Hibbits device_printf(dev, "memory region width %jd too small\n",
498964bf2f9SJohn F. Carr hpet_region_size);
499c73930f3SNate Lawson bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
500c73930f3SNate Lawson return (ENXIO);
5014fa7241fSPoul-Henning Kamp }
5024fa7241fSPoul-Henning Kamp
5039bbad5afSNate Lawson /* Be sure timer is enabled. */
504572f347dSJohn Baldwin hpet_enable(sc);
5059bbad5afSNate Lawson
506c73930f3SNate Lawson /* Read basic statistics about the timer. */
507f831d6e0SJohn Baldwin val = bus_read_4(sc->mem_res, HPET_PERIOD);
508572f347dSJohn Baldwin if (val == 0) {
509572f347dSJohn Baldwin device_printf(dev, "invalid period\n");
510572f347dSJohn Baldwin hpet_disable(sc);
511572f347dSJohn Baldwin bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
512572f347dSJohn Baldwin return (ENXIO);
513572f347dSJohn Baldwin }
514572f347dSJohn Baldwin
515875b8844SAlexander Motin sc->freq = (1000000000000000LL + val / 2) / val;
51649ed68bbSAlexander Motin sc->caps = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
51749ed68bbSAlexander Motin vendor = (sc->caps & HPET_CAP_VENDOR_ID) >> 16;
51849ed68bbSAlexander Motin rev = sc->caps & HPET_CAP_REV_ID;
51949ed68bbSAlexander Motin num_timers = 1 + ((sc->caps & HPET_CAP_NUM_TIM) >> 8);
5209a6a6ecbSAndriy Gapon /*
5219a6a6ecbSAndriy Gapon * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
5229a6a6ecbSAndriy Gapon * Specification and provides an off by one number
5239a6a6ecbSAndriy Gapon * of timers/comparators.
5249a6a6ecbSAndriy Gapon * Additionally, they use unregistered value in VENDOR_ID field.
5259a6a6ecbSAndriy Gapon */
526875b8844SAlexander Motin if (vendor == HPET_VENDID_AMD && rev < 0x10 && num_timers > 0)
5279a6a6ecbSAndriy Gapon num_timers--;
528964bf2f9SJohn F. Carr /*
529964bf2f9SJohn F. Carr * Now validate that the region is big enough to address all counters.
530964bf2f9SJohn F. Carr */
531964bf2f9SJohn F. Carr if (hpet_region_size < HPET_TIMER_CAP_CNF(num_timers)) {
532964bf2f9SJohn F. Carr device_printf(dev,
533964bf2f9SJohn F. Carr "memory region width %jd too small for %d timers\n",
534964bf2f9SJohn F. Carr hpet_region_size, num_timers);
535964bf2f9SJohn F. Carr hpet_disable(sc);
536964bf2f9SJohn F. Carr bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
537964bf2f9SJohn F. Carr return (ENXIO);
538964bf2f9SJohn F. Carr }
539964bf2f9SJohn F. Carr
540875b8844SAlexander Motin sc->num_timers = num_timers;
541875b8844SAlexander Motin if (bootverbose) {
542c73930f3SNate Lawson device_printf(dev,
543875b8844SAlexander Motin "vendor 0x%x, rev 0x%x, %jdHz%s, %d timers,%s\n",
54449ed68bbSAlexander Motin vendor, rev, sc->freq,
54549ed68bbSAlexander Motin (sc->caps & HPET_CAP_COUNT_SIZE) ? " 64bit" : "",
54649ed68bbSAlexander Motin num_timers,
54749ed68bbSAlexander Motin (sc->caps & HPET_CAP_LEG_RT) ? " legacy route" : "");
548c73930f3SNate Lawson }
549875b8844SAlexander Motin for (i = 0; i < num_timers; i++) {
550875b8844SAlexander Motin t = &sc->t[i];
551875b8844SAlexander Motin t->sc = sc;
552875b8844SAlexander Motin t->num = i;
5537bf91e9aSAndriy Gapon t->mode = TIMER_STOPPED;
554875b8844SAlexander Motin t->intr_rid = -1;
555875b8844SAlexander Motin t->irq = -1;
556b28fc1b5SAlexander Motin t->pcpu_cpu = -1;
557b28fc1b5SAlexander Motin t->pcpu_misrouted = 0;
558875b8844SAlexander Motin t->pcpu_master = -1;
559875b8844SAlexander Motin t->caps = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i));
560875b8844SAlexander Motin t->vectors = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i) + 4);
561875b8844SAlexander Motin if (bootverbose) {
562875b8844SAlexander Motin device_printf(dev,
563875b8844SAlexander Motin " t%d: irqs 0x%08x (%d)%s%s%s\n", i,
564875b8844SAlexander Motin t->vectors, (t->caps & HPET_TCNF_INT_ROUTE) >> 9,
565875b8844SAlexander Motin (t->caps & HPET_TCAP_FSB_INT_DEL) ? ", MSI" : "",
566875b8844SAlexander Motin (t->caps & HPET_TCAP_SIZE) ? ", 64bit" : "",
567875b8844SAlexander Motin (t->caps & HPET_TCAP_PER_INT) ? ", periodic" : "");
568875b8844SAlexander Motin }
569875b8844SAlexander Motin }
570c73930f3SNate Lawson if (testenv("debug.acpi.hpet_test"))
571875b8844SAlexander Motin hpet_test(sc);
5729bbad5afSNate Lawson /*
5739bbad5afSNate Lawson * Don't attach if the timer never increments. Since the spec
5749bbad5afSNate Lawson * requires it to be at least 10 MHz, it has to change in 1 us.
5759bbad5afSNate Lawson */
576f831d6e0SJohn Baldwin val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
5779bbad5afSNate Lawson DELAY(1);
578f831d6e0SJohn Baldwin val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
5799bbad5afSNate Lawson if (val == val2) {
5809bbad5afSNate Lawson device_printf(dev, "HPET never increments, disabling\n");
581572f347dSJohn Baldwin hpet_disable(sc);
5829bbad5afSNate Lawson bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
5839bbad5afSNate Lawson return (ENXIO);
5849bbad5afSNate Lawson }
5853c4c08dcSAlexander Motin /* Announce first HPET as timecounter. */
5863c4c08dcSAlexander Motin if (device_get_unit(dev) == 0) {
587875b8844SAlexander Motin sc->tc.tc_get_timecount = hpet_get_timecount,
588875b8844SAlexander Motin sc->tc.tc_counter_mask = ~0u,
589875b8844SAlexander Motin sc->tc.tc_name = "HPET",
590ca5f1efdSJung-uk Kim sc->tc.tc_quality = 950,
591875b8844SAlexander Motin sc->tc.tc_frequency = sc->freq;
592875b8844SAlexander Motin sc->tc.tc_priv = sc;
59316808549SKonstantin Belousov sc->tc.tc_fill_vdso_timehands = hpet_vdso_timehands;
59416808549SKonstantin Belousov #ifdef COMPAT_FREEBSD32
59516808549SKonstantin Belousov sc->tc.tc_fill_vdso_timehands32 = hpet_vdso_timehands32;
59616808549SKonstantin Belousov #endif
597875b8844SAlexander Motin tc_init(&sc->tc);
598875b8844SAlexander Motin }
599875b8844SAlexander Motin /* If not disabled - setup and announce event timers. */
600875b8844SAlexander Motin if (resource_int_value(device_get_name(dev), device_get_unit(dev),
601875b8844SAlexander Motin "clock", &i) == 0 && i == 0)
602875b8844SAlexander Motin return (0);
60349ed68bbSAlexander Motin
60449ed68bbSAlexander Motin /* Check whether we can and want legacy routing. */
60549ed68bbSAlexander Motin sc->legacy_route = 0;
60649ed68bbSAlexander Motin resource_int_value(device_get_name(dev), device_get_unit(dev),
60749ed68bbSAlexander Motin "legacy_route", &sc->legacy_route);
60849ed68bbSAlexander Motin if ((sc->caps & HPET_CAP_LEG_RT) == 0)
60949ed68bbSAlexander Motin sc->legacy_route = 0;
61049ed68bbSAlexander Motin if (sc->legacy_route) {
61149ed68bbSAlexander Motin sc->t[0].vectors = 0;
61249ed68bbSAlexander Motin sc->t[1].vectors = 0;
61349ed68bbSAlexander Motin }
61449ed68bbSAlexander Motin
61509538b10SAlexander Motin /* Check what IRQs we want use. */
61609538b10SAlexander Motin /* By default allow any PCI IRQs. */
61709538b10SAlexander Motin sc->allowed_irqs = 0xffff0000;
618875b8844SAlexander Motin /*
619875b8844SAlexander Motin * HPETs in AMD chipsets before SB800 have problems with IRQs >= 16
620875b8844SAlexander Motin * Lower are also not always working for different reasons.
621875b8844SAlexander Motin * SB800 fixed it, but seems do not implements level triggering
622875b8844SAlexander Motin * properly, that makes it very unreliable - it freezes after any
623875b8844SAlexander Motin * interrupt loss. Avoid legacy IRQs for AMD.
624875b8844SAlexander Motin */
625fc913424SKonstantin Belousov if (vendor == HPET_VENDID_AMD || vendor == HPET_VENDID_AMD2 ||
626fc913424SKonstantin Belousov vendor == HPET_VENDID_HYGON)
62709538b10SAlexander Motin sc->allowed_irqs = 0x00000000;
62809538b10SAlexander Motin /*
6293a2c9a26SAlexander Motin * NVidia MCP5x chipsets have number of unexplained interrupt
6303a2c9a26SAlexander Motin * problems. For some reason, using HPET interrupts breaks HDA sound.
6313a2c9a26SAlexander Motin */
6323a2c9a26SAlexander Motin if (vendor == HPET_VENDID_NVIDIA && rev <= 0x01)
6333a2c9a26SAlexander Motin sc->allowed_irqs = 0x00000000;
6343a2c9a26SAlexander Motin /*
635fd94de5cSAlexander Motin * ServerWorks HT1000 reported to have problems with IRQs >= 16.
636fd94de5cSAlexander Motin * Lower IRQs are working, but allowed mask is not set correctly.
637fd94de5cSAlexander Motin * Legacy_route mode works fine.
638fd94de5cSAlexander Motin */
639fd94de5cSAlexander Motin if (vendor == HPET_VENDID_SW && rev <= 0x01)
640fd94de5cSAlexander Motin sc->allowed_irqs = 0x00000000;
641fd94de5cSAlexander Motin /*
64209538b10SAlexander Motin * Neither QEMU nor VirtualBox report supported IRQs correctly.
64309538b10SAlexander Motin * The only way to use HPET there is to specify IRQs manually
644d2014f51SJohn Baldwin * and/or use legacy_route. Legacy_route mode works on both.
64509538b10SAlexander Motin */
646*579cb41bSZhenlei Huang if (vm_guest != VM_GUEST_NO)
64709538b10SAlexander Motin sc->allowed_irqs = 0x00000000;
64809538b10SAlexander Motin /* Let user override. */
64909538b10SAlexander Motin resource_int_value(device_get_name(dev), device_get_unit(dev),
65009538b10SAlexander Motin "allowed_irqs", &sc->allowed_irqs);
65109538b10SAlexander Motin
652373d257eSAlexander Motin /* Get how much per-CPU timers we should try to provide. */
653373d257eSAlexander Motin sc->per_cpu = 1;
654373d257eSAlexander Motin resource_int_value(device_get_name(dev), device_get_unit(dev),
655373d257eSAlexander Motin "per_cpu", &sc->per_cpu);
656373d257eSAlexander Motin
65709538b10SAlexander Motin num_msi = 0;
65809538b10SAlexander Motin sc->useirq = 0;
65909538b10SAlexander Motin /* Find IRQ vectors for all timers. */
66009538b10SAlexander Motin cvectors = sc->allowed_irqs & 0xffff0000;
66109538b10SAlexander Motin dvectors = sc->allowed_irqs & 0x0000ffff;
66209538b10SAlexander Motin if (sc->legacy_route)
66309538b10SAlexander Motin dvectors &= 0x0000fefe;
664875b8844SAlexander Motin for (i = 0; i < num_timers; i++) {
665875b8844SAlexander Motin t = &sc->t[i];
66649ed68bbSAlexander Motin if (sc->legacy_route && i < 2)
66749ed68bbSAlexander Motin t->irq = (i == 0) ? 0 : 8;
668875b8844SAlexander Motin #ifdef DEV_APIC
66949ed68bbSAlexander Motin else if (t->caps & HPET_TCAP_FSB_INT_DEL) {
670875b8844SAlexander Motin if ((j = PCIB_ALLOC_MSIX(
671875b8844SAlexander Motin device_get_parent(device_get_parent(dev)), dev,
672875b8844SAlexander Motin &t->irq))) {
673875b8844SAlexander Motin device_printf(dev,
6743c6f0322SNeel Natu "Can't allocate interrupt for t%d: %d\n",
6753c6f0322SNeel Natu i, j);
67649ed68bbSAlexander Motin }
67749ed68bbSAlexander Motin }
67849ed68bbSAlexander Motin #endif
67909538b10SAlexander Motin else if (dvectors & t->vectors) {
68009538b10SAlexander Motin t->irq = ffs(dvectors & t->vectors) - 1;
68109538b10SAlexander Motin dvectors &= ~(1 << t->irq);
68209538b10SAlexander Motin }
68349ed68bbSAlexander Motin if (t->irq >= 0) {
6844a588c1bSJohn Baldwin t->intr_rid = hpet_find_irq_rid(dev, t->irq, t->irq);
685686b1e6bSJohn Baldwin t->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
686686b1e6bSJohn Baldwin &t->intr_rid, t->irq, t->irq, 1, RF_ACTIVE);
687686b1e6bSJohn Baldwin if (t->intr_res == NULL) {
68849ed68bbSAlexander Motin t->irq = -1;
68949ed68bbSAlexander Motin device_printf(dev,
69049ed68bbSAlexander Motin "Can't map interrupt for t%d.\n", i);
691686b1e6bSJohn Baldwin } else if (bus_setup_intr(dev, t->intr_res,
692686b1e6bSJohn Baldwin INTR_TYPE_CLK, hpet_intr_single, NULL, t,
693686b1e6bSJohn Baldwin &t->intr_handle) != 0) {
69449ed68bbSAlexander Motin t->irq = -1;
69549ed68bbSAlexander Motin device_printf(dev,
69649ed68bbSAlexander Motin "Can't setup interrupt for t%d.\n", i);
697875b8844SAlexander Motin } else {
698875b8844SAlexander Motin bus_describe_intr(dev, t->intr_res,
699875b8844SAlexander Motin t->intr_handle, "t%d", i);
700875b8844SAlexander Motin num_msi++;
70149ed68bbSAlexander Motin }
70249ed68bbSAlexander Motin }
70349ed68bbSAlexander Motin if (t->irq < 0 && (cvectors & t->vectors) != 0) {
70449ed68bbSAlexander Motin cvectors &= t->vectors;
70549ed68bbSAlexander Motin sc->useirq |= (1 << i);
70649ed68bbSAlexander Motin }
70749ed68bbSAlexander Motin }
70849ed68bbSAlexander Motin if (sc->legacy_route && sc->t[0].irq < 0 && sc->t[1].irq < 0)
70949ed68bbSAlexander Motin sc->legacy_route = 0;
71049ed68bbSAlexander Motin if (sc->legacy_route)
71149ed68bbSAlexander Motin hpet_enable(sc);
71249ed68bbSAlexander Motin /* Group timers for per-CPU operation. */
713373d257eSAlexander Motin num_percpu_et = min(num_msi / mp_ncpus, sc->per_cpu);
71449ed68bbSAlexander Motin num_percpu_t = num_percpu_et * mp_ncpus;
71549ed68bbSAlexander Motin pcpu_master = 0;
71649ed68bbSAlexander Motin cur_cpu = CPU_FIRST();
71749ed68bbSAlexander Motin for (i = 0; i < num_timers; i++) {
71849ed68bbSAlexander Motin t = &sc->t[i];
71949ed68bbSAlexander Motin if (t->irq >= 0 && num_percpu_t > 0) {
720875b8844SAlexander Motin if (cur_cpu == CPU_FIRST())
721875b8844SAlexander Motin pcpu_master = i;
722b28fc1b5SAlexander Motin t->pcpu_cpu = cur_cpu;
723875b8844SAlexander Motin t->pcpu_master = pcpu_master;
724875b8844SAlexander Motin sc->t[pcpu_master].
725875b8844SAlexander Motin pcpu_slaves[cur_cpu] = i;
726875b8844SAlexander Motin bus_bind_intr(dev, t->intr_res, cur_cpu);
727875b8844SAlexander Motin cur_cpu = CPU_NEXT(cur_cpu);
728875b8844SAlexander Motin num_percpu_t--;
72909538b10SAlexander Motin } else if (t->irq >= 0)
73009538b10SAlexander Motin bus_bind_intr(dev, t->intr_res, CPU_FIRST());
731875b8844SAlexander Motin }
732875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_ISR, 0xffffffff);
733875b8844SAlexander Motin sc->irq = -1;
734d2014f51SJohn Baldwin /* If at least one timer needs legacy IRQ - set it up. */
735875b8844SAlexander Motin if (sc->useirq) {
736875b8844SAlexander Motin j = i = fls(cvectors) - 1;
737875b8844SAlexander Motin while (j > 0 && (cvectors & (1 << (j - 1))) != 0)
738875b8844SAlexander Motin j--;
7394a588c1bSJohn Baldwin sc->intr_rid = hpet_find_irq_rid(dev, j, i);
740686b1e6bSJohn Baldwin sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
741686b1e6bSJohn Baldwin &sc->intr_rid, j, i, 1, RF_SHAREABLE | RF_ACTIVE);
742686b1e6bSJohn Baldwin if (sc->intr_res == NULL)
743875b8844SAlexander Motin device_printf(dev, "Can't map interrupt.\n");
744686b1e6bSJohn Baldwin else if (bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK,
745686b1e6bSJohn Baldwin hpet_intr, NULL, sc, &sc->intr_handle) != 0) {
746875b8844SAlexander Motin device_printf(dev, "Can't setup interrupt.\n");
747875b8844SAlexander Motin } else {
748875b8844SAlexander Motin sc->irq = rman_get_start(sc->intr_res);
749875b8844SAlexander Motin /* Bind IRQ to BSP to avoid live migration. */
750875b8844SAlexander Motin bus_bind_intr(dev, sc->intr_res, CPU_FIRST());
751875b8844SAlexander Motin }
752875b8844SAlexander Motin }
753875b8844SAlexander Motin /* Program and announce event timers. */
754875b8844SAlexander Motin for (i = 0; i < num_timers; i++) {
755875b8844SAlexander Motin t = &sc->t[i];
756875b8844SAlexander Motin t->caps &= ~(HPET_TCNF_FSB_EN | HPET_TCNF_INT_ROUTE);
757875b8844SAlexander Motin t->caps &= ~(HPET_TCNF_VAL_SET | HPET_TCNF_INT_ENB);
75849ed68bbSAlexander Motin t->caps &= ~(HPET_TCNF_INT_TYPE);
759875b8844SAlexander Motin t->caps |= HPET_TCNF_32MODE;
76049ed68bbSAlexander Motin if (t->irq >= 0 && sc->legacy_route && i < 2) {
76149ed68bbSAlexander Motin /* Legacy route doesn't need more configuration. */
76249ed68bbSAlexander Motin } else
763875b8844SAlexander Motin #ifdef DEV_APIC
76409538b10SAlexander Motin if ((t->caps & HPET_TCAP_FSB_INT_DEL) && t->irq >= 0) {
765875b8844SAlexander Motin uint64_t addr;
766875b8844SAlexander Motin uint32_t data;
767875b8844SAlexander Motin
768875b8844SAlexander Motin if (PCIB_MAP_MSI(
769875b8844SAlexander Motin device_get_parent(device_get_parent(dev)), dev,
770875b8844SAlexander Motin t->irq, &addr, &data) == 0) {
771875b8844SAlexander Motin bus_write_4(sc->mem_res,
772875b8844SAlexander Motin HPET_TIMER_FSB_ADDR(i), addr);
773875b8844SAlexander Motin bus_write_4(sc->mem_res,
774875b8844SAlexander Motin HPET_TIMER_FSB_VAL(i), data);
775875b8844SAlexander Motin t->caps |= HPET_TCNF_FSB_EN;
776875b8844SAlexander Motin } else
777875b8844SAlexander Motin t->irq = -2;
778875b8844SAlexander Motin } else
779875b8844SAlexander Motin #endif
78009538b10SAlexander Motin if (t->irq >= 0)
78109538b10SAlexander Motin t->caps |= (t->irq << 9);
78209538b10SAlexander Motin else if (sc->irq >= 0 && (t->vectors & (1 << sc->irq)))
783875b8844SAlexander Motin t->caps |= (sc->irq << 9) | HPET_TCNF_INT_TYPE;
784875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps);
785875b8844SAlexander Motin /* Skip event timers without set up IRQ. */
786875b8844SAlexander Motin if (t->irq < 0 &&
787875b8844SAlexander Motin (sc->irq < 0 || (t->vectors & (1 << sc->irq)) == 0))
788875b8844SAlexander Motin continue;
789875b8844SAlexander Motin /* Announce the reset. */
790875b8844SAlexander Motin if (maxhpetet == 0)
791875b8844SAlexander Motin t->et.et_name = "HPET";
792875b8844SAlexander Motin else {
793875b8844SAlexander Motin sprintf(t->name, "HPET%d", maxhpetet);
794875b8844SAlexander Motin t->et.et_name = t->name;
795875b8844SAlexander Motin }
796875b8844SAlexander Motin t->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
797875b8844SAlexander Motin t->et.et_quality = 450;
798875b8844SAlexander Motin if (t->pcpu_master >= 0) {
799875b8844SAlexander Motin t->et.et_flags |= ET_FLAGS_PERCPU;
800875b8844SAlexander Motin t->et.et_quality += 100;
8015fd81be1SAlexander Motin } else if (mp_ncpus >= 8)
80298992b29SAlexander Motin t->et.et_quality -= 100;
803875b8844SAlexander Motin if ((t->caps & HPET_TCAP_PER_INT) == 0)
804875b8844SAlexander Motin t->et.et_quality -= 10;
805875b8844SAlexander Motin t->et.et_frequency = sc->freq;
806fdc5dd2dSAlexander Motin t->et.et_min_period =
807fdc5dd2dSAlexander Motin ((uint64_t)(HPET_MIN_CYCLES * 2) << 32) / sc->freq;
808fdc5dd2dSAlexander Motin t->et.et_max_period = (0xfffffffeLLU << 32) / sc->freq;
809875b8844SAlexander Motin t->et.et_start = hpet_start;
810875b8844SAlexander Motin t->et.et_stop = hpet_stop;
811875b8844SAlexander Motin t->et.et_priv = &sc->t[i];
812875b8844SAlexander Motin if (t->pcpu_master < 0 || t->pcpu_master == i) {
813875b8844SAlexander Motin et_register(&t->et);
814875b8844SAlexander Motin maxhpetet++;
815875b8844SAlexander Motin }
8163c4c08dcSAlexander Motin }
8172fe1339eSKonstantin Belousov acpi_GetInteger(sc->handle, "_UID", &sc->acpi_uid);
8183149cc9dSRui Paulo
81994a4ee3bSKonstantin Belousov make_dev_args_init(&mda);
82094a4ee3bSKonstantin Belousov mda.mda_devsw = &hpet_cdevsw;
82194a4ee3bSKonstantin Belousov mda.mda_uid = UID_ROOT;
82294a4ee3bSKonstantin Belousov mda.mda_gid = GID_WHEEL;
82351c762d1SKonstantin Belousov mda.mda_mode = 0644;
82494a4ee3bSKonstantin Belousov mda.mda_si_drv1 = sc;
82594a4ee3bSKonstantin Belousov error = make_dev_s(&mda, &sc->pdev, "hpet%d", device_get_unit(dev));
82694a4ee3bSKonstantin Belousov if (error == 0) {
8273149cc9dSRui Paulo sc->mmap_allow = 1;
8283149cc9dSRui Paulo TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow",
8293149cc9dSRui Paulo &sc->mmap_allow);
83051c762d1SKonstantin Belousov sc->mmap_allow_write = 0;
8313149cc9dSRui Paulo TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow_write",
8323149cc9dSRui Paulo &sc->mmap_allow_write);
8333149cc9dSRui Paulo SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
8343149cc9dSRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
8353149cc9dSRui Paulo OID_AUTO, "mmap_allow",
8363149cc9dSRui Paulo CTLFLAG_RW, &sc->mmap_allow, 0,
8373149cc9dSRui Paulo "Allow userland to memory map HPET");
838f5338994SRui Paulo SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
839f5338994SRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
840f5338994SRui Paulo OID_AUTO, "mmap_allow_write",
841f5338994SRui Paulo CTLFLAG_RW, &sc->mmap_allow_write, 0,
842f5338994SRui Paulo "Allow userland write to the HPET register space");
84394a4ee3bSKonstantin Belousov } else {
84494a4ee3bSKonstantin Belousov device_printf(dev, "could not create /dev/hpet%d, error %d\n",
84594a4ee3bSKonstantin Belousov device_get_unit(dev), error);
84694a4ee3bSKonstantin Belousov }
8473149cc9dSRui Paulo
8484fa7241fSPoul-Henning Kamp return (0);
8494fa7241fSPoul-Henning Kamp }
8504fa7241fSPoul-Henning Kamp
8514fa7241fSPoul-Henning Kamp static int
hpet_detach(device_t dev)852875b8844SAlexander Motin hpet_detach(device_t dev)
8534fa7241fSPoul-Henning Kamp {
8544fa7241fSPoul-Henning Kamp ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
8554fa7241fSPoul-Henning Kamp
856c73930f3SNate Lawson /* XXX Without a tc_remove() function, we can't detach. */
8574fa7241fSPoul-Henning Kamp return (EBUSY);
858c73930f3SNate Lawson }
8594fa7241fSPoul-Henning Kamp
8605394d87eSNate Lawson static int
hpet_suspend(device_t dev)861875b8844SAlexander Motin hpet_suspend(device_t dev)
862572f347dSJohn Baldwin {
863a157e425SAlexander Motin // struct hpet_softc *sc;
864572f347dSJohn Baldwin
865572f347dSJohn Baldwin /*
866572f347dSJohn Baldwin * Disable the timer during suspend. The timer will not lose
867572f347dSJohn Baldwin * its state in S1 or S2, but we are required to disable
868572f347dSJohn Baldwin * it.
869572f347dSJohn Baldwin */
870a157e425SAlexander Motin // sc = device_get_softc(dev);
871a157e425SAlexander Motin // hpet_disable(sc);
872572f347dSJohn Baldwin
873572f347dSJohn Baldwin return (0);
874572f347dSJohn Baldwin }
875572f347dSJohn Baldwin
876572f347dSJohn Baldwin static int
hpet_resume(device_t dev)877875b8844SAlexander Motin hpet_resume(device_t dev)
8785394d87eSNate Lawson {
879875b8844SAlexander Motin struct hpet_softc *sc;
880875b8844SAlexander Motin struct hpet_timer *t;
881875b8844SAlexander Motin int i;
8825394d87eSNate Lawson
8835394d87eSNate Lawson /* Re-enable the timer after a resume to keep the clock advancing. */
8845394d87eSNate Lawson sc = device_get_softc(dev);
885572f347dSJohn Baldwin hpet_enable(sc);
886875b8844SAlexander Motin /* Restart event timers that were running on suspend. */
887875b8844SAlexander Motin for (i = 0; i < sc->num_timers; i++) {
888875b8844SAlexander Motin t = &sc->t[i];
889875b8844SAlexander Motin #ifdef DEV_APIC
89049ed68bbSAlexander Motin if (t->irq >= 0 && (sc->legacy_route == 0 || i >= 2)) {
891875b8844SAlexander Motin uint64_t addr;
892875b8844SAlexander Motin uint32_t data;
8935394d87eSNate Lawson
894875b8844SAlexander Motin if (PCIB_MAP_MSI(
895875b8844SAlexander Motin device_get_parent(device_get_parent(dev)), dev,
896875b8844SAlexander Motin t->irq, &addr, &data) == 0) {
897875b8844SAlexander Motin bus_write_4(sc->mem_res,
898875b8844SAlexander Motin HPET_TIMER_FSB_ADDR(i), addr);
899875b8844SAlexander Motin bus_write_4(sc->mem_res,
900875b8844SAlexander Motin HPET_TIMER_FSB_VAL(i), data);
901875b8844SAlexander Motin }
902875b8844SAlexander Motin }
903875b8844SAlexander Motin #endif
9047bf91e9aSAndriy Gapon if (t->mode == TIMER_STOPPED)
905875b8844SAlexander Motin continue;
9066184f8d6SAlexander Motin t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
9077bf91e9aSAndriy Gapon if (t->mode == TIMER_PERIODIC &&
9087bf91e9aSAndriy Gapon (t->caps & HPET_TCAP_PER_INT) != 0) {
909875b8844SAlexander Motin t->caps |= HPET_TCNF_TYPE;
9106184f8d6SAlexander Motin t->next += t->div;
911875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
912875b8844SAlexander Motin t->caps | HPET_TCNF_VAL_SET);
913875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
9146184f8d6SAlexander Motin t->next);
915875b8844SAlexander Motin bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
916875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
917875b8844SAlexander Motin t->div);
918875b8844SAlexander Motin } else {
9196184f8d6SAlexander Motin t->next += sc->freq / 1024;
920875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
9216184f8d6SAlexander Motin t->next);
922875b8844SAlexander Motin }
923875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
924875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
925875b8844SAlexander Motin }
9265394d87eSNate Lawson return (0);
9275394d87eSNate Lawson }
9285394d87eSNate Lawson
929c73930f3SNate Lawson /* Print some basic latency/rate information to assist in debugging. */
930c73930f3SNate Lawson static void
hpet_test(struct hpet_softc * sc)931875b8844SAlexander Motin hpet_test(struct hpet_softc *sc)
932c73930f3SNate Lawson {
933c73930f3SNate Lawson int i;
934c73930f3SNate Lawson uint32_t u1, u2;
935c73930f3SNate Lawson struct bintime b0, b1, b2;
936c73930f3SNate Lawson struct timespec ts;
937c73930f3SNate Lawson
938c73930f3SNate Lawson binuptime(&b0);
939c73930f3SNate Lawson binuptime(&b0);
940c73930f3SNate Lawson binuptime(&b1);
941f831d6e0SJohn Baldwin u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
942c73930f3SNate Lawson for (i = 1; i < 1000; i++)
943f831d6e0SJohn Baldwin u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
944c73930f3SNate Lawson binuptime(&b2);
945f831d6e0SJohn Baldwin u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
946c73930f3SNate Lawson
947c73930f3SNate Lawson bintime_sub(&b2, &b1);
948c73930f3SNate Lawson bintime_sub(&b1, &b0);
949c73930f3SNate Lawson bintime_sub(&b2, &b1);
950c73930f3SNate Lawson bintime2timespec(&b2, &ts);
951c73930f3SNate Lawson
952c73930f3SNate Lawson device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
953c73930f3SNate Lawson (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
954c73930f3SNate Lawson
955c73930f3SNate Lawson device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
9564fa7241fSPoul-Henning Kamp }
9574fa7241fSPoul-Henning Kamp
958875b8844SAlexander Motin #ifdef DEV_APIC
959875b8844SAlexander Motin static int
hpet_remap_intr(device_t dev,device_t child,u_int irq)960875b8844SAlexander Motin hpet_remap_intr(device_t dev, device_t child, u_int irq)
961875b8844SAlexander Motin {
962875b8844SAlexander Motin struct hpet_softc *sc = device_get_softc(dev);
963875b8844SAlexander Motin struct hpet_timer *t;
964875b8844SAlexander Motin uint64_t addr;
965875b8844SAlexander Motin uint32_t data;
966875b8844SAlexander Motin int error, i;
967875b8844SAlexander Motin
968875b8844SAlexander Motin for (i = 0; i < sc->num_timers; i++) {
969875b8844SAlexander Motin t = &sc->t[i];
970875b8844SAlexander Motin if (t->irq != irq)
971875b8844SAlexander Motin continue;
972875b8844SAlexander Motin error = PCIB_MAP_MSI(
973875b8844SAlexander Motin device_get_parent(device_get_parent(dev)), dev,
974875b8844SAlexander Motin irq, &addr, &data);
975875b8844SAlexander Motin if (error)
976875b8844SAlexander Motin return (error);
977875b8844SAlexander Motin hpet_disable(sc); /* Stop timer to avoid interrupt loss. */
978875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_FSB_ADDR(i), addr);
979875b8844SAlexander Motin bus_write_4(sc->mem_res, HPET_TIMER_FSB_VAL(i), data);
980875b8844SAlexander Motin hpet_enable(sc);
981875b8844SAlexander Motin return (0);
982875b8844SAlexander Motin }
983875b8844SAlexander Motin return (ENOENT);
984875b8844SAlexander Motin }
985875b8844SAlexander Motin #endif
986875b8844SAlexander Motin
987875b8844SAlexander Motin static device_method_t hpet_methods[] = {
9884fa7241fSPoul-Henning Kamp /* Device interface */
989875b8844SAlexander Motin DEVMETHOD(device_identify, hpet_identify),
990875b8844SAlexander Motin DEVMETHOD(device_probe, hpet_probe),
991875b8844SAlexander Motin DEVMETHOD(device_attach, hpet_attach),
992875b8844SAlexander Motin DEVMETHOD(device_detach, hpet_detach),
993875b8844SAlexander Motin DEVMETHOD(device_suspend, hpet_suspend),
994875b8844SAlexander Motin DEVMETHOD(device_resume, hpet_resume),
995875b8844SAlexander Motin
996875b8844SAlexander Motin #ifdef DEV_APIC
997875b8844SAlexander Motin DEVMETHOD(bus_remap_intr, hpet_remap_intr),
998875b8844SAlexander Motin #endif
9994fa7241fSPoul-Henning Kamp
100061bfd867SSofian Brabez DEVMETHOD_END
10014fa7241fSPoul-Henning Kamp };
10024fa7241fSPoul-Henning Kamp
1003875b8844SAlexander Motin static driver_t hpet_driver = {
1004875b8844SAlexander Motin "hpet",
1005875b8844SAlexander Motin hpet_methods,
1006875b8844SAlexander Motin sizeof(struct hpet_softc),
10074fa7241fSPoul-Henning Kamp };
10084fa7241fSPoul-Henning Kamp
1009916a5d8aSJohn Baldwin DRIVER_MODULE(hpet, acpi, hpet_driver, 0, 0);
1010875b8844SAlexander Motin MODULE_DEPEND(hpet, acpi, 1, 1, 1);
1011