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: stable/11/sys/dev/acpica/acpi_hpet.c 335533 2018-06-22 09:08:38Z avg $");
30151912Sphk
31151912Sphk#include "opt_acpi.h"
32305866Skib#include "opt_compat.h"
33305866Skib
34268351Smarcel#if defined(__amd64__)
35209371Smav#define	DEV_APIC
36209371Smav#else
37209371Smav#include "opt_apic.h"
38209371Smav#endif
39151912Sphk#include <sys/param.h>
40273598Srpaulo#include <sys/conf.h>
41159217Snjl#include <sys/bus.h>
42151912Sphk#include <sys/kernel.h>
43151912Sphk#include <sys/module.h>
44209371Smav#include <sys/proc.h>
45151912Sphk#include <sys/rman.h>
46273598Srpaulo#include <sys/mman.h>
47151912Sphk#include <sys/time.h>
48209371Smav#include <sys/smp.h>
49209371Smav#include <sys/sysctl.h>
50209371Smav#include <sys/timeet.h>
51151912Sphk#include <sys/timetc.h>
52305866Skib#include <sys/vdso.h>
53159217Snjl
54193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
55193530Sjkim#include <contrib/dev/acpica/include/accommon.h>
56193530Sjkim
57151912Sphk#include <dev/acpica/acpivar.h>
58175385Sjhb#include <dev/acpica/acpi_hpet.h>
59151912Sphk
60209371Smav#ifdef DEV_APIC
61209371Smav#include "pcib_if.h"
62209371Smav#endif
63209371Smav
64203062Savg#define HPET_VENDID_AMD		0x4353
65240286Smav#define HPET_VENDID_AMD2	0x1022
66203062Savg#define HPET_VENDID_INTEL	0x8086
67213302Smav#define HPET_VENDID_NVIDIA	0x10de
68232797Smav#define HPET_VENDID_SW		0x1166
69203062Savg
70151912SphkACPI_SERIAL_DECL(hpet, "ACPI HPET support");
71151912Sphk
72209371Smavstatic devclass_t hpet_devclass;
73169574Stakawata
74151931Sscottl/* ACPI CA debugging */
75151935Sscottl#define _COMPONENT	ACPI_TIMER
76151931SscottlACPI_MODULE_NAME("HPET")
77151931Sscottl
78209371Smavstruct hpet_softc {
79151912Sphk	device_t		dev;
80209371Smav	int			mem_rid;
81209371Smav	int			intr_rid;
82209371Smav	int			irq;
83209371Smav	int			useirq;
84209440Smav	int			legacy_route;
85212533Smav	int			per_cpu;
86212238Smav	uint32_t		allowed_irqs;
87159217Snjl	struct resource		*mem_res;
88209371Smav	struct resource		*intr_res;
89209371Smav	void			*intr_handle;
90151912Sphk	ACPI_HANDLE		handle;
91295841Skib	uint32_t		acpi_uid;
92209371Smav	uint64_t		freq;
93209440Smav	uint32_t		caps;
94209371Smav	struct timecounter	tc;
95209371Smav	struct hpet_timer {
96209371Smav		struct eventtimer	et;
97209371Smav		struct hpet_softc	*sc;
98209371Smav		int			num;
99209371Smav		int			mode;
100335533Savg#define	TIMER_STOPPED	0
101335533Savg#define	TIMER_PERIODIC	1
102335533Savg#define	TIMER_ONESHOT	2
103209371Smav		int			intr_rid;
104209371Smav		int			irq;
105212323Smav		int			pcpu_cpu;
106212323Smav		int			pcpu_misrouted;
107209371Smav		int			pcpu_master;
108209371Smav		int			pcpu_slaves[MAXCPU];
109209371Smav		struct resource		*intr_res;
110209371Smav		void			*intr_handle;
111209371Smav		uint32_t		caps;
112209371Smav		uint32_t		vectors;
113209371Smav		uint32_t		div;
114212491Smav		uint32_t		next;
115209371Smav		char			name[8];
116209371Smav	} 			t[32];
117209371Smav	int			num_timers;
118273598Srpaulo	struct cdev		*pdev;
119273598Srpaulo	int			mmap_allow;
120273598Srpaulo	int			mmap_allow_write;
121151912Sphk};
122151912Sphk
123273598Srpaulostatic d_open_t hpet_open;
124273598Srpaulostatic d_mmap_t hpet_mmap;
125273598Srpaulo
126273598Srpaulostatic struct cdevsw hpet_cdevsw = {
127273598Srpaulo	.d_version =	D_VERSION,
128273598Srpaulo	.d_name =	"hpet",
129273598Srpaulo	.d_open =	hpet_open,
130273598Srpaulo	.d_mmap =	hpet_mmap,
131273598Srpaulo};
132273598Srpaulo
133159217Snjlstatic u_int hpet_get_timecount(struct timecounter *tc);
134209371Smavstatic void hpet_test(struct hpet_softc *sc);
135151912Sphk
136159217Snjlstatic char *hpet_ids[] = { "PNP0103", NULL };
137159217Snjl
138269515Sroyger/* Knob to disable acpi_hpet device */
139269515Sroygerbool acpi_hpet_disabled = false;
140269515Sroyger
141159217Snjlstatic u_int
142151912Sphkhpet_get_timecount(struct timecounter *tc)
143151912Sphk{
144209371Smav	struct hpet_softc *sc;
145151912Sphk
146151912Sphk	sc = tc->tc_priv;
147175385Sjhb	return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
148151912Sphk}
149151912Sphk
150305866Skibuint32_t
151305866Skibhpet_vdso_timehands(struct vdso_timehands *vdso_th, struct timecounter *tc)
152305866Skib{
153305866Skib	struct hpet_softc *sc;
154305866Skib
155305866Skib	sc = tc->tc_priv;
156305866Skib	vdso_th->th_algo = VDSO_TH_ALGO_X86_HPET;
157305866Skib	vdso_th->th_x86_shift = 0;
158305866Skib	vdso_th->th_x86_hpet_idx = device_get_unit(sc->dev);
159305866Skib	bzero(vdso_th->th_res, sizeof(vdso_th->th_res));
160305866Skib	return (sc->mmap_allow != 0);
161305866Skib}
162305866Skib
163305866Skib#ifdef COMPAT_FREEBSD32
164305866Skibuint32_t
165305866Skibhpet_vdso_timehands32(struct vdso_timehands32 *vdso_th32,
166305866Skib    struct timecounter *tc)
167305866Skib{
168305866Skib	struct hpet_softc *sc;
169305866Skib
170305866Skib	sc = tc->tc_priv;
171305866Skib	vdso_th32->th_algo = VDSO_TH_ALGO_X86_HPET;
172305866Skib	vdso_th32->th_x86_shift = 0;
173305866Skib	vdso_th32->th_x86_hpet_idx = device_get_unit(sc->dev);
174305866Skib	bzero(vdso_th32->th_res, sizeof(vdso_th32->th_res));
175305866Skib	return (sc->mmap_allow != 0);
176305866Skib}
177305866Skib#endif
178305866Skib
179175361Sjhbstatic void
180209371Smavhpet_enable(struct hpet_softc *sc)
181175361Sjhb{
182175361Sjhb	uint32_t val;
183175385Sjhb
184175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
185209440Smav	if (sc->legacy_route)
186209440Smav		val |= HPET_CNF_LEG_RT;
187209440Smav	else
188209440Smav		val &= ~HPET_CNF_LEG_RT;
189185103Sjkim	val |= HPET_CNF_ENABLE;
190185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
191175361Sjhb}
192175361Sjhb
193175361Sjhbstatic void
194209371Smavhpet_disable(struct hpet_softc *sc)
195175361Sjhb{
196175361Sjhb	uint32_t val;
197175385Sjhb
198175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
199185103Sjkim	val &= ~HPET_CNF_ENABLE;
200185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
201175361Sjhb}
202175361Sjhb
203209371Smavstatic int
204247463Smavhpet_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
205209371Smav{
206209371Smav	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
207209371Smav	struct hpet_timer *t;
208209371Smav	struct hpet_softc *sc = mt->sc;
209212491Smav	uint32_t fdiv, now;
210209371Smav
211209371Smav	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
212247463Smav	if (period != 0) {
213335533Savg		t->mode = TIMER_PERIODIC;
214247463Smav		t->div = (sc->freq * period) >> 32;
215209371Smav	} else {
216335533Savg		t->mode = TIMER_ONESHOT;
217209371Smav		t->div = 0;
218209371Smav	}
219247463Smav	if (first != 0)
220247463Smav		fdiv = (sc->freq * first) >> 32;
221247463Smav	else
222210290Smav		fdiv = t->div;
223212238Smav	if (t->irq < 0)
224212238Smav		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
225212238Smav	t->caps |= HPET_TCNF_INT_ENB;
226212491Smav	now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
227212238Smavrestart:
228212491Smav	t->next = now + fdiv;
229335533Savg	if (t->mode == TIMER_PERIODIC && (t->caps & HPET_TCAP_PER_INT)) {
230209371Smav		t->caps |= HPET_TCNF_TYPE;
231209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
232209371Smav		    t->caps | HPET_TCNF_VAL_SET);
233212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
234212491Smav		    t->next);
235212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
236212491Smav		    t->div);
237209371Smav	} else {
238212238Smav		t->caps &= ~HPET_TCNF_TYPE;
239212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
240212491Smav		    t->caps);
241212491Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
242212491Smav		    t->next);
243209371Smav	}
244224919Smav	now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
245224919Smav	if ((int32_t)(now - t->next + HPET_MIN_CYCLES) >= 0) {
246224919Smav		fdiv *= 2;
247224919Smav		goto restart;
248212238Smav	}
249209371Smav	return (0);
250209371Smav}
251209371Smav
252209371Smavstatic int
253209371Smavhpet_stop(struct eventtimer *et)
254209371Smav{
255209371Smav	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
256209371Smav	struct hpet_timer *t;
257209371Smav	struct hpet_softc *sc = mt->sc;
258209371Smav
259209371Smav	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
260335533Savg	t->mode = TIMER_STOPPED;
261209371Smav	t->caps &= ~(HPET_TCNF_INT_ENB | HPET_TCNF_TYPE);
262209371Smav	bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
263209371Smav	return (0);
264209371Smav}
265209371Smav
266209371Smavstatic int
267209371Smavhpet_intr_single(void *arg)
268209371Smav{
269209371Smav	struct hpet_timer *t = (struct hpet_timer *)arg;
270209371Smav	struct hpet_timer *mt;
271209371Smav	struct hpet_softc *sc = t->sc;
272209371Smav	uint32_t now;
273209371Smav
274335533Savg	if (t->mode == TIMER_STOPPED)
275212491Smav		return (FILTER_STRAY);
276212323Smav	/* Check that per-CPU timer interrupt reached right CPU. */
277212323Smav	if (t->pcpu_cpu >= 0 && t->pcpu_cpu != curcpu) {
278212323Smav		if ((++t->pcpu_misrouted) % 32 == 0) {
279212323Smav			printf("HPET interrupt routed to the wrong CPU"
280212323Smav			    " (timer %d CPU %d -> %d)!\n",
281212323Smav			    t->num, t->pcpu_cpu, curcpu);
282212323Smav		}
283212323Smav
284212323Smav		/*
285212323Smav		 * Reload timer, hoping that next time may be more lucky
286212323Smav		 * (system will manage proper interrupt binding).
287212323Smav		 */
288335533Savg		if ((t->mode == TIMER_PERIODIC &&
289335533Savg		    (t->caps & HPET_TCAP_PER_INT) == 0) ||
290335533Savg		    t->mode == TIMER_ONESHOT) {
291212491Smav			t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER) +
292212491Smav			    sc->freq / 8;
293212323Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
294212491Smav			    t->next);
295212323Smav		}
296212323Smav		return (FILTER_HANDLED);
297212323Smav	}
298335533Savg	if (t->mode == TIMER_PERIODIC &&
299209371Smav	    (t->caps & HPET_TCAP_PER_INT) == 0) {
300212491Smav		t->next += t->div;
301209371Smav		now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
302212491Smav		if ((int32_t)((now + t->div / 2) - t->next) > 0)
303212491Smav			t->next = now + t->div / 2;
304209371Smav		bus_write_4(sc->mem_res,
305212491Smav		    HPET_TIMER_COMPARATOR(t->num), t->next);
306335533Savg	} else if (t->mode == TIMER_ONESHOT)
307335533Savg		t->mode = TIMER_STOPPED;
308209371Smav	mt = (t->pcpu_master < 0) ? t : &sc->t[t->pcpu_master];
309209990Smav	if (mt->et.et_active)
310209990Smav		mt->et.et_event_cb(&mt->et, mt->et.et_arg);
311209371Smav	return (FILTER_HANDLED);
312209371Smav}
313209371Smav
314209371Smavstatic int
315209371Smavhpet_intr(void *arg)
316209371Smav{
317209371Smav	struct hpet_softc *sc = (struct hpet_softc *)arg;
318209371Smav	int i;
319209371Smav	uint32_t val;
320209371Smav
321209371Smav	val = bus_read_4(sc->mem_res, HPET_ISR);
322209371Smav	if (val) {
323209371Smav		bus_write_4(sc->mem_res, HPET_ISR, val);
324209371Smav		val &= sc->useirq;
325209371Smav		for (i = 0; i < sc->num_timers; i++) {
326209371Smav			if ((val & (1 << i)) == 0)
327209371Smav				continue;
328209371Smav			hpet_intr_single(&sc->t[i]);
329209371Smav		}
330209371Smav		return (FILTER_HANDLED);
331209371Smav	}
332209371Smav	return (FILTER_STRAY);
333209371Smav}
334209371Smav
335295841Skibuint32_t
336295841Skibhpet_get_uid(device_t dev)
337295841Skib{
338295841Skib	struct hpet_softc *sc;
339295841Skib
340295841Skib	sc = device_get_softc(dev);
341295841Skib	return (sc->acpi_uid);
342295841Skib}
343295841Skib
344208436Smavstatic ACPI_STATUS
345209371Smavhpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
346208436Smav    void **status)
347208436Smav{
348208436Smav	char 		**ids;
349208436Smav	uint32_t	id = (uint32_t)(uintptr_t)context;
350208438Smav	uint32_t	uid = 0;
351208436Smav
352208436Smav	for (ids = hpet_ids; *ids != NULL; ids++) {
353208436Smav		if (acpi_MatchHid(handle, *ids))
354208436Smav		        break;
355208436Smav	}
356208436Smav	if (*ids == NULL)
357208436Smav		return (AE_OK);
358209371Smav	if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)) ||
359209371Smav	    id == uid)
360258164Smav		*status = acpi_get_device(handle);
361208436Smav	return (AE_OK);
362208436Smav}
363208436Smav
364216263Sjhb/*
365216263Sjhb * Find an existing IRQ resource that matches the requested IRQ range
366216263Sjhb * and return its RID.  If one is not found, use a new RID.
367216263Sjhb */
368216263Sjhbstatic int
369216263Sjhbhpet_find_irq_rid(device_t dev, u_long start, u_long end)
370216263Sjhb{
371294883Sjhibbits	rman_res_t irq;
372216263Sjhb	int error, rid;
373216263Sjhb
374216263Sjhb	for (rid = 0;; rid++) {
375216263Sjhb		error = bus_get_resource(dev, SYS_RES_IRQ, rid, &irq, NULL);
376216263Sjhb		if (error != 0 || (start <= irq && irq <= end))
377216263Sjhb			return (rid);
378216263Sjhb	}
379216263Sjhb}
380216263Sjhb
381273598Srpaulostatic int
382273598Srpaulohpet_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
383273598Srpaulo{
384273598Srpaulo	struct hpet_softc *sc;
385273598Srpaulo
386273598Srpaulo	sc = cdev->si_drv1;
387273598Srpaulo	if (!sc->mmap_allow)
388273598Srpaulo		return (EPERM);
389273598Srpaulo	else
390273598Srpaulo		return (0);
391273598Srpaulo}
392273598Srpaulo
393273598Srpaulostatic int
394273598Srpaulohpet_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
395273598Srpaulo    int nprot, vm_memattr_t *memattr)
396273598Srpaulo{
397273598Srpaulo	struct hpet_softc *sc;
398273598Srpaulo
399273598Srpaulo	sc = cdev->si_drv1;
400273598Srpaulo	if (offset > rman_get_size(sc->mem_res))
401273598Srpaulo		return (EINVAL);
402273598Srpaulo	if (!sc->mmap_allow_write && (nprot & PROT_WRITE))
403273598Srpaulo		return (EPERM);
404273598Srpaulo	*paddr = rman_get_start(sc->mem_res) + offset;
405273647Skib	*memattr = VM_MEMATTR_UNCACHEABLE;
406273598Srpaulo
407273598Srpaulo	return (0);
408273598Srpaulo}
409273598Srpaulo
410169592Snjl/* Discover the HPET via the ACPI table of the same name. */
411273602Srpaulostatic void
412209371Smavhpet_identify(driver_t *driver, device_t parent)
413169574Stakawata{
414169574Stakawata	ACPI_TABLE_HPET *hpet;
415169574Stakawata	ACPI_STATUS	status;
416169574Stakawata	device_t	child;
417258164Smav	int		i;
418169574Stakawata
419172489Snjl	/* Only one HPET device can be added. */
420209371Smav	if (devclass_get_device(hpet_devclass, 0))
421172489Snjl		return;
422208436Smav	for (i = 1; ; i++) {
423208436Smav		/* Search for HPET table. */
424208436Smav		status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
425208436Smav		if (ACPI_FAILURE(status))
426208436Smav			return;
427208436Smav		/* Search for HPET device with same ID. */
428258164Smav		child = NULL;
429208436Smav		AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
430258164Smav		    100, hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence,
431258164Smav		    (void *)&child);
432208436Smav		/* If found - let it be probed in normal way. */
433258164Smav		if (child) {
434258164Smav			if (bus_get_resource(child, SYS_RES_MEMORY, 0,
435258164Smav			    NULL, NULL) != 0)
436258164Smav				bus_set_resource(child, SYS_RES_MEMORY, 0,
437258164Smav				    hpet->Address.Address, HPET_MEM_WIDTH);
438208436Smav			continue;
439258164Smav		}
440208436Smav		/* If not - create it from table info. */
441231161Sjkim		child = BUS_ADD_CHILD(parent, 2, "hpet", 0);
442208436Smav		if (child == NULL) {
443208436Smav			printf("%s: can't add child\n", __func__);
444208436Smav			continue;
445208436Smav		}
446208436Smav		bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
447208436Smav		    HPET_MEM_WIDTH);
448169574Stakawata	}
449169574Stakawata}
450169574Stakawata
451151912Sphkstatic int
452209371Smavhpet_probe(device_t dev)
453151912Sphk{
454159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
455159217Snjl
456269515Sroyger	if (acpi_disabled("hpet") || acpi_hpet_disabled)
457151912Sphk		return (ENXIO);
458199016Savg	if (acpi_get_handle(dev) != NULL &&
459208436Smav	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL)
460169592Snjl		return (ENXIO);
461151912Sphk
462159217Snjl	device_set_desc(dev, "High Precision Event Timer");
463151912Sphk	return (0);
464151912Sphk}
465151912Sphk
466151912Sphkstatic int
467209371Smavhpet_attach(device_t dev)
468151912Sphk{
469209371Smav	struct hpet_softc *sc;
470209371Smav	struct hpet_timer *t;
471295839Skib	struct make_dev_args mda;
472209371Smav	int i, j, num_msi, num_timers, num_percpu_et, num_percpu_t, cur_cpu;
473295839Skib	int pcpu_master, error;
474209371Smav	static int maxhpetet = 0;
475212238Smav	uint32_t val, val2, cvectors, dvectors;
476209371Smav	uint16_t vendor, rev;
477151912Sphk
478151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
479151912Sphk
480151912Sphk	sc = device_get_softc(dev);
481151912Sphk	sc->dev = dev;
482151912Sphk	sc->handle = acpi_get_handle(dev);
483151912Sphk
484209371Smav	sc->mem_rid = 0;
485209371Smav	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
486159217Snjl	    RF_ACTIVE);
487159217Snjl	if (sc->mem_res == NULL)
488159217Snjl		return (ENOMEM);
489151912Sphk
490159217Snjl	/* Validate that we can access the whole region. */
491159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
492297000Sjhibbits		device_printf(dev, "memory region width %jd too small\n",
493159217Snjl		    rman_get_size(sc->mem_res));
494159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
495159217Snjl		return (ENXIO);
496159217Snjl	}
497151912Sphk
498171547Snjl	/* Be sure timer is enabled. */
499175361Sjhb	hpet_enable(sc);
500171547Snjl
501159217Snjl	/* Read basic statistics about the timer. */
502175385Sjhb	val = bus_read_4(sc->mem_res, HPET_PERIOD);
503175361Sjhb	if (val == 0) {
504175361Sjhb		device_printf(dev, "invalid period\n");
505175361Sjhb		hpet_disable(sc);
506175361Sjhb		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
507175361Sjhb		return (ENXIO);
508175361Sjhb	}
509175361Sjhb
510209371Smav	sc->freq = (1000000000000000LL + val / 2) / val;
511209440Smav	sc->caps = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
512209440Smav	vendor = (sc->caps & HPET_CAP_VENDOR_ID) >> 16;
513209440Smav	rev = sc->caps & HPET_CAP_REV_ID;
514209440Smav	num_timers = 1 + ((sc->caps & HPET_CAP_NUM_TIM) >> 8);
515209371Smav	/*
516209371Smav	 * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
517209371Smav	 * Specification and provides an off by one number
518209371Smav	 * of timers/comparators.
519209371Smav	 * Additionally, they use unregistered value in VENDOR_ID field.
520209371Smav	 */
521209371Smav	if (vendor == HPET_VENDID_AMD && rev < 0x10 && num_timers > 0)
522209371Smav		num_timers--;
523209371Smav	sc->num_timers = num_timers;
524159217Snjl	if (bootverbose) {
525159217Snjl		device_printf(dev,
526209371Smav		    "vendor 0x%x, rev 0x%x, %jdHz%s, %d timers,%s\n",
527209440Smav		    vendor, rev, sc->freq,
528209440Smav		    (sc->caps & HPET_CAP_COUNT_SIZE) ? " 64bit" : "",
529209440Smav		    num_timers,
530209440Smav		    (sc->caps & HPET_CAP_LEG_RT) ? " legacy route" : "");
531159217Snjl	}
532209371Smav	for (i = 0; i < num_timers; i++) {
533209371Smav		t = &sc->t[i];
534209371Smav		t->sc = sc;
535209371Smav		t->num = i;
536335533Savg		t->mode = TIMER_STOPPED;
537209371Smav		t->intr_rid = -1;
538209371Smav		t->irq = -1;
539212323Smav		t->pcpu_cpu = -1;
540212323Smav		t->pcpu_misrouted = 0;
541209371Smav		t->pcpu_master = -1;
542209371Smav		t->caps = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i));
543209371Smav		t->vectors = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i) + 4);
544209371Smav		if (bootverbose) {
545209371Smav			device_printf(dev,
546209371Smav			    " t%d: irqs 0x%08x (%d)%s%s%s\n", i,
547209371Smav			    t->vectors, (t->caps & HPET_TCNF_INT_ROUTE) >> 9,
548209371Smav			    (t->caps & HPET_TCAP_FSB_INT_DEL) ? ", MSI" : "",
549209371Smav			    (t->caps & HPET_TCAP_SIZE) ? ", 64bit" : "",
550209371Smav			    (t->caps & HPET_TCAP_PER_INT) ? ", periodic" : "");
551209371Smav		}
552209371Smav	}
553159217Snjl	if (testenv("debug.acpi.hpet_test"))
554209371Smav		hpet_test(sc);
555171547Snjl	/*
556171547Snjl	 * Don't attach if the timer never increments.  Since the spec
557171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
558171547Snjl	 */
559175385Sjhb	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
560171547Snjl	DELAY(1);
561175385Sjhb	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
562171547Snjl	if (val == val2) {
563171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
564175361Sjhb		hpet_disable(sc);
565171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
566171547Snjl		return (ENXIO);
567171547Snjl	}
568208436Smav	/* Announce first HPET as timecounter. */
569208436Smav	if (device_get_unit(dev) == 0) {
570209371Smav		sc->tc.tc_get_timecount = hpet_get_timecount,
571209371Smav		sc->tc.tc_counter_mask = ~0u,
572209371Smav		sc->tc.tc_name = "HPET",
573222222Sjkim		sc->tc.tc_quality = 950,
574209371Smav		sc->tc.tc_frequency = sc->freq;
575209371Smav		sc->tc.tc_priv = sc;
576305866Skib		sc->tc.tc_fill_vdso_timehands = hpet_vdso_timehands;
577305866Skib#ifdef COMPAT_FREEBSD32
578305866Skib		sc->tc.tc_fill_vdso_timehands32 = hpet_vdso_timehands32;
579305866Skib#endif
580209371Smav		tc_init(&sc->tc);
581208436Smav	}
582209371Smav	/* If not disabled - setup and announce event timers. */
583209371Smav	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
584209371Smav	     "clock", &i) == 0 && i == 0)
585209371Smav	        return (0);
586209440Smav
587209440Smav	/* Check whether we can and want legacy routing. */
588209440Smav	sc->legacy_route = 0;
589209440Smav	resource_int_value(device_get_name(dev), device_get_unit(dev),
590209440Smav	     "legacy_route", &sc->legacy_route);
591209440Smav	if ((sc->caps & HPET_CAP_LEG_RT) == 0)
592209440Smav		sc->legacy_route = 0;
593209440Smav	if (sc->legacy_route) {
594209440Smav		sc->t[0].vectors = 0;
595209440Smav		sc->t[1].vectors = 0;
596209440Smav	}
597209440Smav
598212238Smav	/* Check what IRQs we want use. */
599212238Smav	/* By default allow any PCI IRQs. */
600212238Smav	sc->allowed_irqs = 0xffff0000;
601209371Smav	/*
602209371Smav	 * HPETs in AMD chipsets before SB800 have problems with IRQs >= 16
603209371Smav	 * Lower are also not always working for different reasons.
604209371Smav	 * SB800 fixed it, but seems do not implements level triggering
605209371Smav	 * properly, that makes it very unreliable - it freezes after any
606209371Smav	 * interrupt loss. Avoid legacy IRQs for AMD.
607209371Smav	 */
608240286Smav	if (vendor == HPET_VENDID_AMD || vendor == HPET_VENDID_AMD2)
609212238Smav		sc->allowed_irqs = 0x00000000;
610212238Smav	/*
611213302Smav	 * NVidia MCP5x chipsets have number of unexplained interrupt
612213302Smav	 * problems. For some reason, using HPET interrupts breaks HDA sound.
613213302Smav	 */
614213302Smav	if (vendor == HPET_VENDID_NVIDIA && rev <= 0x01)
615213302Smav		sc->allowed_irqs = 0x00000000;
616213302Smav	/*
617232797Smav	 * ServerWorks HT1000 reported to have problems with IRQs >= 16.
618232797Smav	 * Lower IRQs are working, but allowed mask is not set correctly.
619232797Smav	 * Legacy_route mode works fine.
620232797Smav	 */
621232797Smav	if (vendor == HPET_VENDID_SW && rev <= 0x01)
622232797Smav		sc->allowed_irqs = 0x00000000;
623232797Smav	/*
624212238Smav	 * Neither QEMU nor VirtualBox report supported IRQs correctly.
625212238Smav	 * The only way to use HPET there is to specify IRQs manually
626215473Sjhb	 * and/or use legacy_route. Legacy_route mode works on both.
627212238Smav	 */
628212238Smav	if (vm_guest)
629212238Smav		sc->allowed_irqs = 0x00000000;
630212238Smav	/* Let user override. */
631212238Smav	resource_int_value(device_get_name(dev), device_get_unit(dev),
632212238Smav	     "allowed_irqs", &sc->allowed_irqs);
633212238Smav
634212533Smav	/* Get how much per-CPU timers we should try to provide. */
635212533Smav	sc->per_cpu = 1;
636212533Smav	resource_int_value(device_get_name(dev), device_get_unit(dev),
637212533Smav	     "per_cpu", &sc->per_cpu);
638212533Smav
639212238Smav	num_msi = 0;
640212238Smav	sc->useirq = 0;
641212238Smav	/* Find IRQ vectors for all timers. */
642212238Smav	cvectors = sc->allowed_irqs & 0xffff0000;
643212238Smav	dvectors = sc->allowed_irqs & 0x0000ffff;
644212238Smav	if (sc->legacy_route)
645212238Smav		dvectors &= 0x0000fefe;
646209371Smav	for (i = 0; i < num_timers; i++) {
647209371Smav		t = &sc->t[i];
648209440Smav		if (sc->legacy_route && i < 2)
649209440Smav			t->irq = (i == 0) ? 0 : 8;
650209371Smav#ifdef DEV_APIC
651209440Smav		else if (t->caps & HPET_TCAP_FSB_INT_DEL) {
652209371Smav			if ((j = PCIB_ALLOC_MSIX(
653209371Smav			    device_get_parent(device_get_parent(dev)), dev,
654209371Smav			    &t->irq))) {
655209371Smav				device_printf(dev,
656269897Sneel				    "Can't allocate interrupt for t%d: %d\n",
657269897Sneel				    i, j);
658209440Smav			}
659209440Smav		}
660209440Smav#endif
661212238Smav		else if (dvectors & t->vectors) {
662212238Smav			t->irq = ffs(dvectors & t->vectors) - 1;
663212238Smav			dvectors &= ~(1 << t->irq);
664212238Smav		}
665209440Smav		if (t->irq >= 0) {
666216263Sjhb			t->intr_rid = hpet_find_irq_rid(dev, t->irq, t->irq);
667216490Sjhb			t->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
668216490Sjhb			    &t->intr_rid, t->irq, t->irq, 1, RF_ACTIVE);
669216490Sjhb			if (t->intr_res == NULL) {
670209440Smav				t->irq = -1;
671209440Smav				device_printf(dev,
672209440Smav				    "Can't map interrupt for t%d.\n", i);
673216490Sjhb			} else if (bus_setup_intr(dev, t->intr_res,
674216490Sjhb			    INTR_TYPE_CLK, hpet_intr_single, NULL, t,
675216490Sjhb			    &t->intr_handle) != 0) {
676209440Smav				t->irq = -1;
677209440Smav				device_printf(dev,
678209440Smav				    "Can't setup interrupt for t%d.\n", i);
679209371Smav			} else {
680209371Smav				bus_describe_intr(dev, t->intr_res,
681209371Smav				    t->intr_handle, "t%d", i);
682209371Smav				num_msi++;
683209371Smav			}
684209440Smav		}
685209440Smav		if (t->irq < 0 && (cvectors & t->vectors) != 0) {
686209371Smav			cvectors &= t->vectors;
687209371Smav			sc->useirq |= (1 << i);
688209371Smav		}
689209371Smav	}
690209440Smav	if (sc->legacy_route && sc->t[0].irq < 0 && sc->t[1].irq < 0)
691209440Smav		sc->legacy_route = 0;
692209440Smav	if (sc->legacy_route)
693209440Smav		hpet_enable(sc);
694209440Smav	/* Group timers for per-CPU operation. */
695212533Smav	num_percpu_et = min(num_msi / mp_ncpus, sc->per_cpu);
696209440Smav	num_percpu_t = num_percpu_et * mp_ncpus;
697209440Smav	pcpu_master = 0;
698209440Smav	cur_cpu = CPU_FIRST();
699209440Smav	for (i = 0; i < num_timers; i++) {
700209440Smav		t = &sc->t[i];
701209440Smav		if (t->irq >= 0 && num_percpu_t > 0) {
702209440Smav			if (cur_cpu == CPU_FIRST())
703209440Smav				pcpu_master = i;
704212323Smav			t->pcpu_cpu = cur_cpu;
705209440Smav			t->pcpu_master = pcpu_master;
706209440Smav			sc->t[pcpu_master].
707209440Smav			    pcpu_slaves[cur_cpu] = i;
708209440Smav			bus_bind_intr(dev, t->intr_res, cur_cpu);
709209440Smav			cur_cpu = CPU_NEXT(cur_cpu);
710209440Smav			num_percpu_t--;
711212238Smav		} else if (t->irq >= 0)
712212238Smav			bus_bind_intr(dev, t->intr_res, CPU_FIRST());
713209440Smav	}
714209371Smav	bus_write_4(sc->mem_res, HPET_ISR, 0xffffffff);
715209371Smav	sc->irq = -1;
716215473Sjhb	/* If at least one timer needs legacy IRQ - set it up. */
717209371Smav	if (sc->useirq) {
718209371Smav		j = i = fls(cvectors) - 1;
719209371Smav		while (j > 0 && (cvectors & (1 << (j - 1))) != 0)
720209371Smav			j--;
721216263Sjhb		sc->intr_rid = hpet_find_irq_rid(dev, j, i);
722216490Sjhb		sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
723216490Sjhb		    &sc->intr_rid, j, i, 1, RF_SHAREABLE | RF_ACTIVE);
724216490Sjhb		if (sc->intr_res == NULL)
725216490Sjhb			device_printf(dev, "Can't map interrupt.\n");
726216490Sjhb		else if (bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK,
727216490Sjhb		    hpet_intr, NULL, sc, &sc->intr_handle) != 0) {
728209371Smav			device_printf(dev, "Can't setup interrupt.\n");
729209371Smav		} else {
730209371Smav			sc->irq = rman_get_start(sc->intr_res);
731209371Smav			/* Bind IRQ to BSP to avoid live migration. */
732209371Smav			bus_bind_intr(dev, sc->intr_res, CPU_FIRST());
733209371Smav		}
734209371Smav	}
735209371Smav	/* Program and announce event timers. */
736209371Smav	for (i = 0; i < num_timers; i++) {
737209371Smav		t = &sc->t[i];
738209371Smav		t->caps &= ~(HPET_TCNF_FSB_EN | HPET_TCNF_INT_ROUTE);
739209371Smav		t->caps &= ~(HPET_TCNF_VAL_SET | HPET_TCNF_INT_ENB);
740209440Smav		t->caps &= ~(HPET_TCNF_INT_TYPE);
741209371Smav		t->caps |= HPET_TCNF_32MODE;
742209440Smav		if (t->irq >= 0 && sc->legacy_route && i < 2) {
743209440Smav			/* Legacy route doesn't need more configuration. */
744209440Smav		} else
745209371Smav#ifdef DEV_APIC
746212238Smav		if ((t->caps & HPET_TCAP_FSB_INT_DEL) && t->irq >= 0) {
747209371Smav			uint64_t addr;
748273602Srpaulo			uint32_t data;
749273602Srpaulo
750209371Smav			if (PCIB_MAP_MSI(
751209371Smav			    device_get_parent(device_get_parent(dev)), dev,
752209371Smav			    t->irq, &addr, &data) == 0) {
753209371Smav				bus_write_4(sc->mem_res,
754209371Smav				    HPET_TIMER_FSB_ADDR(i), addr);
755209371Smav				bus_write_4(sc->mem_res,
756209371Smav				    HPET_TIMER_FSB_VAL(i), data);
757209371Smav				t->caps |= HPET_TCNF_FSB_EN;
758209371Smav			} else
759209371Smav				t->irq = -2;
760209371Smav		} else
761209371Smav#endif
762212238Smav		if (t->irq >= 0)
763212238Smav			t->caps |= (t->irq << 9);
764212238Smav		else if (sc->irq >= 0 && (t->vectors & (1 << sc->irq)))
765209371Smav			t->caps |= (sc->irq << 9) | HPET_TCNF_INT_TYPE;
766209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps);
767209371Smav		/* Skip event timers without set up IRQ. */
768209371Smav		if (t->irq < 0 &&
769209371Smav		    (sc->irq < 0 || (t->vectors & (1 << sc->irq)) == 0))
770209371Smav			continue;
771209371Smav		/* Announce the reset. */
772209371Smav		if (maxhpetet == 0)
773209371Smav			t->et.et_name = "HPET";
774209371Smav		else {
775209371Smav			sprintf(t->name, "HPET%d", maxhpetet);
776209371Smav			t->et.et_name = t->name;
777209371Smav		}
778209371Smav		t->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
779209371Smav		t->et.et_quality = 450;
780209371Smav		if (t->pcpu_master >= 0) {
781209371Smav			t->et.et_flags |= ET_FLAGS_PERCPU;
782209371Smav			t->et.et_quality += 100;
783248170Smav		} else if (mp_ncpus >= 8)
784248154Smav			t->et.et_quality -= 100;
785209371Smav		if ((t->caps & HPET_TCAP_PER_INT) == 0)
786209371Smav			t->et.et_quality -= 10;
787209371Smav		t->et.et_frequency = sc->freq;
788247463Smav		t->et.et_min_period =
789247463Smav		    ((uint64_t)(HPET_MIN_CYCLES * 2) << 32) / sc->freq;
790247463Smav		t->et.et_max_period = (0xfffffffeLLU << 32) / sc->freq;
791209371Smav		t->et.et_start = hpet_start;
792209371Smav		t->et.et_stop = hpet_stop;
793209371Smav		t->et.et_priv = &sc->t[i];
794209371Smav		if (t->pcpu_master < 0 || t->pcpu_master == i) {
795209371Smav			et_register(&t->et);
796209371Smav			maxhpetet++;
797209371Smav		}
798209371Smav	}
799295841Skib	acpi_GetInteger(sc->handle, "_UID", &sc->acpi_uid);
800273598Srpaulo
801295839Skib	make_dev_args_init(&mda);
802295839Skib	mda.mda_devsw = &hpet_cdevsw;
803295839Skib	mda.mda_uid = UID_ROOT;
804295839Skib	mda.mda_gid = GID_WHEEL;
805305538Skib	mda.mda_mode = 0644;
806295839Skib	mda.mda_si_drv1 = sc;
807295839Skib	error = make_dev_s(&mda, &sc->pdev, "hpet%d", device_get_unit(dev));
808295839Skib	if (error == 0) {
809273598Srpaulo		sc->mmap_allow = 1;
810273598Srpaulo		TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow",
811273598Srpaulo		    &sc->mmap_allow);
812305538Skib		sc->mmap_allow_write = 0;
813273598Srpaulo		TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow_write",
814273598Srpaulo		    &sc->mmap_allow_write);
815273598Srpaulo		SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
816273598Srpaulo		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
817273598Srpaulo		    OID_AUTO, "mmap_allow",
818273598Srpaulo		    CTLFLAG_RW, &sc->mmap_allow, 0,
819273598Srpaulo		    "Allow userland to memory map HPET");
820273607Srpaulo		SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
821273607Srpaulo		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
822273607Srpaulo		    OID_AUTO, "mmap_allow_write",
823273607Srpaulo		    CTLFLAG_RW, &sc->mmap_allow_write, 0,
824273607Srpaulo		    "Allow userland write to the HPET register space");
825295839Skib	} else {
826295839Skib		device_printf(dev, "could not create /dev/hpet%d, error %d\n",
827295839Skib		    device_get_unit(dev), error);
828295839Skib	}
829273598Srpaulo
830159217Snjl	return (0);
831159217Snjl}
832159217Snjl
833159217Snjlstatic int
834209371Smavhpet_detach(device_t dev)
835159217Snjl{
836159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
837159217Snjl
838159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
839159217Snjl	return (EBUSY);
840159217Snjl}
841159217Snjl
842168010Snjlstatic int
843209371Smavhpet_suspend(device_t dev)
844175361Sjhb{
845212541Smav//	struct hpet_softc *sc;
846175361Sjhb
847175361Sjhb	/*
848175361Sjhb	 * Disable the timer during suspend.  The timer will not lose
849175361Sjhb	 * its state in S1 or S2, but we are required to disable
850175361Sjhb	 * it.
851175361Sjhb	 */
852212541Smav//	sc = device_get_softc(dev);
853212541Smav//	hpet_disable(sc);
854175361Sjhb
855175361Sjhb	return (0);
856175361Sjhb}
857175361Sjhb
858175361Sjhbstatic int
859209371Smavhpet_resume(device_t dev)
860168010Snjl{
861209371Smav	struct hpet_softc *sc;
862209371Smav	struct hpet_timer *t;
863209371Smav	int i;
864168010Snjl
865168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
866168010Snjl	sc = device_get_softc(dev);
867175361Sjhb	hpet_enable(sc);
868209371Smav	/* Restart event timers that were running on suspend. */
869209371Smav	for (i = 0; i < sc->num_timers; i++) {
870209371Smav		t = &sc->t[i];
871209371Smav#ifdef DEV_APIC
872209440Smav		if (t->irq >= 0 && (sc->legacy_route == 0 || i >= 2)) {
873209371Smav			uint64_t addr;
874273602Srpaulo			uint32_t data;
875273602Srpaulo
876209371Smav			if (PCIB_MAP_MSI(
877209371Smav			    device_get_parent(device_get_parent(dev)), dev,
878209371Smav			    t->irq, &addr, &data) == 0) {
879209371Smav				bus_write_4(sc->mem_res,
880209371Smav				    HPET_TIMER_FSB_ADDR(i), addr);
881209371Smav				bus_write_4(sc->mem_res,
882209371Smav				    HPET_TIMER_FSB_VAL(i), data);
883209371Smav			}
884209371Smav		}
885209371Smav#endif
886335533Savg		if (t->mode == TIMER_STOPPED)
887209371Smav			continue;
888212491Smav		t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
889335533Savg		if (t->mode == TIMER_PERIODIC &&
890335533Savg		    (t->caps & HPET_TCAP_PER_INT) != 0) {
891209371Smav			t->caps |= HPET_TCNF_TYPE;
892212491Smav			t->next += t->div;
893209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
894209371Smav			    t->caps | HPET_TCNF_VAL_SET);
895209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
896212491Smav			    t->next);
897209371Smav			bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
898209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
899209371Smav			    t->div);
900209371Smav		} else {
901212491Smav			t->next += sc->freq / 1024;
902209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
903212491Smav			    t->next);
904209371Smav		}
905209371Smav		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
906209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
907209371Smav	}
908168010Snjl	return (0);
909168010Snjl}
910168010Snjl
911159217Snjl/* Print some basic latency/rate information to assist in debugging. */
912159217Snjlstatic void
913209371Smavhpet_test(struct hpet_softc *sc)
914159217Snjl{
915151912Sphk	int i;
916151912Sphk	uint32_t u1, u2;
917151912Sphk	struct bintime b0, b1, b2;
918151912Sphk	struct timespec ts;
919151912Sphk
920151912Sphk	binuptime(&b0);
921151912Sphk	binuptime(&b0);
922151912Sphk	binuptime(&b1);
923175385Sjhb	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
924151912Sphk	for (i = 1; i < 1000; i++)
925175385Sjhb		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
926151912Sphk	binuptime(&b2);
927175385Sjhb	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
928151912Sphk
929151912Sphk	bintime_sub(&b2, &b1);
930151912Sphk	bintime_sub(&b1, &b0);
931151912Sphk	bintime_sub(&b2, &b1);
932151912Sphk	bintime2timespec(&b2, &ts);
933151912Sphk
934159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
935151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
936151912Sphk
937159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
938151912Sphk}
939151912Sphk
940209371Smav#ifdef DEV_APIC
941209371Smavstatic int
942209371Smavhpet_remap_intr(device_t dev, device_t child, u_int irq)
943209371Smav{
944209371Smav	struct hpet_softc *sc = device_get_softc(dev);
945209371Smav	struct hpet_timer *t;
946209371Smav	uint64_t addr;
947273602Srpaulo	uint32_t data;
948209371Smav	int error, i;
949209371Smav
950209371Smav	for (i = 0; i < sc->num_timers; i++) {
951209371Smav		t = &sc->t[i];
952209371Smav		if (t->irq != irq)
953209371Smav			continue;
954209371Smav		error = PCIB_MAP_MSI(
955209371Smav		    device_get_parent(device_get_parent(dev)), dev,
956209371Smav		    irq, &addr, &data);
957209371Smav		if (error)
958209371Smav			return (error);
959209371Smav		hpet_disable(sc); /* Stop timer to avoid interrupt loss. */
960209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_FSB_ADDR(i), addr);
961209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_FSB_VAL(i), data);
962209371Smav		hpet_enable(sc);
963209371Smav		return (0);
964209371Smav	}
965209371Smav	return (ENOENT);
966209371Smav}
967209371Smav#endif
968209371Smav
969209371Smavstatic device_method_t hpet_methods[] = {
970151912Sphk	/* Device interface */
971209371Smav	DEVMETHOD(device_identify, hpet_identify),
972209371Smav	DEVMETHOD(device_probe, hpet_probe),
973209371Smav	DEVMETHOD(device_attach, hpet_attach),
974209371Smav	DEVMETHOD(device_detach, hpet_detach),
975209371Smav	DEVMETHOD(device_suspend, hpet_suspend),
976209371Smav	DEVMETHOD(device_resume, hpet_resume),
977151912Sphk
978209371Smav#ifdef DEV_APIC
979209371Smav	DEVMETHOD(bus_remap_intr, hpet_remap_intr),
980209371Smav#endif
981209371Smav
982246128Ssbz	DEVMETHOD_END
983151912Sphk};
984151912Sphk
985209371Smavstatic driver_t	hpet_driver = {
986209371Smav	"hpet",
987209371Smav	hpet_methods,
988209371Smav	sizeof(struct hpet_softc),
989151912Sphk};
990151912Sphk
991209371SmavDRIVER_MODULE(hpet, acpi, hpet_driver, hpet_devclass, 0, 0);
992209371SmavMODULE_DEPEND(hpet, acpi, 1, 1, 1);
993