acpi_hpet.c revision 209371
1151912Sphk/*-
2151912Sphk * Copyright (c) 2005 Poul-Henning Kamp
3151912Sphk * All rights reserved.
4151912Sphk *
5151912Sphk * Redistribution and use in source and binary forms, with or without
6151912Sphk * modification, are permitted provided that the following conditions
7151912Sphk * are met:
8151912Sphk * 1. Redistributions of source code must retain the above copyright
9151912Sphk *    notice, this list of conditions and the following disclaimer.
10151912Sphk * 2. Redistributions in binary form must reproduce the above copyright
11151912Sphk *    notice, this list of conditions and the following disclaimer in the
12151912Sphk *    documentation and/or other materials provided with the distribution.
13151912Sphk *
14151912Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15151912Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16151912Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17151912Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18151912Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19151912Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20151912Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21151912Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22151912Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23151912Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24151912Sphk * SUCH DAMAGE.
25151912Sphk */
26151912Sphk
27151912Sphk#include <sys/cdefs.h>
28151912Sphk__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_hpet.c 209371 2010-06-20 21:33:29Z mav $");
29151912Sphk
30151912Sphk#include "opt_acpi.h"
31209371Smav#ifdef __amd64__
32209371Smav#define	DEV_APIC
33209371Smav#else
34209371Smav#include "opt_apic.h"
35209371Smav#endif
36151912Sphk#include <sys/param.h>
37159217Snjl#include <sys/bus.h>
38151912Sphk#include <sys/kernel.h>
39151912Sphk#include <sys/module.h>
40209371Smav#include <sys/proc.h>
41151912Sphk#include <sys/rman.h>
42151912Sphk#include <sys/time.h>
43209371Smav#include <sys/smp.h>
44209371Smav#include <sys/sysctl.h>
45209371Smav#include <sys/timeet.h>
46151912Sphk#include <sys/timetc.h>
47159217Snjl
48193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
49193530Sjkim#include <contrib/dev/acpica/include/accommon.h>
50193530Sjkim
51151912Sphk#include <dev/acpica/acpivar.h>
52175385Sjhb#include <dev/acpica/acpi_hpet.h>
53151912Sphk
54209371Smav#ifdef DEV_APIC
55209371Smav#include "pcib_if.h"
56209371Smav#endif
57209371Smav
58203062Savg#define HPET_VENDID_AMD		0x4353
59203062Savg#define HPET_VENDID_INTEL	0x8086
60203062Savg
61151912SphkACPI_SERIAL_DECL(hpet, "ACPI HPET support");
62151912Sphk
63209371Smavstatic devclass_t hpet_devclass;
64169574Stakawata
65151931Sscottl/* ACPI CA debugging */
66151935Sscottl#define _COMPONENT	ACPI_TIMER
67151931SscottlACPI_MODULE_NAME("HPET")
68151931Sscottl
69209371Smavstruct hpet_softc {
70151912Sphk	device_t		dev;
71209371Smav	int			mem_rid;
72209371Smav	int			intr_rid;
73209371Smav	int			irq;
74209371Smav	int			useirq;
75159217Snjl	struct resource		*mem_res;
76209371Smav	struct resource		*intr_res;
77209371Smav	void			*intr_handle;
78151912Sphk	ACPI_HANDLE		handle;
79209371Smav	uint64_t		freq;
80209371Smav	struct timecounter	tc;
81209371Smav	struct hpet_timer {
82209371Smav		struct eventtimer	et;
83209371Smav		struct hpet_softc	*sc;
84209371Smav		int			num;
85209371Smav		int			mode;
86209371Smav		int			intr_rid;
87209371Smav		int			irq;
88209371Smav		int			pcpu_master;
89209371Smav		int			pcpu_slaves[MAXCPU];
90209371Smav		struct resource		*intr_res;
91209371Smav		void			*intr_handle;
92209371Smav		uint32_t		caps;
93209371Smav		uint32_t		vectors;
94209371Smav		uint32_t		div;
95209371Smav		uint32_t		last;
96209371Smav		char			name[8];
97209371Smav	} 			t[32];
98209371Smav	int			num_timers;
99151912Sphk};
100151912Sphk
101159217Snjlstatic u_int hpet_get_timecount(struct timecounter *tc);
102209371Smavstatic void hpet_test(struct hpet_softc *sc);
103151912Sphk
104159217Snjlstatic char *hpet_ids[] = { "PNP0103", NULL };
105159217Snjl
106159217Snjlstatic u_int
107151912Sphkhpet_get_timecount(struct timecounter *tc)
108151912Sphk{
109209371Smav	struct hpet_softc *sc;
110151912Sphk
111151912Sphk	sc = tc->tc_priv;
112175385Sjhb	return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
113151912Sphk}
114151912Sphk
115175361Sjhbstatic void
116209371Smavhpet_enable(struct hpet_softc *sc)
117175361Sjhb{
118175361Sjhb	uint32_t val;
119175385Sjhb
120175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
121185103Sjkim	val &= ~HPET_CNF_LEG_RT;
122185103Sjkim	val |= HPET_CNF_ENABLE;
123185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
124175361Sjhb}
125175361Sjhb
126175361Sjhbstatic void
127209371Smavhpet_disable(struct hpet_softc *sc)
128175361Sjhb{
129175361Sjhb	uint32_t val;
130175385Sjhb
131175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
132185103Sjkim	val &= ~HPET_CNF_ENABLE;
133185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
134175361Sjhb}
135175361Sjhb
136209371Smavstatic int
137209371Smavhpet_start(struct eventtimer *et,
138209371Smav    struct bintime *first, struct bintime *period)
139209371Smav{
140209371Smav	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
141209371Smav	struct hpet_timer *t;
142209371Smav	struct hpet_softc *sc = mt->sc;
143209371Smav	uint32_t fdiv;
144209371Smav
145209371Smav	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
146209371Smav	if (period != NULL) {
147209371Smav		t->mode = 1;
148209371Smav		t->div = (sc->freq * (period->frac >> 32)) >> 32;
149209371Smav		if (period->sec != 0)
150209371Smav			t->div += sc->freq * period->sec;
151209371Smav		if (first == NULL)
152209371Smav			first = period;
153209371Smav	} else {
154209371Smav		t->mode = 2;
155209371Smav		t->div = 0;
156209371Smav	}
157209371Smav	fdiv = (sc->freq * (first->frac >> 32)) >> 32;
158209371Smav	if (first->sec != 0)
159209371Smav		fdiv += sc->freq * first->sec;
160209371Smav	t->last = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
161209371Smav	if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
162209371Smav		t->caps |= HPET_TCNF_TYPE;
163209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
164209371Smav		    t->caps | HPET_TCNF_VAL_SET);
165209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
166209371Smav		    t->last + fdiv);
167209371Smav		bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
168209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
169209371Smav		    t->div);
170209371Smav	} else {
171209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
172209371Smav		    t->last + fdiv);
173209371Smav	}
174209371Smav	t->caps |= HPET_TCNF_INT_ENB;
175209371Smav	bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
176209371Smav	bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
177209371Smav	return (0);
178209371Smav}
179209371Smav
180209371Smavstatic int
181209371Smavhpet_stop(struct eventtimer *et)
182209371Smav{
183209371Smav	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
184209371Smav	struct hpet_timer *t;
185209371Smav	struct hpet_softc *sc = mt->sc;
186209371Smav
187209371Smav	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
188209371Smav	t->mode = 0;
189209371Smav	t->caps &= ~(HPET_TCNF_INT_ENB | HPET_TCNF_TYPE);
190209371Smav	bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
191209371Smav	return (0);
192209371Smav}
193209371Smav
194209371Smavstatic int
195209371Smavhpet_intr_single(void *arg)
196209371Smav{
197209371Smav	struct hpet_timer *t = (struct hpet_timer *)arg;
198209371Smav	struct hpet_timer *mt;
199209371Smav	struct hpet_softc *sc = t->sc;
200209371Smav	uint32_t now;
201209371Smav
202209371Smav	if (t->mode == 1 &&
203209371Smav	    (t->caps & HPET_TCAP_PER_INT) == 0) {
204209371Smav		t->last += t->div;
205209371Smav		now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
206209371Smav		if ((int32_t)(now - (t->last + t->div / 2)) > 0)
207209371Smav			t->last = now - t->div / 2;
208209371Smav		bus_write_4(sc->mem_res,
209209371Smav		    HPET_TIMER_COMPARATOR(t->num), t->last + t->div);
210209371Smav	} else if (t->mode == 2)
211209371Smav		t->mode = 0;
212209371Smav	mt = (t->pcpu_master < 0) ? t : &sc->t[t->pcpu_master];
213209371Smav	if (mt->et.et_active) {
214209371Smav		mt->et.et_event_cb(&mt->et,
215209371Smav		    mt->et.et_arg ? mt->et.et_arg : curthread->td_intr_frame);
216209371Smav	}
217209371Smav	return (FILTER_HANDLED);
218209371Smav}
219209371Smav
220209371Smavstatic int
221209371Smavhpet_intr(void *arg)
222209371Smav{
223209371Smav	struct hpet_softc *sc = (struct hpet_softc *)arg;
224209371Smav	int i;
225209371Smav	uint32_t val;
226209371Smav
227209371Smav	val = bus_read_4(sc->mem_res, HPET_ISR);
228209371Smav	if (val) {
229209371Smav		bus_write_4(sc->mem_res, HPET_ISR, val);
230209371Smav		val &= sc->useirq;
231209371Smav		for (i = 0; i < sc->num_timers; i++) {
232209371Smav			if ((val & (1 << i)) == 0)
233209371Smav				continue;
234209371Smav			hpet_intr_single(&sc->t[i]);
235209371Smav		}
236209371Smav		return (FILTER_HANDLED);
237209371Smav	}
238209371Smav	return (FILTER_STRAY);
239209371Smav}
240209371Smav
241208436Smavstatic ACPI_STATUS
242209371Smavhpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
243208436Smav    void **status)
244208436Smav{
245208436Smav	char 		**ids;
246208436Smav	uint32_t	id = (uint32_t)(uintptr_t)context;
247208438Smav	uint32_t	uid = 0;
248208436Smav
249208436Smav	for (ids = hpet_ids; *ids != NULL; ids++) {
250208436Smav		if (acpi_MatchHid(handle, *ids))
251208436Smav		        break;
252208436Smav	}
253208436Smav	if (*ids == NULL)
254208436Smav		return (AE_OK);
255209371Smav	if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)) ||
256209371Smav	    id == uid)
257208436Smav		*((int *)status) = 1;
258208436Smav	return (AE_OK);
259208436Smav}
260208436Smav
261169592Snjl/* Discover the HPET via the ACPI table of the same name. */
262172489Snjlstatic void
263209371Smavhpet_identify(driver_t *driver, device_t parent)
264169574Stakawata{
265169574Stakawata	ACPI_TABLE_HPET *hpet;
266169574Stakawata	ACPI_STATUS	status;
267169574Stakawata	device_t	child;
268208436Smav	int 		i, found;
269169574Stakawata
270172489Snjl	/* Only one HPET device can be added. */
271209371Smav	if (devclass_get_device(hpet_devclass, 0))
272172489Snjl		return;
273208436Smav	for (i = 1; ; i++) {
274208436Smav		/* Search for HPET table. */
275208436Smav		status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
276208436Smav		if (ACPI_FAILURE(status))
277208436Smav			return;
278208436Smav		/* Search for HPET device with same ID. */
279208436Smav		found = 0;
280208436Smav		AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
281209371Smav		    100, hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence, (void *)&found);
282208436Smav		/* If found - let it be probed in normal way. */
283208436Smav		if (found)
284208436Smav			continue;
285208436Smav		/* If not - create it from table info. */
286209371Smav		child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "hpet", 0);
287208436Smav		if (child == NULL) {
288208436Smav			printf("%s: can't add child\n", __func__);
289208436Smav			continue;
290208436Smav		}
291208436Smav		bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
292208436Smav		    HPET_MEM_WIDTH);
293169574Stakawata	}
294169574Stakawata}
295169574Stakawata
296151912Sphkstatic int
297209371Smavhpet_probe(device_t dev)
298151912Sphk{
299159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
300159217Snjl
301169592Snjl	if (acpi_disabled("hpet"))
302151912Sphk		return (ENXIO);
303199016Savg	if (acpi_get_handle(dev) != NULL &&
304208436Smav	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL)
305169592Snjl		return (ENXIO);
306151912Sphk
307159217Snjl	device_set_desc(dev, "High Precision Event Timer");
308151912Sphk	return (0);
309151912Sphk}
310151912Sphk
311151912Sphkstatic int
312209371Smavhpet_attach(device_t dev)
313151912Sphk{
314209371Smav	struct hpet_softc *sc;
315209371Smav	struct hpet_timer *t;
316209371Smav	int i, j, num_msi, num_timers, num_percpu_et, num_percpu_t, cur_cpu;
317209371Smav	int pcpu_master;
318209371Smav	static int maxhpetet = 0;
319209371Smav	uint32_t val, val2, cvectors;
320209371Smav	uint16_t vendor, rev;
321151912Sphk
322151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
323151912Sphk
324151912Sphk	sc = device_get_softc(dev);
325151912Sphk	sc->dev = dev;
326151912Sphk	sc->handle = acpi_get_handle(dev);
327151912Sphk
328209371Smav	sc->mem_rid = 0;
329209371Smav	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
330159217Snjl	    RF_ACTIVE);
331159217Snjl	if (sc->mem_res == NULL)
332159217Snjl		return (ENOMEM);
333151912Sphk
334159217Snjl	/* Validate that we can access the whole region. */
335159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
336159217Snjl		device_printf(dev, "memory region width %ld too small\n",
337159217Snjl		    rman_get_size(sc->mem_res));
338159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
339159217Snjl		return (ENXIO);
340159217Snjl	}
341151912Sphk
342171547Snjl	/* Be sure timer is enabled. */
343175361Sjhb	hpet_enable(sc);
344171547Snjl
345159217Snjl	/* Read basic statistics about the timer. */
346175385Sjhb	val = bus_read_4(sc->mem_res, HPET_PERIOD);
347175361Sjhb	if (val == 0) {
348175361Sjhb		device_printf(dev, "invalid period\n");
349175361Sjhb		hpet_disable(sc);
350175361Sjhb		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
351175361Sjhb		return (ENXIO);
352175361Sjhb	}
353175361Sjhb
354209371Smav	sc->freq = (1000000000000000LL + val / 2) / val;
355209371Smav	val = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
356209371Smav	vendor = (val & HPET_CAP_VENDOR_ID) >> 16;
357209371Smav	rev = val & HPET_CAP_REV_ID;
358209371Smav	num_timers = 1 + ((val & HPET_CAP_NUM_TIM) >> 8);
359209371Smav	/*
360209371Smav	 * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
361209371Smav	 * Specification and provides an off by one number
362209371Smav	 * of timers/comparators.
363209371Smav	 * Additionally, they use unregistered value in VENDOR_ID field.
364209371Smav	 */
365209371Smav	if (vendor == HPET_VENDID_AMD && rev < 0x10 && num_timers > 0)
366209371Smav		num_timers--;
367209371Smav	sc->num_timers = num_timers;
368159217Snjl	if (bootverbose) {
369159217Snjl		device_printf(dev,
370209371Smav		    "vendor 0x%x, rev 0x%x, %jdHz%s, %d timers,%s\n",
371209371Smav		    vendor, rev,
372209371Smav		    sc->freq, (val & HPET_CAP_COUNT_SIZE) ? " 64bit" : "",
373209371Smav		    num_timers, (val & HPET_CAP_LEG_RT) ? " legacy route" : "");
374159217Snjl	}
375209371Smav	num_msi = 0;
376209371Smav	for (i = 0; i < num_timers; i++) {
377209371Smav		t = &sc->t[i];
378209371Smav		t->sc = sc;
379209371Smav		t->num = i;
380209371Smav		t->mode = 0;
381209371Smav		t->intr_rid = -1;
382209371Smav		t->irq = -1;
383209371Smav		t->pcpu_master = -1;
384209371Smav		t->caps = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i));
385209371Smav		t->vectors = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i) + 4);
386209371Smav		if (bootverbose) {
387209371Smav			device_printf(dev,
388209371Smav			    " t%d: irqs 0x%08x (%d)%s%s%s\n", i,
389209371Smav			    t->vectors, (t->caps & HPET_TCNF_INT_ROUTE) >> 9,
390209371Smav			    (t->caps & HPET_TCAP_FSB_INT_DEL) ? ", MSI" : "",
391209371Smav			    (t->caps & HPET_TCAP_SIZE) ? ", 64bit" : "",
392209371Smav			    (t->caps & HPET_TCAP_PER_INT) ? ", periodic" : "");
393209371Smav		}
394209371Smav#ifdef DEV_APIC
395209371Smav		if (t->caps & HPET_TCAP_FSB_INT_DEL)
396209371Smav			num_msi++;
397209371Smav#endif
398209371Smav	}
399159217Snjl	if (testenv("debug.acpi.hpet_test"))
400209371Smav		hpet_test(sc);
401171547Snjl	/*
402171547Snjl	 * Don't attach if the timer never increments.  Since the spec
403171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
404171547Snjl	 */
405175385Sjhb	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
406171547Snjl	DELAY(1);
407175385Sjhb	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
408171547Snjl	if (val == val2) {
409171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
410175361Sjhb		hpet_disable(sc);
411171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
412171547Snjl		return (ENXIO);
413171547Snjl	}
414208436Smav	/* Announce first HPET as timecounter. */
415208436Smav	if (device_get_unit(dev) == 0) {
416209371Smav		sc->tc.tc_get_timecount = hpet_get_timecount,
417209371Smav		sc->tc.tc_counter_mask = ~0u,
418209371Smav		sc->tc.tc_name = "HPET",
419209371Smav		sc->tc.tc_quality = 900,
420209371Smav		sc->tc.tc_frequency = sc->freq;
421209371Smav		sc->tc.tc_priv = sc;
422209371Smav		tc_init(&sc->tc);
423208436Smav	}
424209371Smav	/* If not disabled - setup and announce event timers. */
425209371Smav	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
426209371Smav	     "clock", &i) == 0 && i == 0)
427209371Smav	        return (0);
428209371Smav	num_percpu_et = min(num_msi / mp_ncpus, 2);
429209371Smav	num_percpu_t = num_percpu_et * mp_ncpus;
430209371Smav	cur_cpu = CPU_FIRST();
431209371Smav	pcpu_master = 0;
432209371Smav	/* Find common legacy IRQ vectors for all timers. */
433209371Smav	cvectors = 0xffff0000;
434209371Smav	/*
435209371Smav	 * HPETs in AMD chipsets before SB800 have problems with IRQs >= 16
436209371Smav	 * Lower are also not always working for different reasons.
437209371Smav	 * SB800 fixed it, but seems do not implements level triggering
438209371Smav	 * properly, that makes it very unreliable - it freezes after any
439209371Smav	 * interrupt loss. Avoid legacy IRQs for AMD.
440209371Smav	 */
441209371Smav	if (vendor == HPET_VENDID_AMD)
442209371Smav		cvectors = 0x00000000;
443209371Smav	sc->useirq = 0;
444209371Smav	for (i = 0; i < num_timers; i++) {
445209371Smav		t = &sc->t[i];
446209371Smav#ifdef DEV_APIC
447209371Smav		if (t->caps & HPET_TCAP_FSB_INT_DEL) {
448209371Smav			if ((j = PCIB_ALLOC_MSIX(
449209371Smav			    device_get_parent(device_get_parent(dev)), dev,
450209371Smav			    &t->irq))) {
451209371Smav				device_printf(dev,
452209371Smav				    "Can't allocate interrupt: %d.\n", j);
453209371Smav			} else if (!(t->intr_res =
454209371Smav			    bus_alloc_resource(dev, SYS_RES_IRQ, &t->intr_rid,
455209371Smav			    t->irq, t->irq, 1, RF_SHAREABLE | RF_ACTIVE))) {
456209371Smav				device_printf(dev, "Can't map interrupt.\n");
457209371Smav			} else if ((bus_setup_intr(dev, t->intr_res,
458209371Smav			    INTR_MPSAFE | INTR_TYPE_CLK,
459209371Smav			    (driver_filter_t *)hpet_intr_single, NULL,
460209371Smav			    t, &t->intr_handle))) {
461209371Smav				device_printf(dev, "Can't setup interrupt.\n");
462209371Smav			} else {
463209371Smav				bus_describe_intr(dev, t->intr_res,
464209371Smav				    t->intr_handle, "t%d", i);
465209371Smav				num_msi++;
466209371Smav				if (num_percpu_t > 0) {
467209371Smav					if (cur_cpu == CPU_FIRST())
468209371Smav						pcpu_master = i;
469209371Smav					t->pcpu_master = pcpu_master;
470209371Smav					sc->t[pcpu_master].
471209371Smav					    pcpu_slaves[cur_cpu] = i;
472209371Smav					bus_bind_intr(dev, t->intr_res, cur_cpu);
473209371Smav					cur_cpu = CPU_NEXT(cur_cpu);
474209371Smav					num_percpu_t--;
475209371Smav				}
476209371Smav			}
477209371Smav		} else
478209371Smav#endif
479209371Smav		if ((cvectors & t->vectors) != 0) {
480209371Smav			cvectors &= t->vectors;
481209371Smav			sc->useirq |= (1 << i);
482209371Smav		}
483209371Smav	}
484209371Smav	bus_write_4(sc->mem_res, HPET_ISR, 0xffffffff);
485209371Smav	sc->irq = -1;
486209371Smav	sc->intr_rid = -1;
487209371Smav	/* If at least one timer needs legacy IRQ - setup it. */
488209371Smav	if (sc->useirq) {
489209371Smav		j = i = fls(cvectors) - 1;
490209371Smav		while (j > 0 && (cvectors & (1 << (j - 1))) != 0)
491209371Smav			j--;
492209371Smav		if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
493209371Smav		    &sc->intr_rid, j, i, 1, RF_SHAREABLE | RF_ACTIVE)))
494209371Smav			device_printf(dev,"Can't map interrupt.\n");
495209371Smav		else if ((bus_setup_intr(dev, sc->intr_res,
496209371Smav		    INTR_MPSAFE | INTR_TYPE_CLK,
497209371Smav		    (driver_filter_t *)hpet_intr, NULL,
498209371Smav		    sc, &sc->intr_handle))) {
499209371Smav			device_printf(dev, "Can't setup interrupt.\n");
500209371Smav		} else {
501209371Smav			sc->irq = rman_get_start(sc->intr_res);
502209371Smav			/* Bind IRQ to BSP to avoid live migration. */
503209371Smav			bus_bind_intr(dev, sc->intr_res, CPU_FIRST());
504209371Smav		}
505209371Smav	}
506209371Smav	/* Program and announce event timers. */
507209371Smav	for (i = 0; i < num_timers; i++) {
508209371Smav		t = &sc->t[i];
509209371Smav		t->caps &= ~(HPET_TCNF_FSB_EN | HPET_TCNF_INT_ROUTE);
510209371Smav		t->caps &= ~(HPET_TCNF_VAL_SET | HPET_TCNF_INT_ENB);
511209371Smav		t->caps |= HPET_TCNF_32MODE;
512209371Smav#ifdef DEV_APIC
513209371Smav		if (t->irq >= 0) {
514209371Smav			uint64_t addr;
515209371Smav			uint32_t data;
516209371Smav
517209371Smav			if (PCIB_MAP_MSI(
518209371Smav			    device_get_parent(device_get_parent(dev)), dev,
519209371Smav			    t->irq, &addr, &data) == 0) {
520209371Smav				bus_write_4(sc->mem_res,
521209371Smav				    HPET_TIMER_FSB_ADDR(i), addr);
522209371Smav				bus_write_4(sc->mem_res,
523209371Smav				    HPET_TIMER_FSB_VAL(i), data);
524209371Smav				t->caps |= HPET_TCNF_FSB_EN;
525209371Smav			} else
526209371Smav				t->irq = -2;
527209371Smav		} else
528209371Smav#endif
529209371Smav			t->caps |= (sc->irq << 9) | HPET_TCNF_INT_TYPE;
530209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps);
531209371Smav		/* Skip event timers without set up IRQ. */
532209371Smav		if (t->irq < 0 &&
533209371Smav		    (sc->irq < 0 || (t->vectors & (1 << sc->irq)) == 0))
534209371Smav			continue;
535209371Smav		/* Announce the reset. */
536209371Smav		if (maxhpetet == 0)
537209371Smav			t->et.et_name = "HPET";
538209371Smav		else {
539209371Smav			sprintf(t->name, "HPET%d", maxhpetet);
540209371Smav			t->et.et_name = t->name;
541209371Smav		}
542209371Smav		t->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
543209371Smav		t->et.et_quality = 450;
544209371Smav		if (t->pcpu_master >= 0) {
545209371Smav			t->et.et_flags |= ET_FLAGS_PERCPU;
546209371Smav			t->et.et_quality += 100;
547209371Smav		}
548209371Smav		if ((t->caps & HPET_TCAP_PER_INT) == 0)
549209371Smav			t->et.et_quality -= 10;
550209371Smav		t->et.et_frequency = sc->freq;
551209371Smav		t->et.et_start = hpet_start;
552209371Smav		t->et.et_stop = hpet_stop;
553209371Smav		t->et.et_priv = &sc->t[i];
554209371Smav		if (t->pcpu_master < 0 || t->pcpu_master == i) {
555209371Smav			et_register(&t->et);
556209371Smav			maxhpetet++;
557209371Smav		}
558209371Smav	}
559159217Snjl	return (0);
560159217Snjl}
561159217Snjl
562159217Snjlstatic int
563209371Smavhpet_detach(device_t dev)
564159217Snjl{
565159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
566159217Snjl
567159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
568159217Snjl	return (EBUSY);
569159217Snjl}
570159217Snjl
571168010Snjlstatic int
572209371Smavhpet_suspend(device_t dev)
573175361Sjhb{
574209371Smav	struct hpet_softc *sc;
575175361Sjhb
576175361Sjhb	/*
577175361Sjhb	 * Disable the timer during suspend.  The timer will not lose
578175361Sjhb	 * its state in S1 or S2, but we are required to disable
579175361Sjhb	 * it.
580175361Sjhb	 */
581175361Sjhb	sc = device_get_softc(dev);
582175361Sjhb	hpet_disable(sc);
583175361Sjhb
584175361Sjhb	return (0);
585175361Sjhb}
586175361Sjhb
587175361Sjhbstatic int
588209371Smavhpet_resume(device_t dev)
589168010Snjl{
590209371Smav	struct hpet_softc *sc;
591209371Smav	struct hpet_timer *t;
592209371Smav	int i;
593168010Snjl
594168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
595168010Snjl	sc = device_get_softc(dev);
596175361Sjhb	hpet_enable(sc);
597209371Smav	/* Restart event timers that were running on suspend. */
598209371Smav	for (i = 0; i < sc->num_timers; i++) {
599209371Smav		t = &sc->t[i];
600209371Smav#ifdef DEV_APIC
601209371Smav		if (t->irq >= 0) {
602209371Smav			uint64_t addr;
603209371Smav			uint32_t data;
604209371Smav
605209371Smav			if (PCIB_MAP_MSI(
606209371Smav			    device_get_parent(device_get_parent(dev)), dev,
607209371Smav			    t->irq, &addr, &data) == 0) {
608209371Smav				bus_write_4(sc->mem_res,
609209371Smav				    HPET_TIMER_FSB_ADDR(i), addr);
610209371Smav				bus_write_4(sc->mem_res,
611209371Smav				    HPET_TIMER_FSB_VAL(i), data);
612209371Smav			}
613209371Smav		}
614209371Smav#endif
615209371Smav		if (t->mode == 0)
616209371Smav			continue;
617209371Smav		t->last = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
618209371Smav		if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
619209371Smav			t->caps |= HPET_TCNF_TYPE;
620209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
621209371Smav			    t->caps | HPET_TCNF_VAL_SET);
622209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
623209371Smav			    t->last + t->div);
624209371Smav			bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
625209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
626209371Smav			    t->div);
627209371Smav		} else {
628209371Smav			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
629209371Smav			    t->last + sc->freq / 1024);
630209371Smav		}
631209371Smav		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
632209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
633209371Smav	}
634168010Snjl	return (0);
635168010Snjl}
636168010Snjl
637159217Snjl/* Print some basic latency/rate information to assist in debugging. */
638159217Snjlstatic void
639209371Smavhpet_test(struct hpet_softc *sc)
640159217Snjl{
641151912Sphk	int i;
642151912Sphk	uint32_t u1, u2;
643151912Sphk	struct bintime b0, b1, b2;
644151912Sphk	struct timespec ts;
645151912Sphk
646151912Sphk	binuptime(&b0);
647151912Sphk	binuptime(&b0);
648151912Sphk	binuptime(&b1);
649175385Sjhb	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
650151912Sphk	for (i = 1; i < 1000; i++)
651175385Sjhb		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
652151912Sphk	binuptime(&b2);
653175385Sjhb	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
654151912Sphk
655151912Sphk	bintime_sub(&b2, &b1);
656151912Sphk	bintime_sub(&b1, &b0);
657151912Sphk	bintime_sub(&b2, &b1);
658151912Sphk	bintime2timespec(&b2, &ts);
659151912Sphk
660159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
661151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
662151912Sphk
663159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
664151912Sphk}
665151912Sphk
666209371Smav#ifdef DEV_APIC
667209371Smavstatic int
668209371Smavhpet_remap_intr(device_t dev, device_t child, u_int irq)
669209371Smav{
670209371Smav	struct hpet_softc *sc = device_get_softc(dev);
671209371Smav	struct hpet_timer *t;
672209371Smav	uint64_t addr;
673209371Smav	uint32_t data;
674209371Smav	int error, i;
675209371Smav
676209371Smav	for (i = 0; i < sc->num_timers; i++) {
677209371Smav		t = &sc->t[i];
678209371Smav		if (t->irq != irq)
679209371Smav			continue;
680209371Smav		error = PCIB_MAP_MSI(
681209371Smav		    device_get_parent(device_get_parent(dev)), dev,
682209371Smav		    irq, &addr, &data);
683209371Smav		if (error)
684209371Smav			return (error);
685209371Smav		hpet_disable(sc); /* Stop timer to avoid interrupt loss. */
686209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_FSB_ADDR(i), addr);
687209371Smav		bus_write_4(sc->mem_res, HPET_TIMER_FSB_VAL(i), data);
688209371Smav		hpet_enable(sc);
689209371Smav		return (0);
690209371Smav	}
691209371Smav	return (ENOENT);
692209371Smav}
693209371Smav#endif
694209371Smav
695209371Smavstatic device_method_t hpet_methods[] = {
696151912Sphk	/* Device interface */
697209371Smav	DEVMETHOD(device_identify, hpet_identify),
698209371Smav	DEVMETHOD(device_probe, hpet_probe),
699209371Smav	DEVMETHOD(device_attach, hpet_attach),
700209371Smav	DEVMETHOD(device_detach, hpet_detach),
701209371Smav	DEVMETHOD(device_suspend, hpet_suspend),
702209371Smav	DEVMETHOD(device_resume, hpet_resume),
703151912Sphk
704209371Smav#ifdef DEV_APIC
705209371Smav	DEVMETHOD(bus_remap_intr, hpet_remap_intr),
706209371Smav#endif
707209371Smav
708151912Sphk	{0, 0}
709151912Sphk};
710151912Sphk
711209371Smavstatic driver_t	hpet_driver = {
712209371Smav	"hpet",
713209371Smav	hpet_methods,
714209371Smav	sizeof(struct hpet_softc),
715151912Sphk};
716151912Sphk
717209371SmavDRIVER_MODULE(hpet, acpi, hpet_driver, hpet_devclass, 0, 0);
718209371SmavMODULE_DEPEND(hpet, acpi, 1, 1, 1);
719