acpi_hpet.c revision 212491
1331722Seadler/*-
2169400Sscottl * Copyright (c) 2005 Poul-Henning Kamp
3144966Svkashyap * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
4144966Svkashyap * All rights reserved.
5144966Svkashyap *
6144966Svkashyap * Redistribution and use in source and binary forms, with or without
7144966Svkashyap * modification, are permitted provided that the following conditions
8144966Svkashyap * are met:
9144966Svkashyap * 1. Redistributions of source code must retain the above copyright
10144966Svkashyap *    notice, this list of conditions and the following disclaimer.
11144966Svkashyap * 2. Redistributions in binary form must reproduce the above copyright
12144966Svkashyap *    notice, this list of conditions and the following disclaimer in the
13144966Svkashyap *    documentation and/or other materials provided with the distribution.
14144966Svkashyap *
15144966Svkashyap * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16144966Svkashyap * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17144966Svkashyap * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18144966Svkashyap * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19144966Svkashyap * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20144966Svkashyap * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21144966Svkashyap * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22144966Svkashyap * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23144966Svkashyap * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24144966Svkashyap * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25144966Svkashyap * SUCH DAMAGE.
26144966Svkashyap */
27144966Svkashyap
28144966Svkashyap#include <sys/cdefs.h>
29144966Svkashyap__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_hpet.c 212491 2010-09-12 11:11:53Z mav $");
30144966Svkashyap
31144966Svkashyap#include "opt_acpi.h"
32144966Svkashyap#if defined(__amd64__) || defined(__ia64__)
33144966Svkashyap#define	DEV_APIC
34169400Sscottl#else
35172496Sscottl#include "opt_apic.h"
36144966Svkashyap#endif
37144966Svkashyap#include <sys/param.h>
38144966Svkashyap#include <sys/bus.h>
39144966Svkashyap#include <sys/kernel.h>
40144966Svkashyap#include <sys/module.h>
41144966Svkashyap#include <sys/proc.h>
42144966Svkashyap#include <sys/rman.h>
43144966Svkashyap#include <sys/time.h>
44149968Sobrien#include <sys/smp.h>
45144966Svkashyap#include <sys/sysctl.h>
46144966Svkashyap#include <sys/timeet.h>
47144966Svkashyap#include <sys/timetc.h>
48144966Svkashyap
49144966Svkashyap#include <contrib/dev/acpica/include/acpi.h>
50144966Svkashyap#include <contrib/dev/acpica/include/accommon.h>
51144966Svkashyap
52144966Svkashyap#include <dev/acpica/acpivar.h>
53144966Svkashyap#include <dev/acpica/acpi_hpet.h>
54144966Svkashyap
55144966Svkashyap#ifdef DEV_APIC
56144966Svkashyap#include "pcib_if.h"
57144966Svkashyap#endif
58144966Svkashyap
59144966Svkashyap#define HPET_VENDID_AMD		0x4353
60144966Svkashyap#define HPET_VENDID_INTEL	0x8086
61144966Svkashyap
62144966SvkashyapACPI_SERIAL_DECL(hpet, "ACPI HPET support");
63144966Svkashyap
64144966Svkashyapstatic devclass_t hpet_devclass;
65144966Svkashyap
66144966Svkashyap/* ACPI CA debugging */
67144966Svkashyap#define _COMPONENT	ACPI_TIMER
68144966SvkashyapACPI_MODULE_NAME("HPET")
69144966Svkashyap
70144966Svkashyapstruct hpet_softc {
71144966Svkashyap	device_t		dev;
72144966Svkashyap	int			mem_rid;
73144966Svkashyap	int			intr_rid;
74144966Svkashyap	int			irq;
75144966Svkashyap	int			useirq;
76144966Svkashyap	int			legacy_route;
77144966Svkashyap	uint32_t		allowed_irqs;
78144966Svkashyap	struct resource		*mem_res;
79144966Svkashyap	struct resource		*intr_res;
80144966Svkashyap	void			*intr_handle;
81144966Svkashyap	ACPI_HANDLE		handle;
82144966Svkashyap	uint64_t		freq;
83212008Sdelphij	uint32_t		caps;
84144966Svkashyap	struct timecounter	tc;
85144966Svkashyap	struct hpet_timer {
86144966Svkashyap		struct eventtimer	et;
87144966Svkashyap		struct hpet_softc	*sc;
88144966Svkashyap		int			num;
89144966Svkashyap		int			mode;
90144966Svkashyap		int			intr_rid;
91144966Svkashyap		int			irq;
92144966Svkashyap		int			pcpu_cpu;
93144966Svkashyap		int			pcpu_misrouted;
94208969Sdelphij		int			pcpu_master;
95144966Svkashyap		int			pcpu_slaves[MAXCPU];
96208969Sdelphij		struct resource		*intr_res;
97208969Sdelphij		void			*intr_handle;
98144966Svkashyap		uint32_t		caps;
99144966Svkashyap		uint32_t		vectors;
100144966Svkashyap		uint32_t		div;
101172496Sscottl		uint32_t		next;
102208969Sdelphij		char			name[8];
103144966Svkashyap	} 			t[32];
104144966Svkashyap	int			num_timers;
105144966Svkashyap};
106144966Svkashyap
107144966Svkashyapstatic u_int hpet_get_timecount(struct timecounter *tc);
108144966Svkashyapstatic void hpet_test(struct hpet_softc *sc);
109144966Svkashyap
110144966Svkashyapstatic char *hpet_ids[] = { "PNP0103", NULL };
111144966Svkashyap
112144966Svkashyapstatic u_int
113144966Svkashyaphpet_get_timecount(struct timecounter *tc)
114144966Svkashyap{
115144966Svkashyap	struct hpet_softc *sc;
116144966Svkashyap
117144966Svkashyap	sc = tc->tc_priv;
118172496Sscottl	return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
119170872Sscottl}
120144966Svkashyap
121144966Svkashyapstatic void
122144966Svkashyaphpet_enable(struct hpet_softc *sc)
123144966Svkashyap{
124144966Svkashyap	uint32_t val;
125144966Svkashyap
126144966Svkashyap	val = bus_read_4(sc->mem_res, HPET_CONFIG);
127144966Svkashyap	if (sc->legacy_route)
128172496Sscottl		val |= HPET_CNF_LEG_RT;
129144966Svkashyap	else
130144966Svkashyap		val &= ~HPET_CNF_LEG_RT;
131144966Svkashyap	val |= HPET_CNF_ENABLE;
132144966Svkashyap	bus_write_4(sc->mem_res, HPET_CONFIG, val);
133144966Svkashyap}
134144966Svkashyap
135144966Svkashyapstatic void
136144966Svkashyaphpet_disable(struct hpet_softc *sc)
137144966Svkashyap{
138144966Svkashyap	uint32_t val;
139144966Svkashyap
140144966Svkashyap	val = bus_read_4(sc->mem_res, HPET_CONFIG);
141144966Svkashyap	val &= ~HPET_CNF_ENABLE;
142144966Svkashyap	bus_write_4(sc->mem_res, HPET_CONFIG, val);
143144966Svkashyap}
144144966Svkashyap
145144966Svkashyapstatic int
146172496Sscottlhpet_start(struct eventtimer *et,
147144966Svkashyap    struct bintime *first, struct bintime *period)
148144966Svkashyap{
149172496Sscottl	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
150144966Svkashyap	struct hpet_timer *t;
151144966Svkashyap	struct hpet_softc *sc = mt->sc;
152144966Svkashyap	uint32_t fdiv, now;
153144966Svkashyap
154144966Svkashyap	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
155144966Svkashyap	if (period != NULL) {
156144966Svkashyap		t->mode = 1;
157144966Svkashyap		t->div = (sc->freq * (period->frac >> 32)) >> 32;
158144966Svkashyap		if (period->sec != 0)
159144966Svkashyap			t->div += sc->freq * period->sec;
160144966Svkashyap	} else {
161144966Svkashyap		t->mode = 2;
162144966Svkashyap		t->div = 0;
163144966Svkashyap	}
164144966Svkashyap	if (first != NULL) {
165144966Svkashyap		fdiv = (sc->freq * (first->frac >> 32)) >> 32;
166144966Svkashyap		if (first->sec != 0)
167144966Svkashyap			fdiv += sc->freq * first->sec;
168144966Svkashyap	} else
169144966Svkashyap		fdiv = t->div;
170172496Sscottl	if (t->irq < 0)
171172496Sscottl		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
172144966Svkashyap	t->caps |= HPET_TCNF_INT_ENB;
173144966Svkashyap	now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
174144966Svkashyaprestart:
175144966Svkashyap	t->next = now + fdiv;
176144966Svkashyap	if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
177144966Svkashyap		t->caps |= HPET_TCNF_TYPE;
178144966Svkashyap		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
179172496Sscottl		    t->caps | HPET_TCNF_VAL_SET);
180172496Sscottl		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
181144966Svkashyap		    t->next);
182144966Svkashyap		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
183144966Svkashyap		    t->div);
184144966Svkashyap	} else {
185144966Svkashyap		t->caps &= ~HPET_TCNF_TYPE;
186144966Svkashyap		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
187144966Svkashyap		    t->caps);
188144966Svkashyap		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
189144966Svkashyap		    t->next);
190144966Svkashyap	}
191144966Svkashyap	if (fdiv < 5000) {
192144966Svkashyap		bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
193144966Svkashyap		now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
194144966Svkashyap		if ((int32_t)(now - t->next) >= 0) {
195144966Svkashyap			fdiv *= 2;
196144966Svkashyap			goto restart;
197144966Svkashyap		}
198144966Svkashyap	}
199144966Svkashyap	return (0);
200144966Svkashyap}
201144966Svkashyap
202144966Svkashyapstatic int
203144966Svkashyaphpet_stop(struct eventtimer *et)
204144966Svkashyap{
205144966Svkashyap	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
206144966Svkashyap	struct hpet_timer *t;
207144966Svkashyap	struct hpet_softc *sc = mt->sc;
208144966Svkashyap
209144966Svkashyap	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
210257381Snwhitehorn	t->mode = 0;
211257381Snwhitehorn	t->caps &= ~(HPET_TCNF_INT_ENB | HPET_TCNF_TYPE);
212257381Snwhitehorn	bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
213144966Svkashyap	return (0);
214144966Svkashyap}
215144966Svkashyap
216144966Svkashyapstatic int
217144966Svkashyaphpet_intr_single(void *arg)
218257381Snwhitehorn{
219257381Snwhitehorn	struct hpet_timer *t = (struct hpet_timer *)arg;
220257381Snwhitehorn	struct hpet_timer *mt;
221144966Svkashyap	struct hpet_softc *sc = t->sc;
222144966Svkashyap	uint32_t now;
223144966Svkashyap
224144966Svkashyap	if (t->mode == 0)
225144966Svkashyap		return (FILTER_STRAY);
226144966Svkashyap	/* Check that per-CPU timer interrupt reached right CPU. */
227144966Svkashyap	if (t->pcpu_cpu >= 0 && t->pcpu_cpu != curcpu) {
228144966Svkashyap		if ((++t->pcpu_misrouted) % 32 == 0) {
229144966Svkashyap			printf("HPET interrupt routed to the wrong CPU"
230144966Svkashyap			    " (timer %d CPU %d -> %d)!\n",
231144966Svkashyap			    t->num, t->pcpu_cpu, curcpu);
232208969Sdelphij		}
233144966Svkashyap
234144966Svkashyap		/*
235144966Svkashyap		 * Reload timer, hoping that next time may be more lucky
236144966Svkashyap		 * (system will manage proper interrupt binding).
237144966Svkashyap		 */
238144966Svkashyap		if ((t->mode == 1 && (t->caps & HPET_TCAP_PER_INT) == 0) ||
239144966Svkashyap		    t->mode == 2) {
240144966Svkashyap			t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER) +
241144966Svkashyap			    sc->freq / 8;
242144966Svkashyap			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
243144966Svkashyap			    t->next);
244144966Svkashyap		}
245144966Svkashyap		return (FILTER_HANDLED);
246144966Svkashyap	}
247144966Svkashyap	if (t->mode == 1 &&
248144966Svkashyap	    (t->caps & HPET_TCAP_PER_INT) == 0) {
249144966Svkashyap		t->next += t->div;
250144966Svkashyap		now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
251144966Svkashyap		if ((int32_t)((now + t->div / 2) - t->next) > 0)
252144966Svkashyap			t->next = now + t->div / 2;
253144966Svkashyap		bus_write_4(sc->mem_res,
254144966Svkashyap		    HPET_TIMER_COMPARATOR(t->num), t->next);
255144966Svkashyap	} else if (t->mode == 2)
256144966Svkashyap		t->mode = 0;
257144966Svkashyap	mt = (t->pcpu_master < 0) ? t : &sc->t[t->pcpu_master];
258144966Svkashyap	if (mt->et.et_active)
259144966Svkashyap		mt->et.et_event_cb(&mt->et, mt->et.et_arg);
260144966Svkashyap	return (FILTER_HANDLED);
261144966Svkashyap}
262144966Svkashyap
263144966Svkashyapstatic int
264144966Svkashyaphpet_intr(void *arg)
265144966Svkashyap{
266246713Skib	struct hpet_softc *sc = (struct hpet_softc *)arg;
267246713Skib	int i;
268144966Svkashyap	uint32_t val;
269144966Svkashyap
270246713Skib	val = bus_read_4(sc->mem_res, HPET_ISR);
271246713Skib	if (val) {
272246713Skib		bus_write_4(sc->mem_res, HPET_ISR, val);
273246713Skib		val &= sc->useirq;
274144966Svkashyap		for (i = 0; i < sc->num_timers; i++) {
275144966Svkashyap			if ((val & (1 << i)) == 0)
276144966Svkashyap				continue;
277144966Svkashyap			hpet_intr_single(&sc->t[i]);
278248583Skib		}
279248583Skib		return (FILTER_HANDLED);
280248583Skib	}
281248583Skib	return (FILTER_STRAY);
282248583Skib}
283248583Skib
284248583Skibstatic ACPI_STATUS
285212008Sdelphijhpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
286212008Sdelphij    void **status)
287144966Svkashyap{
288144966Svkashyap	char 		**ids;
289144966Svkashyap	uint32_t	id = (uint32_t)(uintptr_t)context;
290144966Svkashyap	uint32_t	uid = 0;
291144966Svkashyap
292212008Sdelphij	for (ids = hpet_ids; *ids != NULL; ids++) {
293212008Sdelphij		if (acpi_MatchHid(handle, *ids))
294212008Sdelphij		        break;
295212008Sdelphij	}
296212008Sdelphij	if (*ids == NULL)
297212008Sdelphij		return (AE_OK);
298144966Svkashyap	if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)) ||
299144966Svkashyap	    id == uid)
300144966Svkashyap		*((int *)status) = 1;
301144966Svkashyap	return (AE_OK);
302144966Svkashyap}
303144966Svkashyap
304144966Svkashyap/* Discover the HPET via the ACPI table of the same name. */
305144966Svkashyapstatic void
306144966Svkashyaphpet_identify(driver_t *driver, device_t parent)
307144966Svkashyap{
308144966Svkashyap	ACPI_TABLE_HPET *hpet;
309144966Svkashyap	ACPI_STATUS	status;
310144966Svkashyap	device_t	child;
311144966Svkashyap	int 		i, found;
312144966Svkashyap
313144966Svkashyap	/* Only one HPET device can be added. */
314144966Svkashyap	if (devclass_get_device(hpet_devclass, 0))
315144966Svkashyap		return;
316144966Svkashyap	for (i = 1; ; i++) {
317144966Svkashyap		/* Search for HPET table. */
318144966Svkashyap		status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
319144966Svkashyap		if (ACPI_FAILURE(status))
320144966Svkashyap			return;
321144966Svkashyap		/* Search for HPET device with same ID. */
322144966Svkashyap		found = 0;
323208969Sdelphij		AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
324208969Sdelphij		    100, hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence, (void *)&found);
325208969Sdelphij		/* If found - let it be probed in normal way. */
326144966Svkashyap		if (found)
327144966Svkashyap			continue;
328144966Svkashyap		/* If not - create it from table info. */
329144966Svkashyap		child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "hpet", 0);
330212008Sdelphij		if (child == NULL) {
331144966Svkashyap			printf("%s: can't add child\n", __func__);
332144966Svkashyap			continue;
333144966Svkashyap		}
334144966Svkashyap		bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
335212008Sdelphij		    HPET_MEM_WIDTH);
336212008Sdelphij	}
337212008Sdelphij}
338212008Sdelphij
339212008Sdelphijstatic int
340212008Sdelphijhpet_probe(device_t dev)
341212008Sdelphij{
342212008Sdelphij	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
343212008Sdelphij
344144966Svkashyap	if (acpi_disabled("hpet"))
345208969Sdelphij		return (ENXIO);
346144966Svkashyap	if (acpi_get_handle(dev) != NULL &&
347144966Svkashyap	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL)
348144966Svkashyap		return (ENXIO);
349144966Svkashyap
350144966Svkashyap	device_set_desc(dev, "High Precision Event Timer");
351144966Svkashyap	return (0);
352144966Svkashyap}
353144966Svkashyap
354144966Svkashyapstatic int
355144966Svkashyaphpet_attach(device_t dev)
356144966Svkashyap{
357144966Svkashyap	struct hpet_softc *sc;
358144966Svkashyap	struct hpet_timer *t;
359212008Sdelphij	int i, j, num_msi, num_timers, num_percpu_et, num_percpu_t, cur_cpu;
360144966Svkashyap	int pcpu_master;
361144966Svkashyap	static int maxhpetet = 0;
362144966Svkashyap	uint32_t val, val2, cvectors, dvectors;
363144966Svkashyap	uint16_t vendor, rev;
364144966Svkashyap
365212008Sdelphij	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
366212008Sdelphij
367144966Svkashyap	sc = device_get_softc(dev);
368144966Svkashyap	sc->dev = dev;
369144966Svkashyap	sc->handle = acpi_get_handle(dev);
370144966Svkashyap
371144966Svkashyap	sc->mem_rid = 0;
372144966Svkashyap	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
373144966Svkashyap	    RF_ACTIVE);
374144966Svkashyap	if (sc->mem_res == NULL)
375144966Svkashyap		return (ENOMEM);
376144966Svkashyap
377144966Svkashyap	/* Validate that we can access the whole region. */
378144966Svkashyap	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
379144966Svkashyap		device_printf(dev, "memory region width %ld too small\n",
380144966Svkashyap		    rman_get_size(sc->mem_res));
381144966Svkashyap		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
382144966Svkashyap		return (ENXIO);
383144966Svkashyap	}
384163816Smjacob
385163816Smjacob	/* Be sure timer is enabled. */
386163816Smjacob	hpet_enable(sc);
387163816Smjacob
388144966Svkashyap	/* Read basic statistics about the timer. */
389163816Smjacob	val = bus_read_4(sc->mem_res, HPET_PERIOD);
390163816Smjacob	if (val == 0) {
391163816Smjacob		device_printf(dev, "invalid period\n");
392163816Smjacob		hpet_disable(sc);
393163816Smjacob		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
394163816Smjacob		return (ENXIO);
395163816Smjacob	}
396163816Smjacob
397163816Smjacob	sc->freq = (1000000000000000LL + val / 2) / val;
398163816Smjacob	sc->caps = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
399144966Svkashyap	vendor = (sc->caps & HPET_CAP_VENDOR_ID) >> 16;
400144966Svkashyap	rev = sc->caps & HPET_CAP_REV_ID;
401144966Svkashyap	num_timers = 1 + ((sc->caps & HPET_CAP_NUM_TIM) >> 8);
402144966Svkashyap	/*
403144966Svkashyap	 * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
404144966Svkashyap	 * Specification and provides an off by one number
405144966Svkashyap	 * of timers/comparators.
406144966Svkashyap	 * Additionally, they use unregistered value in VENDOR_ID field.
407144966Svkashyap	 */
408144966Svkashyap	if (vendor == HPET_VENDID_AMD && rev < 0x10 && num_timers > 0)
409144966Svkashyap		num_timers--;
410144966Svkashyap	sc->num_timers = num_timers;
411144966Svkashyap	if (bootverbose) {
412144966Svkashyap		device_printf(dev,
413144966Svkashyap		    "vendor 0x%x, rev 0x%x, %jdHz%s, %d timers,%s\n",
414144966Svkashyap		    vendor, rev, sc->freq,
415144966Svkashyap		    (sc->caps & HPET_CAP_COUNT_SIZE) ? " 64bit" : "",
416144966Svkashyap		    num_timers,
417144966Svkashyap		    (sc->caps & HPET_CAP_LEG_RT) ? " legacy route" : "");
418144966Svkashyap	}
419144966Svkashyap	for (i = 0; i < num_timers; i++) {
420144966Svkashyap		t = &sc->t[i];
421144966Svkashyap		t->sc = sc;
422144966Svkashyap		t->num = i;
423144966Svkashyap		t->mode = 0;
424144966Svkashyap		t->intr_rid = -1;
425152213Svkashyap		t->irq = -1;
426144966Svkashyap		t->pcpu_cpu = -1;
427315812Smav		t->pcpu_misrouted = 0;
428315812Smav		t->pcpu_master = -1;
429315812Smav		t->caps = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i));
430335138Smav		t->vectors = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i) + 4);
431335138Smav		if (bootverbose) {
432335138Smav			device_printf(dev,
433335138Smav			    " t%d: irqs 0x%08x (%d)%s%s%s\n", i,
434335138Smav			    t->vectors, (t->caps & HPET_TCNF_INT_ROUTE) >> 9,
435144966Svkashyap			    (t->caps & HPET_TCAP_FSB_INT_DEL) ? ", MSI" : "",
436144966Svkashyap			    (t->caps & HPET_TCAP_SIZE) ? ", 64bit" : "",
437144966Svkashyap			    (t->caps & HPET_TCAP_PER_INT) ? ", periodic" : "");
438144966Svkashyap		}
439144966Svkashyap	}
440144966Svkashyap	if (testenv("debug.acpi.hpet_test"))
441144966Svkashyap		hpet_test(sc);
442144966Svkashyap	/*
443144966Svkashyap	 * Don't attach if the timer never increments.  Since the spec
444144966Svkashyap	 * requires it to be at least 10 MHz, it has to change in 1 us.
445144966Svkashyap	 */
446144966Svkashyap	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
447144966Svkashyap	DELAY(1);
448144966Svkashyap	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
449144966Svkashyap	if (val == val2) {
450144966Svkashyap		device_printf(dev, "HPET never increments, disabling\n");
451144966Svkashyap		hpet_disable(sc);
452144966Svkashyap		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
453144966Svkashyap		return (ENXIO);
454144966Svkashyap	}
455144966Svkashyap	/* Announce first HPET as timecounter. */
456144966Svkashyap	if (device_get_unit(dev) == 0) {
457144966Svkashyap		sc->tc.tc_get_timecount = hpet_get_timecount,
458144966Svkashyap		sc->tc.tc_counter_mask = ~0u,
459144966Svkashyap		sc->tc.tc_name = "HPET",
460144966Svkashyap		sc->tc.tc_quality = 900,
461144966Svkashyap		sc->tc.tc_frequency = sc->freq;
462144966Svkashyap		sc->tc.tc_priv = sc;
463144966Svkashyap		tc_init(&sc->tc);
464144966Svkashyap	}
465145688Svkashyap	/* If not disabled - setup and announce event timers. */
466144966Svkashyap	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
467144966Svkashyap	     "clock", &i) == 0 && i == 0)
468144966Svkashyap	        return (0);
469144966Svkashyap
470144966Svkashyap	/* Check whether we can and want legacy routing. */
471144966Svkashyap	sc->legacy_route = 0;
472144966Svkashyap	resource_int_value(device_get_name(dev), device_get_unit(dev),
473144966Svkashyap	     "legacy_route", &sc->legacy_route);
474144966Svkashyap	if ((sc->caps & HPET_CAP_LEG_RT) == 0)
475144966Svkashyap		sc->legacy_route = 0;
476144966Svkashyap	if (sc->legacy_route) {
477144966Svkashyap		sc->t[0].vectors = 0;
478144966Svkashyap		sc->t[1].vectors = 0;
479144966Svkashyap	}
480144966Svkashyap
481144966Svkashyap	/* Check what IRQs we want use. */
482144966Svkashyap	/* By default allow any PCI IRQs. */
483144966Svkashyap	sc->allowed_irqs = 0xffff0000;
484144966Svkashyap	/*
485144966Svkashyap	 * HPETs in AMD chipsets before SB800 have problems with IRQs >= 16
486144966Svkashyap	 * Lower are also not always working for different reasons.
487152213Svkashyap	 * SB800 fixed it, but seems do not implements level triggering
488152213Svkashyap	 * properly, that makes it very unreliable - it freezes after any
489152213Svkashyap	 * interrupt loss. Avoid legacy IRQs for AMD.
490203108Smav	 */
491144966Svkashyap	if (vendor == HPET_VENDID_AMD)
492172496Sscottl		sc->allowed_irqs = 0x00000000;
493249468Smav	/*
494203108Smav	 * Neither QEMU nor VirtualBox report supported IRQs correctly.
495203108Smav	 * The only way to use HPET there is to specify IRQs manually
496172496Sscottl	 * and/or use legacy_route. Legacy_route mode work on both.
497144966Svkashyap	 */
498169400Sscottl	if (vm_guest)
499144966Svkashyap		sc->allowed_irqs = 0x00000000;
500203108Smav	/* Let user override. */
501172496Sscottl	resource_int_value(device_get_name(dev), device_get_unit(dev),
502144966Svkashyap	     "allowed_irqs", &sc->allowed_irqs);
503144966Svkashyap
504144966Svkashyap	num_msi = 0;
505144966Svkashyap	sc->useirq = 0;
506144966Svkashyap	/* Find IRQ vectors for all timers. */
507144966Svkashyap	cvectors = sc->allowed_irqs & 0xffff0000;
508144966Svkashyap	dvectors = sc->allowed_irqs & 0x0000ffff;
509144966Svkashyap	if (sc->legacy_route)
510144966Svkashyap		dvectors &= 0x0000fefe;
511144966Svkashyap	for (i = 0; i < num_timers; i++) {
512144966Svkashyap		t = &sc->t[i];
513208969Sdelphij		if (sc->legacy_route && i < 2)
514144966Svkashyap			t->irq = (i == 0) ? 0 : 8;
515144966Svkashyap#ifdef DEV_APIC
516144966Svkashyap		else if (t->caps & HPET_TCAP_FSB_INT_DEL) {
517144966Svkashyap			if ((j = PCIB_ALLOC_MSIX(
518208969Sdelphij			    device_get_parent(device_get_parent(dev)), dev,
519208969Sdelphij			    &t->irq))) {
520144966Svkashyap				device_printf(dev,
521208969Sdelphij				    "Can't allocate interrupt for t%d.\n", j);
522208969Sdelphij			}
523208969Sdelphij		}
524208969Sdelphij#endif
525208969Sdelphij		else if (dvectors & t->vectors) {
526172496Sscottl			t->irq = ffs(dvectors & t->vectors) - 1;
527208969Sdelphij			dvectors &= ~(1 << t->irq);
528172496Sscottl		}
529144966Svkashyap		if (t->irq >= 0) {
530144966Svkashyap			if (!(t->intr_res =
531144966Svkashyap			    bus_alloc_resource(dev, SYS_RES_IRQ, &t->intr_rid,
532144966Svkashyap			    t->irq, t->irq, 1, RF_ACTIVE))) {
533144966Svkashyap				t->irq = -1;
534212008Sdelphij				device_printf(dev,
535212008Sdelphij				    "Can't map interrupt for t%d.\n", i);
536152213Svkashyap			} else if ((bus_setup_intr(dev, t->intr_res,
537212008Sdelphij			    INTR_MPSAFE | INTR_TYPE_CLK,
538152213Svkashyap			    (driver_filter_t *)hpet_intr_single, NULL,
539152213Svkashyap			    t, &t->intr_handle))) {
540152213Svkashyap				t->irq = -1;
541152213Svkashyap				device_printf(dev,
542212008Sdelphij				    "Can't setup interrupt for t%d.\n", i);
543152213Svkashyap			} else {
544212008Sdelphij				bus_describe_intr(dev, t->intr_res,
545212008Sdelphij				    t->intr_handle, "t%d", i);
546212008Sdelphij				num_msi++;
547212008Sdelphij			}
548212008Sdelphij		}
549152213Svkashyap		if (t->irq < 0 && (cvectors & t->vectors) != 0) {
550152213Svkashyap			cvectors &= t->vectors;
551152213Svkashyap			sc->useirq |= (1 << i);
552152213Svkashyap		}
553152213Svkashyap	}
554212008Sdelphij	if (sc->legacy_route && sc->t[0].irq < 0 && sc->t[1].irq < 0)
555212008Sdelphij		sc->legacy_route = 0;
556212008Sdelphij	if (sc->legacy_route)
557212008Sdelphij		hpet_enable(sc);
558212008Sdelphij	/* Group timers for per-CPU operation. */
559212008Sdelphij	num_percpu_et = min(num_msi / mp_ncpus, 1);
560212008Sdelphij	num_percpu_t = num_percpu_et * mp_ncpus;
561212008Sdelphij	pcpu_master = 0;
562212008Sdelphij	cur_cpu = CPU_FIRST();
563212008Sdelphij	for (i = 0; i < num_timers; i++) {
564212008Sdelphij		t = &sc->t[i];
565212008Sdelphij		if (t->irq >= 0 && num_percpu_t > 0) {
566212008Sdelphij			if (cur_cpu == CPU_FIRST())
567212008Sdelphij				pcpu_master = i;
568212008Sdelphij			t->pcpu_cpu = cur_cpu;
569212008Sdelphij			t->pcpu_master = pcpu_master;
570212008Sdelphij			sc->t[pcpu_master].
571212008Sdelphij			    pcpu_slaves[cur_cpu] = i;
572144966Svkashyap			bus_bind_intr(dev, t->intr_res, cur_cpu);
573144966Svkashyap			cur_cpu = CPU_NEXT(cur_cpu);
574144966Svkashyap			num_percpu_t--;
575144966Svkashyap		} else if (t->irq >= 0)
576144966Svkashyap			bus_bind_intr(dev, t->intr_res, CPU_FIRST());
577144966Svkashyap	}
578144966Svkashyap	bus_write_4(sc->mem_res, HPET_ISR, 0xffffffff);
579144966Svkashyap	sc->irq = -1;
580144966Svkashyap	sc->intr_rid = -1;
581144966Svkashyap	/* If at least one timer needs legacy IRQ - setup it. */
582144966Svkashyap	if (sc->useirq) {
583144966Svkashyap		j = i = fls(cvectors) - 1;
584144966Svkashyap		while (j > 0 && (cvectors & (1 << (j - 1))) != 0)
585144966Svkashyap			j--;
586144966Svkashyap		if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
587144966Svkashyap		    &sc->intr_rid, j, i, 1, RF_SHAREABLE | RF_ACTIVE)))
588144966Svkashyap			device_printf(dev,"Can't map interrupt.\n");
589144966Svkashyap		else if ((bus_setup_intr(dev, sc->intr_res,
590144966Svkashyap		    INTR_MPSAFE | INTR_TYPE_CLK,
591144966Svkashyap		    (driver_filter_t *)hpet_intr, NULL,
592144966Svkashyap		    sc, &sc->intr_handle))) {
593144966Svkashyap			device_printf(dev, "Can't setup interrupt.\n");
594144966Svkashyap		} else {
595144966Svkashyap			sc->irq = rman_get_start(sc->intr_res);
596144966Svkashyap			/* Bind IRQ to BSP to avoid live migration. */
597144966Svkashyap			bus_bind_intr(dev, sc->intr_res, CPU_FIRST());
598144966Svkashyap		}
599144966Svkashyap	}
600144966Svkashyap	/* Program and announce event timers. */
601144966Svkashyap	for (i = 0; i < num_timers; i++) {
602144966Svkashyap		t = &sc->t[i];
603144966Svkashyap		t->caps &= ~(HPET_TCNF_FSB_EN | HPET_TCNF_INT_ROUTE);
604144966Svkashyap		t->caps &= ~(HPET_TCNF_VAL_SET | HPET_TCNF_INT_ENB);
605144966Svkashyap		t->caps &= ~(HPET_TCNF_INT_TYPE);
606144966Svkashyap		t->caps |= HPET_TCNF_32MODE;
607144966Svkashyap		if (t->irq >= 0 && sc->legacy_route && i < 2) {
608144966Svkashyap			/* Legacy route doesn't need more configuration. */
609144966Svkashyap		} else
610144966Svkashyap#ifdef DEV_APIC
611144966Svkashyap		if ((t->caps & HPET_TCAP_FSB_INT_DEL) && t->irq >= 0) {
612144966Svkashyap			uint64_t addr;
613144966Svkashyap			uint32_t data;
614144966Svkashyap
615144966Svkashyap			if (PCIB_MAP_MSI(
616144966Svkashyap			    device_get_parent(device_get_parent(dev)), dev,
617144966Svkashyap			    t->irq, &addr, &data) == 0) {
618144966Svkashyap				bus_write_4(sc->mem_res,
619144966Svkashyap				    HPET_TIMER_FSB_ADDR(i), addr);
620144966Svkashyap				bus_write_4(sc->mem_res,
621144966Svkashyap				    HPET_TIMER_FSB_VAL(i), data);
622144966Svkashyap				t->caps |= HPET_TCNF_FSB_EN;
623144966Svkashyap			} else
624144966Svkashyap				t->irq = -2;
625144966Svkashyap		} else
626144966Svkashyap#endif
627144966Svkashyap		if (t->irq >= 0)
628144966Svkashyap			t->caps |= (t->irq << 9);
629144966Svkashyap		else if (sc->irq >= 0 && (t->vectors & (1 << sc->irq)))
630144966Svkashyap			t->caps |= (sc->irq << 9) | HPET_TCNF_INT_TYPE;
631144966Svkashyap		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps);
632144966Svkashyap		/* Skip event timers without set up IRQ. */
633144966Svkashyap		if (t->irq < 0 &&
634212008Sdelphij		    (sc->irq < 0 || (t->vectors & (1 << sc->irq)) == 0))
635144966Svkashyap			continue;
636144966Svkashyap		/* Announce the reset. */
637144966Svkashyap		if (maxhpetet == 0)
638144966Svkashyap			t->et.et_name = "HPET";
639152213Svkashyap		else {
640208969Sdelphij			sprintf(t->name, "HPET%d", maxhpetet);
641208969Sdelphij			t->et.et_name = t->name;
642144966Svkashyap		}
643144966Svkashyap		t->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
644144966Svkashyap		t->et.et_quality = 450;
645144966Svkashyap		if (t->pcpu_master >= 0) {
646144966Svkashyap			t->et.et_flags |= ET_FLAGS_PERCPU;
647144966Svkashyap			t->et.et_quality += 100;
648144966Svkashyap		}
649144966Svkashyap		if ((t->caps & HPET_TCAP_PER_INT) == 0)
650144966Svkashyap			t->et.et_quality -= 10;
651144966Svkashyap		t->et.et_frequency = sc->freq;
652144966Svkashyap		t->et.et_min_period.sec = 0;
653144966Svkashyap		t->et.et_min_period.frac = 0x00008000LLU << 32;
654144966Svkashyap		t->et.et_max_period.sec = 0xfffffffeLLU / sc->freq;
655237460Smav		t->et.et_max_period.frac =
656144966Svkashyap		    ((0xfffffffeLLU << 32) / sc->freq) << 32;
657237460Smav		t->et.et_start = hpet_start;
658144966Svkashyap		t->et.et_stop = hpet_stop;
659144966Svkashyap		t->et.et_priv = &sc->t[i];
660144966Svkashyap		if (t->pcpu_master < 0 || t->pcpu_master == i) {
661212008Sdelphij			et_register(&t->et);
662144966Svkashyap			maxhpetet++;
663144966Svkashyap		}
664144966Svkashyap	}
665144966Svkashyap	return (0);
666144966Svkashyap}
667144966Svkashyap
668144966Svkashyapstatic int
669144966Svkashyaphpet_detach(device_t dev)
670144966Svkashyap{
671144966Svkashyap	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
672144966Svkashyap
673144966Svkashyap	/* XXX Without a tc_remove() function, we can't detach. */
674144966Svkashyap	return (EBUSY);
675144966Svkashyap}
676144966Svkashyap
677144966Svkashyapstatic int
678144966Svkashyaphpet_suspend(device_t dev)
679172496Sscottl{
680144966Svkashyap	struct hpet_softc *sc;
681172496Sscottl
682144966Svkashyap	/*
683144966Svkashyap	 * Disable the timer during suspend.  The timer will not lose
684144966Svkashyap	 * its state in S1 or S2, but we are required to disable
685144966Svkashyap	 * it.
686144966Svkashyap	 */
687	sc = device_get_softc(dev);
688	hpet_disable(sc);
689
690	return (0);
691}
692
693static int
694hpet_resume(device_t dev)
695{
696	struct hpet_softc *sc;
697	struct hpet_timer *t;
698	int i;
699
700	/* Re-enable the timer after a resume to keep the clock advancing. */
701	sc = device_get_softc(dev);
702	hpet_enable(sc);
703	/* Restart event timers that were running on suspend. */
704	for (i = 0; i < sc->num_timers; i++) {
705		t = &sc->t[i];
706#ifdef DEV_APIC
707		if (t->irq >= 0 && (sc->legacy_route == 0 || i >= 2)) {
708			uint64_t addr;
709			uint32_t data;
710
711			if (PCIB_MAP_MSI(
712			    device_get_parent(device_get_parent(dev)), dev,
713			    t->irq, &addr, &data) == 0) {
714				bus_write_4(sc->mem_res,
715				    HPET_TIMER_FSB_ADDR(i), addr);
716				bus_write_4(sc->mem_res,
717				    HPET_TIMER_FSB_VAL(i), data);
718			}
719		}
720#endif
721		if (t->mode == 0)
722			continue;
723		t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
724		if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
725			t->caps |= HPET_TCNF_TYPE;
726			t->next += t->div;
727			bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
728			    t->caps | HPET_TCNF_VAL_SET);
729			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
730			    t->next);
731			bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
732			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
733			    t->div);
734		} else {
735			t->next += sc->freq / 1024;
736			bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
737			    t->next);
738		}
739		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
740		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
741	}
742	return (0);
743}
744
745/* Print some basic latency/rate information to assist in debugging. */
746static void
747hpet_test(struct hpet_softc *sc)
748{
749	int i;
750	uint32_t u1, u2;
751	struct bintime b0, b1, b2;
752	struct timespec ts;
753
754	binuptime(&b0);
755	binuptime(&b0);
756	binuptime(&b1);
757	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
758	for (i = 1; i < 1000; i++)
759		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
760	binuptime(&b2);
761	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
762
763	bintime_sub(&b2, &b1);
764	bintime_sub(&b1, &b0);
765	bintime_sub(&b2, &b1);
766	bintime2timespec(&b2, &ts);
767
768	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
769	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
770
771	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
772}
773
774#ifdef DEV_APIC
775static int
776hpet_remap_intr(device_t dev, device_t child, u_int irq)
777{
778	struct hpet_softc *sc = device_get_softc(dev);
779	struct hpet_timer *t;
780	uint64_t addr;
781	uint32_t data;
782	int error, i;
783
784	for (i = 0; i < sc->num_timers; i++) {
785		t = &sc->t[i];
786		if (t->irq != irq)
787			continue;
788		error = PCIB_MAP_MSI(
789		    device_get_parent(device_get_parent(dev)), dev,
790		    irq, &addr, &data);
791		if (error)
792			return (error);
793		hpet_disable(sc); /* Stop timer to avoid interrupt loss. */
794		bus_write_4(sc->mem_res, HPET_TIMER_FSB_ADDR(i), addr);
795		bus_write_4(sc->mem_res, HPET_TIMER_FSB_VAL(i), data);
796		hpet_enable(sc);
797		return (0);
798	}
799	return (ENOENT);
800}
801#endif
802
803static device_method_t hpet_methods[] = {
804	/* Device interface */
805	DEVMETHOD(device_identify, hpet_identify),
806	DEVMETHOD(device_probe, hpet_probe),
807	DEVMETHOD(device_attach, hpet_attach),
808	DEVMETHOD(device_detach, hpet_detach),
809	DEVMETHOD(device_suspend, hpet_suspend),
810	DEVMETHOD(device_resume, hpet_resume),
811
812#ifdef DEV_APIC
813	DEVMETHOD(bus_remap_intr, hpet_remap_intr),
814#endif
815
816	{0, 0}
817};
818
819static driver_t	hpet_driver = {
820	"hpet",
821	hpet_methods,
822	sizeof(struct hpet_softc),
823};
824
825DRIVER_MODULE(hpet, acpi, hpet_driver, hpet_devclass, 0, 0);
826MODULE_DEPEND(hpet, acpi, 1, 1, 1);
827