xref: /freebsd/sys/dev/acpica/acpi_hpet.c (revision 579cb41b132f532bf4915121c0d0b2f43688242e)
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