acpi_hpet.c revision 295841
1151912Sphk/*-
2151912Sphk * Copyright (c) 2005 Poul-Henning Kamp
3209440Smav * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
4151912Sphk * All rights reserved.
5151912Sphk *
6151912Sphk * Redistribution and use in source and binary forms, with or without
7151912Sphk * modification, are permitted provided that the following conditions
8151912Sphk * are met:
9151912Sphk * 1. Redistributions of source code must retain the above copyright
10151912Sphk *    notice, this list of conditions and the following disclaimer.
11151912Sphk * 2. Redistributions in binary form must reproduce the above copyright
12151912Sphk *    notice, this list of conditions and the following disclaimer in the
13151912Sphk *    documentation and/or other materials provided with the distribution.
14151912Sphk *
15151912Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16151912Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17151912Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18151912Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19151912Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20151912Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21151912Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22151912Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23151912Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24151912Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25151912Sphk * SUCH DAMAGE.
26151912Sphk */
27151912Sphk
28151912Sphk#include <sys/cdefs.h>
29151912Sphk__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_hpet.c 295841 2016-02-20 13:37:04Z kib $");
30151912Sphk
31151912Sphk#include "opt_acpi.h"
32268351Smarcel#if defined(__amd64__)
33209371Smav#define	DEV_APIC
34209371Smav#else
35209371Smav#include "opt_apic.h"
36209371Smav#endif
37151912Sphk#include <sys/param.h>
38273598Srpaulo#include <sys/conf.h>
39159217Snjl#include <sys/bus.h>
40151912Sphk#include <sys/kernel.h>
41151912Sphk#include <sys/module.h>
42209371Smav#include <sys/proc.h>
43151912Sphk#include <sys/rman.h>
44273598Srpaulo#include <sys/mman.h>
45151912Sphk#include <sys/time.h>
46209371Smav#include <sys/smp.h>
47209371Smav#include <sys/sysctl.h>
48209371Smav#include <sys/timeet.h>
49151912Sphk#include <sys/timetc.h>
50159217Snjl
51193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
52193530Sjkim#include <contrib/dev/acpica/include/accommon.h>
53193530Sjkim
54151912Sphk#include <dev/acpica/acpivar.h>
55175385Sjhb#include <dev/acpica/acpi_hpet.h>
56151912Sphk
57209371Smav#ifdef DEV_APIC
58209371Smav#include "pcib_if.h"
59209371Smav#endif
60209371Smav
61203062Savg#define HPET_VENDID_AMD		0x4353
62240286Smav#define HPET_VENDID_AMD2	0x1022
63203062Savg#define HPET_VENDID_INTEL	0x8086
64213302Smav#define HPET_VENDID_NVIDIA	0x10de
65232797Smav#define HPET_VENDID_SW		0x1166
66203062Savg
67151912SphkACPI_SERIAL_DECL(hpet, "ACPI HPET support");
68151912Sphk
69209371Smavstatic devclass_t hpet_devclass;
70169574Stakawata
71151931Sscottl/* ACPI CA debugging */
72151935Sscottl#define _COMPONENT	ACPI_TIMER
73151931SscottlACPI_MODULE_NAME("HPET")
74151931Sscottl
75209371Smavstruct hpet_softc {
76151912Sphk	device_t		dev;
77209371Smav	int			mem_rid;
78209371Smav	int			intr_rid;
79209371Smav	int			irq;
80209371Smav	int			useirq;
81209440Smav	int			legacy_route;
82212533Smav	int			per_cpu;
83212238Smav	uint32_t		allowed_irqs;
84159217Snjl	struct resource		*mem_res;
85209371Smav	struct resource		*intr_res;
86209371Smav	void			*intr_handle;
87151912Sphk	ACPI_HANDLE		handle;
88295841Skib	uint32_t		acpi_uid;
89209371Smav	uint64_t		freq;
90209440Smav	uint32_t		caps;
91209371Smav	struct timecounter	tc;
92209371Smav	struct hpet_timer {
93209371Smav		struct eventtimer	et;
94209371Smav		struct hpet_softc	*sc;
95209371Smav		int			num;
96209371Smav		int			mode;
97209371Smav		int			intr_rid;
98209371Smav		int			irq;
99212323Smav		int			pcpu_cpu;
100212323Smav		int			pcpu_misrouted;
101209371Smav		int			pcpu_master;
102209371Smav		int			pcpu_slaves[MAXCPU];
103209371Smav		struct resource		*intr_res;
104209371Smav		void			*intr_handle;
105209371Smav		uint32_t		caps;
106209371Smav		uint32_t		vectors;
107209371Smav		uint32_t		div;
108212491Smav		uint32_t		next;
109209371Smav		char			name[8];
110209371Smav	} 			t[32];
111209371Smav	int			num_timers;
112273598Srpaulo	struct cdev		*pdev;
113273598Srpaulo	int			mmap_allow;
114273598Srpaulo	int			mmap_allow_write;
115151912Sphk};
116151912Sphk
117273598Srpaulostatic d_open_t hpet_open;
118273598Srpaulostatic d_mmap_t hpet_mmap;
119273598Srpaulo
120273598Srpaulostatic struct cdevsw hpet_cdevsw = {
121273598Srpaulo	.d_version =	D_VERSION,
122273598Srpaulo	.d_name =	"hpet",
123273598Srpaulo	.d_open =	hpet_open,
124273598Srpaulo	.d_mmap =	hpet_mmap,
125273598Srpaulo};
126273598Srpaulo
127159217Snjlstatic u_int hpet_get_timecount(struct timecounter *tc);
128209371Smavstatic void hpet_test(struct hpet_softc *sc);
129151912Sphk
130159217Snjlstatic char *hpet_ids[] = { "PNP0103", NULL };
131159217Snjl
132269515Sroyger/* Knob to disable acpi_hpet device */
133269515Sroygerbool acpi_hpet_disabled = false;
134269515Sroyger
135159217Snjlstatic u_int
136151912Sphkhpet_get_timecount(struct timecounter *tc)
137151912Sphk{
138209371Smav	struct hpet_softc *sc;
139151912Sphk
140151912Sphk	sc = tc->tc_priv;
141175385Sjhb	return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
142151912Sphk}
143151912Sphk
144175361Sjhbstatic void
145209371Smavhpet_enable(struct hpet_softc *sc)
146175361Sjhb{
147175361Sjhb	uint32_t val;
148175385Sjhb
149175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
150209440Smav	if (sc->legacy_route)
151209440Smav		val |= HPET_CNF_LEG_RT;
152209440Smav	else
153209440Smav		val &= ~HPET_CNF_LEG_RT;
154185103Sjkim	val |= HPET_CNF_ENABLE;
155185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
156175361Sjhb}
157175361Sjhb
158175361Sjhbstatic void
159209371Smavhpet_disable(struct hpet_softc *sc)
160175361Sjhb{
161175361Sjhb	uint32_t val;
162175385Sjhb
163175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
164185103Sjkim	val &= ~HPET_CNF_ENABLE;
165185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
166175361Sjhb}
167175361Sjhb
168209371Smavstatic int
169247463Smavhpet_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
170209371Smav{
171209371Smav	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
172209371Smav	struct hpet_timer *t;
173209371Smav	struct hpet_softc *sc = mt->sc;
174212491Smav	uint32_t fdiv, now;
175209371Smav
176209371Smav	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
177247463Smav	if (period != 0) {
178209371Smav		t->mode = 1;
179247463Smav		t->div = (sc->freq * period) >> 32;
180209371Smav	} else {
181209371Smav		t->mode = 2;
182209371Smav		t->div = 0;
183209371Smav	}
184247463Smav	if (first != 0)
185247463Smav		fdiv = (sc->freq * first) >> 32;
186247463Smav	else
187210290Smav		fdiv = t->div;
188212238Smav	if (t->irq < 0)
189212238Smav		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
190212238Smav	t->caps |= HPET_TCNF_INT_ENB;
191212491Smav	now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
192212238Smavrestart:
193212491Smav	t->next = now + fdiv;
194209371Smav	if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
195209371Smav		t->caps |= HPET_TCNF_TYPE;
196209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
197209371Smav		    t->caps | HPET_TCNF_VAL_SET);
198212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
199212491Smav		    t->next);
200212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
201212491Smav		    t->div);
202209371Smav	} else {
203212238Smav		t->caps &= ~HPET_TCNF_TYPE;
204212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
205212491Smav		    t->caps);
206212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
207212491Smav		    t->next);
208209371Smav	}
209224919Smav	now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
210224919Smav	if ((int32_t)(now - t->next + HPET_MIN_CYCLES) >= 0) {
211224919Smav		fdiv *= 2;
212224919Smav		goto restart;
213212238Smav	}
214209371Smav	return (0);
215209371Smav}
216209371Smav
217209371Smavstatic int
218209371Smavhpet_stop(struct eventtimer *et)
219209371Smav{
220209371Smav	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
221209371Smav	struct hpet_timer *t;
222209371Smav	struct hpet_softc *sc = mt->sc;
223209371Smav
224209371Smav	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
225209371Smav	t->mode = 0;
226209371Smav	t->caps &= ~(HPET_TCNF_INT_ENB | HPET_TCNF_TYPE);
227209371Smav	bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
228209371Smav	return (0);
229209371Smav}
230209371Smav
231209371Smavstatic int
232209371Smavhpet_intr_single(void *arg)
233209371Smav{
234209371Smav	struct hpet_timer *t = (struct hpet_timer *)arg;
235209371Smav	struct hpet_timer *mt;
236209371Smav	struct hpet_softc *sc = t->sc;
237209371Smav	uint32_t now;
238209371Smav
239212491Smav	if (t->mode == 0)
240212491Smav		return (FILTER_STRAY);
241212323Smav	/* Check that per-CPU timer interrupt reached right CPU. */
242212323Smav	if (t->pcpu_cpu >= 0 && t->pcpu_cpu != curcpu) {
243212323Smav		if ((++t->pcpu_misrouted) % 32 == 0) {
244212323Smav			printf("HPET interrupt routed to the wrong CPU"
245212323Smav			    " (timer %d CPU %d -> %d)!\n",
246212323Smav			    t->num, t->pcpu_cpu, curcpu);
247212323Smav		}
248212323Smav
249212323Smav		/*
250212323Smav		 * Reload timer, hoping that next time may be more lucky
251212323Smav		 * (system will manage proper interrupt binding).
252212323Smav		 */
253212323Smav		if ((t->mode == 1 && (t->caps & HPET_TCAP_PER_INT) == 0) ||
254212323Smav		    t->mode == 2) {
255212491Smav			t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER) +
256212491Smav			    sc->freq / 8;
257212323Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
258212491Smav			    t->next);
259212323Smav		}
260212323Smav		return (FILTER_HANDLED);
261212323Smav	}
262209371Smav	if (t->mode == 1 &&
263209371Smav	    (t->caps & HPET_TCAP_PER_INT) == 0) {
264212491Smav		t->next += t->div;
265209371Smav		now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
266212491Smav		if ((int32_t)((now + t->div / 2) - t->next) > 0)
267212491Smav			t->next = now + t->div / 2;
268209371Smav		bus_write_4(sc->mem_res,
269212491Smav		    HPET_TIMER_COMPARATOR(t->num), t->next);
270209371Smav	} else if (t->mode == 2)
271209371Smav		t->mode = 0;
272209371Smav	mt = (t->pcpu_master < 0) ? t : &sc->t[t->pcpu_master];
273209990Smav	if (mt->et.et_active)
274209990Smav		mt->et.et_event_cb(&mt->et, mt->et.et_arg);
275209371Smav	return (FILTER_HANDLED);
276209371Smav}
277209371Smav
278209371Smavstatic int
279209371Smavhpet_intr(void *arg)
280209371Smav{
281209371Smav	struct hpet_softc *sc = (struct hpet_softc *)arg;
282209371Smav	int i;
283209371Smav	uint32_t val;
284209371Smav
285209371Smav	val = bus_read_4(sc->mem_res, HPET_ISR);
286209371Smav	if (val) {
287209371Smav		bus_write_4(sc->mem_res, HPET_ISR, val);
288209371Smav		val &= sc->useirq;
289209371Smav		for (i = 0; i < sc->num_timers; i++) {
290209371Smav			if ((val & (1 << i)) == 0)
291209371Smav				continue;
292209371Smav			hpet_intr_single(&sc->t[i]);
293209371Smav		}
294209371Smav		return (FILTER_HANDLED);
295209371Smav	}
296209371Smav	return (FILTER_STRAY);
297209371Smav}
298209371Smav
299295841Skibuint32_t
300295841Skibhpet_get_uid(device_t dev)
301295841Skib{
302295841Skib	struct hpet_softc *sc;
303295841Skib
304295841Skib	sc = device_get_softc(dev);
305295841Skib	return (sc->acpi_uid);
306295841Skib}
307295841Skib
308208436Smavstatic ACPI_STATUS
309209371Smavhpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
310208436Smav    void **status)
311208436Smav{
312208436Smav	char 		**ids;
313208436Smav	uint32_t	id = (uint32_t)(uintptr_t)context;
314208438Smav	uint32_t	uid = 0;
315208436Smav
316208436Smav	for (ids = hpet_ids; *ids != NULL; ids++) {
317208436Smav		if (acpi_MatchHid(handle, *ids))
318208436Smav		        break;
319208436Smav	}
320208436Smav	if (*ids == NULL)
321208436Smav		return (AE_OK);
322209371Smav	if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)) ||
323209371Smav	    id == uid)
324258164Smav		*status = acpi_get_device(handle);
325208436Smav	return (AE_OK);
326208436Smav}
327208436Smav
328216263Sjhb/*
329216263Sjhb * Find an existing IRQ resource that matches the requested IRQ range
330216263Sjhb * and return its RID.  If one is not found, use a new RID.
331216263Sjhb */
332216263Sjhbstatic int
333216263Sjhbhpet_find_irq_rid(device_t dev, u_long start, u_long end)
334216263Sjhb{
335294883Sjhibbits	rman_res_t irq;
336216263Sjhb	int error, rid;
337216263Sjhb
338216263Sjhb	for (rid = 0;; rid++) {
339216263Sjhb		error = bus_get_resource(dev, SYS_RES_IRQ, rid, &irq, NULL);
340216263Sjhb		if (error != 0 || (start <= irq && irq <= end))
341216263Sjhb			return (rid);
342216263Sjhb	}
343216263Sjhb}
344216263Sjhb
345273598Srpaulostatic int
346273598Srpaulohpet_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
347273598Srpaulo{
348273598Srpaulo	struct hpet_softc *sc;
349273598Srpaulo
350273598Srpaulo	sc = cdev->si_drv1;
351273598Srpaulo	if (!sc->mmap_allow)
352273598Srpaulo		return (EPERM);
353273598Srpaulo	else
354273598Srpaulo		return (0);
355273598Srpaulo}
356273598Srpaulo
357273598Srpaulostatic int
358273598Srpaulohpet_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
359273598Srpaulo    int nprot, vm_memattr_t *memattr)
360273598Srpaulo{
361273598Srpaulo	struct hpet_softc *sc;
362273598Srpaulo
363273598Srpaulo	sc = cdev->si_drv1;
364273598Srpaulo	if (offset > rman_get_size(sc->mem_res))
365273598Srpaulo		return (EINVAL);
366273598Srpaulo	if (!sc->mmap_allow_write && (nprot & PROT_WRITE))
367273598Srpaulo		return (EPERM);
368273598Srpaulo	*paddr = rman_get_start(sc->mem_res) + offset;
369273647Skib	*memattr = VM_MEMATTR_UNCACHEABLE;
370273598Srpaulo
371273598Srpaulo	return (0);
372273598Srpaulo}
373273598Srpaulo
374169592Snjl/* Discover the HPET via the ACPI table of the same name. */
375273602Srpaulostatic void
376209371Smavhpet_identify(driver_t *driver, device_t parent)
377169574Stakawata{
378169574Stakawata	ACPI_TABLE_HPET *hpet;
379169574Stakawata	ACPI_STATUS	status;
380169574Stakawata	device_t	child;
381258164Smav	int		i;
382169574Stakawata
383172489Snjl	/* Only one HPET device can be added. */
384209371Smav	if (devclass_get_device(hpet_devclass, 0))
385172489Snjl		return;
386208436Smav	for (i = 1; ; i++) {
387208436Smav		/* Search for HPET table. */
388208436Smav		status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
389208436Smav		if (ACPI_FAILURE(status))
390208436Smav			return;
391208436Smav		/* Search for HPET device with same ID. */
392258164Smav		child = NULL;
393208436Smav		AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
394258164Smav		    100, hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence,
395258164Smav		    (void *)&child);
396208436Smav		/* If found - let it be probed in normal way. */
397258164Smav		if (child) {
398258164Smav			if (bus_get_resource(child, SYS_RES_MEMORY, 0,
399258164Smav			    NULL, NULL) != 0)
400258164Smav				bus_set_resource(child, SYS_RES_MEMORY, 0,
401258164Smav				    hpet->Address.Address, HPET_MEM_WIDTH);
402208436Smav			continue;
403258164Smav		}
404208436Smav		/* If not - create it from table info. */
405231161Sjkim		child = BUS_ADD_CHILD(parent, 2, "hpet", 0);
406208436Smav		if (child == NULL) {
407208436Smav			printf("%s: can't add child\n", __func__);
408208436Smav			continue;
409208436Smav		}
410208436Smav		bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
411208436Smav		    HPET_MEM_WIDTH);
412169574Stakawata	}
413169574Stakawata}
414169574Stakawata
415151912Sphkstatic int
416209371Smavhpet_probe(device_t dev)
417151912Sphk{
418159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
419159217Snjl
420269515Sroyger	if (acpi_disabled("hpet") || acpi_hpet_disabled)
421151912Sphk		return (ENXIO);
422199016Savg	if (acpi_get_handle(dev) != NULL &&
423208436Smav	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL)
424169592Snjl		return (ENXIO);
425151912Sphk
426159217Snjl	device_set_desc(dev, "High Precision Event Timer");
427151912Sphk	return (0);
428151912Sphk}
429151912Sphk
430151912Sphkstatic int
431209371Smavhpet_attach(device_t dev)
432151912Sphk{
433209371Smav	struct hpet_softc *sc;
434209371Smav	struct hpet_timer *t;
435295839Skib	struct make_dev_args mda;
436209371Smav	int i, j, num_msi, num_timers, num_percpu_et, num_percpu_t, cur_cpu;
437295839Skib	int pcpu_master, error;
438209371Smav	static int maxhpetet = 0;
439212238Smav	uint32_t val, val2, cvectors, dvectors;
440209371Smav	uint16_t vendor, rev;
441151912Sphk
442151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
443151912Sphk
444151912Sphk	sc = device_get_softc(dev);
445151912Sphk	sc->dev = dev;
446151912Sphk	sc->handle = acpi_get_handle(dev);
447151912Sphk
448209371Smav	sc->mem_rid = 0;
449209371Smav	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
450159217Snjl	    RF_ACTIVE);
451159217Snjl	if (sc->mem_res == NULL)
452159217Snjl		return (ENOMEM);
453151912Sphk
454159217Snjl	/* Validate that we can access the whole region. */
455159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
456159217Snjl		device_printf(dev, "memory region width %ld too small\n",
457159217Snjl		    rman_get_size(sc->mem_res));
458159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
459159217Snjl		return (ENXIO);
460159217Snjl	}
461151912Sphk
462171547Snjl	/* Be sure timer is enabled. */
463175361Sjhb	hpet_enable(sc);
464171547Snjl
465159217Snjl	/* Read basic statistics about the timer. */
466175385Sjhb	val = bus_read_4(sc->mem_res, HPET_PERIOD);
467175361Sjhb	if (val == 0) {
468175361Sjhb		device_printf(dev, "invalid period\n");
469175361Sjhb		hpet_disable(sc);
470175361Sjhb		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
471175361Sjhb		return (ENXIO);
472175361Sjhb	}
473175361Sjhb
474209371Smav	sc->freq = (1000000000000000LL + val / 2) / val;
475209440Smav	sc->caps = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
476209440Smav	vendor = (sc->caps & HPET_CAP_VENDOR_ID) >> 16;
477209440Smav	rev = sc->caps & HPET_CAP_REV_ID;
478209440Smav	num_timers = 1 + ((sc->caps & HPET_CAP_NUM_TIM) >> 8);
479209371Smav	/*
480209371Smav	 * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
481209371Smav	 * Specification and provides an off by one number
482209371Smav	 * of timers/comparators.
483209371Smav	 * Additionally, they use unregistered value in VENDOR_ID field.
484209371Smav	 */
485209371Smav	if (vendor == HPET_VENDID_AMD && rev < 0x10 && num_timers > 0)
486209371Smav		num_timers--;
487209371Smav	sc->num_timers = num_timers;
488159217Snjl	if (bootverbose) {
489159217Snjl		device_printf(dev,
490209371Smav		    "vendor 0x%x, rev 0x%x, %jdHz%s, %d timers,%s\n",
491209440Smav		    vendor, rev, sc->freq,
492209440Smav		    (sc->caps & HPET_CAP_COUNT_SIZE) ? " 64bit" : "",
493209440Smav		    num_timers,
494209440Smav		    (sc->caps & HPET_CAP_LEG_RT) ? " legacy route" : "");
495159217Snjl	}
496209371Smav	for (i = 0; i < num_timers; i++) {
497209371Smav		t = &sc->t[i];
498209371Smav		t->sc = sc;
499209371Smav		t->num = i;
500209371Smav		t->mode = 0;
501209371Smav		t->intr_rid = -1;
502209371Smav		t->irq = -1;
503212323Smav		t->pcpu_cpu = -1;
504212323Smav		t->pcpu_misrouted = 0;
505209371Smav		t->pcpu_master = -1;
506209371Smav		t->caps = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i));
507209371Smav		t->vectors = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i) + 4);
508209371Smav		if (bootverbose) {
509209371Smav			device_printf(dev,
510209371Smav			    " t%d: irqs 0x%08x (%d)%s%s%s\n", i,
511209371Smav			    t->vectors, (t->caps & HPET_TCNF_INT_ROUTE) >> 9,
512209371Smav			    (t->caps & HPET_TCAP_FSB_INT_DEL) ? ", MSI" : "",
513209371Smav			    (t->caps & HPET_TCAP_SIZE) ? ", 64bit" : "",
514209371Smav			    (t->caps & HPET_TCAP_PER_INT) ? ", periodic" : "");
515209371Smav		}
516209371Smav	}
517159217Snjl	if (testenv("debug.acpi.hpet_test"))
518209371Smav		hpet_test(sc);
519171547Snjl	/*
520171547Snjl	 * Don't attach if the timer never increments.  Since the spec
521171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
522171547Snjl	 */
523175385Sjhb	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
524171547Snjl	DELAY(1);
525175385Sjhb	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
526171547Snjl	if (val == val2) {
527171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
528175361Sjhb		hpet_disable(sc);
529171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
530171547Snjl		return (ENXIO);
531171547Snjl	}
532208436Smav	/* Announce first HPET as timecounter. */
533208436Smav	if (device_get_unit(dev) == 0) {
534209371Smav		sc->tc.tc_get_timecount = hpet_get_timecount,
535209371Smav		sc->tc.tc_counter_mask = ~0u,
536209371Smav		sc->tc.tc_name = "HPET",
537222222Sjkim		sc->tc.tc_quality = 950,
538209371Smav		sc->tc.tc_frequency = sc->freq;
539209371Smav		sc->tc.tc_priv = sc;
540209371Smav		tc_init(&sc->tc);
541208436Smav	}
542209371Smav	/* If not disabled - setup and announce event timers. */
543209371Smav	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
544209371Smav	     "clock", &i) == 0 && i == 0)
545209371Smav	        return (0);
546209440Smav
547209440Smav	/* Check whether we can and want legacy routing. */
548209440Smav	sc->legacy_route = 0;
549209440Smav	resource_int_value(device_get_name(dev), device_get_unit(dev),
550209440Smav	     "legacy_route", &sc->legacy_route);
551209440Smav	if ((sc->caps & HPET_CAP_LEG_RT) == 0)
552209440Smav		sc->legacy_route = 0;
553209440Smav	if (sc->legacy_route) {
554209440Smav		sc->t[0].vectors = 0;
555209440Smav		sc->t[1].vectors = 0;
556209440Smav	}
557209440Smav
558212238Smav	/* Check what IRQs we want use. */
559212238Smav	/* By default allow any PCI IRQs. */
560212238Smav	sc->allowed_irqs = 0xffff0000;
561209371Smav	/*
562209371Smav	 * HPETs in AMD chipsets before SB800 have problems with IRQs >= 16
563209371Smav	 * Lower are also not always working for different reasons.
564209371Smav	 * SB800 fixed it, but seems do not implements level triggering
565209371Smav	 * properly, that makes it very unreliable - it freezes after any
566209371Smav	 * interrupt loss. Avoid legacy IRQs for AMD.
567209371Smav	 */
568240286Smav	if (vendor == HPET_VENDID_AMD || vendor == HPET_VENDID_AMD2)
569212238Smav		sc->allowed_irqs = 0x00000000;
570212238Smav	/*
571213302Smav	 * NVidia MCP5x chipsets have number of unexplained interrupt
572213302Smav	 * problems. For some reason, using HPET interrupts breaks HDA sound.
573213302Smav	 */
574213302Smav	if (vendor == HPET_VENDID_NVIDIA && rev <= 0x01)
575213302Smav		sc->allowed_irqs = 0x00000000;
576213302Smav	/*
577232797Smav	 * ServerWorks HT1000 reported to have problems with IRQs >= 16.
578232797Smav	 * Lower IRQs are working, but allowed mask is not set correctly.
579232797Smav	 * Legacy_route mode works fine.
580232797Smav	 */
581232797Smav	if (vendor == HPET_VENDID_SW && rev <= 0x01)
582232797Smav		sc->allowed_irqs = 0x00000000;
583232797Smav	/*
584212238Smav	 * Neither QEMU nor VirtualBox report supported IRQs correctly.
585212238Smav	 * The only way to use HPET there is to specify IRQs manually
586215473Sjhb	 * and/or use legacy_route. Legacy_route mode works on both.
587212238Smav	 */
588212238Smav	if (vm_guest)
589212238Smav		sc->allowed_irqs = 0x00000000;
590212238Smav	/* Let user override. */
591212238Smav	resource_int_value(device_get_name(dev), device_get_unit(dev),
592212238Smav	     "allowed_irqs", &sc->allowed_irqs);
593212238Smav
594212533Smav	/* Get how much per-CPU timers we should try to provide. */
595212533Smav	sc->per_cpu = 1;
596212533Smav	resource_int_value(device_get_name(dev), device_get_unit(dev),
597212533Smav	     "per_cpu", &sc->per_cpu);
598212533Smav
599212238Smav	num_msi = 0;
600212238Smav	sc->useirq = 0;
601212238Smav	/* Find IRQ vectors for all timers. */
602212238Smav	cvectors = sc->allowed_irqs & 0xffff0000;
603212238Smav	dvectors = sc->allowed_irqs & 0x0000ffff;
604212238Smav	if (sc->legacy_route)
605212238Smav		dvectors &= 0x0000fefe;
606209371Smav	for (i = 0; i < num_timers; i++) {
607209371Smav		t = &sc->t[i];
608209440Smav		if (sc->legacy_route && i < 2)
609209440Smav			t->irq = (i == 0) ? 0 : 8;
610209371Smav#ifdef DEV_APIC
611209440Smav		else if (t->caps & HPET_TCAP_FSB_INT_DEL) {
612209371Smav			if ((j = PCIB_ALLOC_MSIX(
613209371Smav			    device_get_parent(device_get_parent(dev)), dev,
614209371Smav			    &t->irq))) {
615209371Smav				device_printf(dev,
616269897Sneel				    "Can't allocate interrupt for t%d: %d\n",
617269897Sneel				    i, j);
618209440Smav			}
619209440Smav		}
620209440Smav#endif
621212238Smav		else if (dvectors & t->vectors) {
622212238Smav			t->irq = ffs(dvectors & t->vectors) - 1;
623212238Smav			dvectors &= ~(1 << t->irq);
624212238Smav		}
625209440Smav		if (t->irq >= 0) {
626216263Sjhb			t->intr_rid = hpet_find_irq_rid(dev, t->irq, t->irq);
627216490Sjhb			t->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
628216490Sjhb			    &t->intr_rid, t->irq, t->irq, 1, RF_ACTIVE);
629216490Sjhb			if (t->intr_res == NULL) {
630209440Smav				t->irq = -1;
631209440Smav				device_printf(dev,
632209440Smav				    "Can't map interrupt for t%d.\n", i);
633216490Sjhb			} else if (bus_setup_intr(dev, t->intr_res,
634216490Sjhb			    INTR_TYPE_CLK, hpet_intr_single, NULL, t,
635216490Sjhb			    &t->intr_handle) != 0) {
636209440Smav				t->irq = -1;
637209440Smav				device_printf(dev,
638209440Smav				    "Can't setup interrupt for t%d.\n", i);
639209371Smav			} else {
640209371Smav				bus_describe_intr(dev, t->intr_res,
641209371Smav				    t->intr_handle, "t%d", i);
642209371Smav				num_msi++;
643209371Smav			}
644209440Smav		}
645209440Smav		if (t->irq < 0 && (cvectors & t->vectors) != 0) {
646209371Smav			cvectors &= t->vectors;
647209371Smav			sc->useirq |= (1 << i);
648209371Smav		}
649209371Smav	}
650209440Smav	if (sc->legacy_route && sc->t[0].irq < 0 && sc->t[1].irq < 0)
651209440Smav		sc->legacy_route = 0;
652209440Smav	if (sc->legacy_route)
653209440Smav		hpet_enable(sc);
654209440Smav	/* Group timers for per-CPU operation. */
655212533Smav	num_percpu_et = min(num_msi / mp_ncpus, sc->per_cpu);
656209440Smav	num_percpu_t = num_percpu_et * mp_ncpus;
657209440Smav	pcpu_master = 0;
658209440Smav	cur_cpu = CPU_FIRST();
659209440Smav	for (i = 0; i < num_timers; i++) {
660209440Smav		t = &sc->t[i];
661209440Smav		if (t->irq >= 0 && num_percpu_t > 0) {
662209440Smav			if (cur_cpu == CPU_FIRST())
663209440Smav				pcpu_master = i;
664212323Smav			t->pcpu_cpu = cur_cpu;
665209440Smav			t->pcpu_master = pcpu_master;
666209440Smav			sc->t[pcpu_master].
667209440Smav			    pcpu_slaves[cur_cpu] = i;
668209440Smav			bus_bind_intr(dev, t->intr_res, cur_cpu);
669209440Smav			cur_cpu = CPU_NEXT(cur_cpu);
670209440Smav			num_percpu_t--;
671212238Smav		} else if (t->irq >= 0)
672212238Smav			bus_bind_intr(dev, t->intr_res, CPU_FIRST());
673209440Smav	}
674209371Smav	bus_write_4(sc->mem_res, HPET_ISR, 0xffffffff);
675209371Smav	sc->irq = -1;
676215473Sjhb	/* If at least one timer needs legacy IRQ - set it up. */
677209371Smav	if (sc->useirq) {
678209371Smav		j = i = fls(cvectors) - 1;
679209371Smav		while (j > 0 && (cvectors & (1 << (j - 1))) != 0)
680209371Smav			j--;
681216263Sjhb		sc->intr_rid = hpet_find_irq_rid(dev, j, i);
682216490Sjhb		sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
683216490Sjhb		    &sc->intr_rid, j, i, 1, RF_SHAREABLE | RF_ACTIVE);
684216490Sjhb		if (sc->intr_res == NULL)
685216490Sjhb			device_printf(dev, "Can't map interrupt.\n");
686216490Sjhb		else if (bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK,
687216490Sjhb		    hpet_intr, NULL, sc, &sc->intr_handle) != 0) {
688209371Smav			device_printf(dev, "Can't setup interrupt.\n");
689209371Smav		} else {
690209371Smav			sc->irq = rman_get_start(sc->intr_res);
691209371Smav			/* Bind IRQ to BSP to avoid live migration. */
692209371Smav			bus_bind_intr(dev, sc->intr_res, CPU_FIRST());
693209371Smav		}
694209371Smav	}
695209371Smav	/* Program and announce event timers. */
696209371Smav	for (i = 0; i < num_timers; i++) {
697209371Smav		t = &sc->t[i];
698209371Smav		t->caps &= ~(HPET_TCNF_FSB_EN | HPET_TCNF_INT_ROUTE);
699209371Smav		t->caps &= ~(HPET_TCNF_VAL_SET | HPET_TCNF_INT_ENB);
700209440Smav		t->caps &= ~(HPET_TCNF_INT_TYPE);
701209371Smav		t->caps |= HPET_TCNF_32MODE;
702209440Smav		if (t->irq >= 0 && sc->legacy_route && i < 2) {
703209440Smav			/* Legacy route doesn't need more configuration. */
704209440Smav		} else
705209371Smav#ifdef DEV_APIC
706212238Smav		if ((t->caps & HPET_TCAP_FSB_INT_DEL) && t->irq >= 0) {
707209371Smav			uint64_t addr;
708273602Srpaulo			uint32_t data;
709273602Srpaulo
710209371Smav			if (PCIB_MAP_MSI(
711209371Smav			    device_get_parent(device_get_parent(dev)), dev,
712209371Smav			    t->irq, &addr, &data) == 0) {
713209371Smav				bus_write_4(sc->mem_res,
714209371Smav				    HPET_TIMER_FSB_ADDR(i), addr);
715209371Smav				bus_write_4(sc->mem_res,
716209371Smav				    HPET_TIMER_FSB_VAL(i), data);
717209371Smav				t->caps |= HPET_TCNF_FSB_EN;
718209371Smav			} else
719209371Smav				t->irq = -2;
720209371Smav		} else
721209371Smav#endif
722212238Smav		if (t->irq >= 0)
723212238Smav			t->caps |= (t->irq << 9);
724212238Smav		else if (sc->irq >= 0 && (t->vectors & (1 << sc->irq)))
725209371Smav			t->caps |= (sc->irq << 9) | HPET_TCNF_INT_TYPE;
726209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps);
727209371Smav		/* Skip event timers without set up IRQ. */
728209371Smav		if (t->irq < 0 &&
729209371Smav		    (sc->irq < 0 || (t->vectors & (1 << sc->irq)) == 0))
730209371Smav			continue;
731209371Smav		/* Announce the reset. */
732209371Smav		if (maxhpetet == 0)
733209371Smav			t->et.et_name = "HPET";
734209371Smav		else {
735209371Smav			sprintf(t->name, "HPET%d", maxhpetet);
736209371Smav			t->et.et_name = t->name;
737209371Smav		}
738209371Smav		t->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
739209371Smav		t->et.et_quality = 450;
740209371Smav		if (t->pcpu_master >= 0) {
741209371Smav			t->et.et_flags |= ET_FLAGS_PERCPU;
742209371Smav			t->et.et_quality += 100;
743248170Smav		} else if (mp_ncpus >= 8)
744248154Smav			t->et.et_quality -= 100;
745209371Smav		if ((t->caps & HPET_TCAP_PER_INT) == 0)
746209371Smav			t->et.et_quality -= 10;
747209371Smav		t->et.et_frequency = sc->freq;
748247463Smav		t->et.et_min_period =
749247463Smav		    ((uint64_t)(HPET_MIN_CYCLES * 2) << 32) / sc->freq;
750247463Smav		t->et.et_max_period = (0xfffffffeLLU << 32) / sc->freq;
751209371Smav		t->et.et_start = hpet_start;
752209371Smav		t->et.et_stop = hpet_stop;
753209371Smav		t->et.et_priv = &sc->t[i];
754209371Smav		if (t->pcpu_master < 0 || t->pcpu_master == i) {
755209371Smav			et_register(&t->et);
756209371Smav			maxhpetet++;
757209371Smav		}
758209371Smav	}
759295841Skib	acpi_GetInteger(sc->handle, "_UID", &sc->acpi_uid);
760273598Srpaulo
761295839Skib	make_dev_args_init(&mda);
762295839Skib	mda.mda_devsw = &hpet_cdevsw;
763295839Skib	mda.mda_uid = UID_ROOT;
764295839Skib	mda.mda_gid = GID_WHEEL;
765295839Skib	mda.mda_mode = 0600;
766295839Skib	mda.mda_si_drv1 = sc;
767295839Skib	error = make_dev_s(&mda, &sc->pdev, "hpet%d", device_get_unit(dev));
768295839Skib	if (error == 0) {
769273598Srpaulo		sc->mmap_allow = 1;
770273598Srpaulo		TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow",
771273598Srpaulo		    &sc->mmap_allow);
772273598Srpaulo		sc->mmap_allow_write = 1;
773273598Srpaulo		TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow_write",
774273598Srpaulo		    &sc->mmap_allow_write);
775273598Srpaulo		SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
776273598Srpaulo		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
777273598Srpaulo		    OID_AUTO, "mmap_allow",
778273598Srpaulo		    CTLFLAG_RW, &sc->mmap_allow, 0,
779273598Srpaulo		    "Allow userland to memory map HPET");
780273607Srpaulo		SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
781273607Srpaulo		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
782273607Srpaulo		    OID_AUTO, "mmap_allow_write",
783273607Srpaulo		    CTLFLAG_RW, &sc->mmap_allow_write, 0,
784273607Srpaulo		    "Allow userland write to the HPET register space");
785295839Skib	} else {
786295839Skib		device_printf(dev, "could not create /dev/hpet%d, error %d\n",
787295839Skib		    device_get_unit(dev), error);
788295839Skib	}
789273598Srpaulo
790159217Snjl	return (0);
791159217Snjl}
792159217Snjl
793159217Snjlstatic int
794209371Smavhpet_detach(device_t dev)
795159217Snjl{
796159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
797159217Snjl
798159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
799159217Snjl	return (EBUSY);
800159217Snjl}
801159217Snjl
802168010Snjlstatic int
803209371Smavhpet_suspend(device_t dev)
804175361Sjhb{
805212541Smav//	struct hpet_softc *sc;
806175361Sjhb
807175361Sjhb	/*
808175361Sjhb	 * Disable the timer during suspend.  The timer will not lose
809175361Sjhb	 * its state in S1 or S2, but we are required to disable
810175361Sjhb	 * it.
811175361Sjhb	 */
812212541Smav//	sc = device_get_softc(dev);
813212541Smav//	hpet_disable(sc);
814175361Sjhb
815175361Sjhb	return (0);
816175361Sjhb}
817175361Sjhb
818175361Sjhbstatic int
819209371Smavhpet_resume(device_t dev)
820168010Snjl{
821209371Smav	struct hpet_softc *sc;
822209371Smav	struct hpet_timer *t;
823209371Smav	int i;
824168010Snjl
825168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
826168010Snjl	sc = device_get_softc(dev);
827175361Sjhb	hpet_enable(sc);
828209371Smav	/* Restart event timers that were running on suspend. */
829209371Smav	for (i = 0; i < sc->num_timers; i++) {
830209371Smav		t = &sc->t[i];
831209371Smav#ifdef DEV_APIC
832209440Smav		if (t->irq >= 0 && (sc->legacy_route == 0 || i >= 2)) {
833209371Smav			uint64_t addr;
834273602Srpaulo			uint32_t data;
835273602Srpaulo
836209371Smav			if (PCIB_MAP_MSI(
837209371Smav			    device_get_parent(device_get_parent(dev)), dev,
838209371Smav			    t->irq, &addr, &data) == 0) {
839209371Smav				bus_write_4(sc->mem_res,
840209371Smav				    HPET_TIMER_FSB_ADDR(i), addr);
841209371Smav				bus_write_4(sc->mem_res,
842209371Smav				    HPET_TIMER_FSB_VAL(i), data);
843209371Smav			}
844209371Smav		}
845209371Smav#endif
846209371Smav		if (t->mode == 0)
847209371Smav			continue;
848212491Smav		t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
849209371Smav		if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
850209371Smav			t->caps |= HPET_TCNF_TYPE;
851212491Smav			t->next += t->div;
852209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
853209371Smav			    t->caps | HPET_TCNF_VAL_SET);
854209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
855212491Smav			    t->next);
856209371Smav			bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
857209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
858209371Smav			    t->div);
859209371Smav		} else {
860212491Smav			t->next += sc->freq / 1024;
861209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
862212491Smav			    t->next);
863209371Smav		}
864209371Smav		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
865209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
866209371Smav	}
867168010Snjl	return (0);
868168010Snjl}
869168010Snjl
870159217Snjl/* Print some basic latency/rate information to assist in debugging. */
871159217Snjlstatic void
872209371Smavhpet_test(struct hpet_softc *sc)
873159217Snjl{
874151912Sphk	int i;
875151912Sphk	uint32_t u1, u2;
876151912Sphk	struct bintime b0, b1, b2;
877151912Sphk	struct timespec ts;
878151912Sphk
879151912Sphk	binuptime(&b0);
880151912Sphk	binuptime(&b0);
881151912Sphk	binuptime(&b1);
882175385Sjhb	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
883151912Sphk	for (i = 1; i < 1000; i++)
884175385Sjhb		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
885151912Sphk	binuptime(&b2);
886175385Sjhb	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
887151912Sphk
888151912Sphk	bintime_sub(&b2, &b1);
889151912Sphk	bintime_sub(&b1, &b0);
890151912Sphk	bintime_sub(&b2, &b1);
891151912Sphk	bintime2timespec(&b2, &ts);
892151912Sphk
893159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
894151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
895151912Sphk
896159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
897151912Sphk}
898151912Sphk
899209371Smav#ifdef DEV_APIC
900209371Smavstatic int
901209371Smavhpet_remap_intr(device_t dev, device_t child, u_int irq)
902209371Smav{
903209371Smav	struct hpet_softc *sc = device_get_softc(dev);
904209371Smav	struct hpet_timer *t;
905209371Smav	uint64_t addr;
906273602Srpaulo	uint32_t data;
907209371Smav	int error, i;
908209371Smav
909209371Smav	for (i = 0; i < sc->num_timers; i++) {
910209371Smav		t = &sc->t[i];
911209371Smav		if (t->irq != irq)
912209371Smav			continue;
913209371Smav		error = PCIB_MAP_MSI(
914209371Smav		    device_get_parent(device_get_parent(dev)), dev,
915209371Smav		    irq, &addr, &data);
916209371Smav		if (error)
917209371Smav			return (error);
918209371Smav		hpet_disable(sc); /* Stop timer to avoid interrupt loss. */
919209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_FSB_ADDR(i), addr);
920209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_FSB_VAL(i), data);
921209371Smav		hpet_enable(sc);
922209371Smav		return (0);
923209371Smav	}
924209371Smav	return (ENOENT);
925209371Smav}
926209371Smav#endif
927209371Smav
928209371Smavstatic device_method_t hpet_methods[] = {
929151912Sphk	/* Device interface */
930209371Smav	DEVMETHOD(device_identify, hpet_identify),
931209371Smav	DEVMETHOD(device_probe, hpet_probe),
932209371Smav	DEVMETHOD(device_attach, hpet_attach),
933209371Smav	DEVMETHOD(device_detach, hpet_detach),
934209371Smav	DEVMETHOD(device_suspend, hpet_suspend),
935209371Smav	DEVMETHOD(device_resume, hpet_resume),
936151912Sphk
937209371Smav#ifdef DEV_APIC
938209371Smav	DEVMETHOD(bus_remap_intr, hpet_remap_intr),
939209371Smav#endif
940209371Smav
941246128Ssbz	DEVMETHOD_END
942151912Sphk};
943151912Sphk
944209371Smavstatic driver_t	hpet_driver = {
945209371Smav	"hpet",
946209371Smav	hpet_methods,
947209371Smav	sizeof(struct hpet_softc),
948151912Sphk};
949151912Sphk
950209371SmavDRIVER_MODULE(hpet, acpi, hpet_driver, hpet_devclass, 0, 0);
951209371SmavMODULE_DEPEND(hpet, acpi, 1, 1, 1);
952