15db2f26eSSascha Wildner /*-
25db2f26eSSascha Wildner * Copyright (c) 2005 Poul-Henning Kamp
35db2f26eSSascha Wildner * All rights reserved.
45db2f26eSSascha Wildner *
55db2f26eSSascha Wildner * Redistribution and use in source and binary forms, with or without
65db2f26eSSascha Wildner * modification, are permitted provided that the following conditions
75db2f26eSSascha Wildner * are met:
85db2f26eSSascha Wildner * 1. Redistributions of source code must retain the above copyright
95db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer.
105db2f26eSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
115db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer in the
125db2f26eSSascha Wildner * documentation and/or other materials provided with the distribution.
135db2f26eSSascha Wildner *
145db2f26eSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
155db2f26eSSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
165db2f26eSSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
175db2f26eSSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
185db2f26eSSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
195db2f26eSSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
205db2f26eSSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
215db2f26eSSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
225db2f26eSSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
235db2f26eSSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
245db2f26eSSascha Wildner * SUCH DAMAGE.
255db2f26eSSascha Wildner *
265db2f26eSSascha Wildner * $FreeBSD: src/sys/dev/acpica/acpi_hpet.c,v 1.12.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $
275db2f26eSSascha Wildner */
285db2f26eSSascha Wildner
295db2f26eSSascha Wildner #include "opt_acpi.h"
305db2f26eSSascha Wildner
315db2f26eSSascha Wildner #include <sys/param.h>
325db2f26eSSascha Wildner #include <sys/bus.h>
335db2f26eSSascha Wildner #include <sys/kernel.h>
345db2f26eSSascha Wildner #include <sys/module.h>
355db2f26eSSascha Wildner #include <sys/systimer.h>
365db2f26eSSascha Wildner #include <sys/rman.h>
375db2f26eSSascha Wildner
38c4984bacSImre Vadász #if !defined(KLD_MODULE)
39*db2ec6f8SSascha Wildner #include <machine/clock.h>
40c4984bacSImre Vadász #include <machine/pmap.h>
41c4984bacSImre Vadász #endif
42c4984bacSImre Vadász
435db2f26eSSascha Wildner #include "acpi.h"
445db2f26eSSascha Wildner #include "accommon.h"
455db2f26eSSascha Wildner #include "acpivar.h"
465db2f26eSSascha Wildner #include "acpi_hpet.h"
475db2f26eSSascha Wildner
48c4984bacSImre Vadász #if !defined(KLD_MODULE)
49c4984bacSImre Vadász #include <platform/pc64/acpica/acpi_sdt_var.h>
50c4984bacSImre Vadász #endif
51c4984bacSImre Vadász
525db2f26eSSascha Wildner /* Hooks for the ACPICA debugging infrastructure */
535db2f26eSSascha Wildner #define _COMPONENT ACPI_TIMER
545db2f26eSSascha Wildner ACPI_MODULE_NAME("HPET")
555db2f26eSSascha Wildner
565db2f26eSSascha Wildner static bus_space_handle_t acpi_hpet_bsh;
575db2f26eSSascha Wildner static bus_space_tag_t acpi_hpet_bst;
585db2f26eSSascha Wildner static u_long acpi_hpet_res_start;
595db2f26eSSascha Wildner
605db2f26eSSascha Wildner struct acpi_hpet_softc {
615db2f26eSSascha Wildner device_t dev;
625db2f26eSSascha Wildner struct resource *mem_res;
635db2f26eSSascha Wildner ACPI_HANDLE handle;
645db2f26eSSascha Wildner };
655db2f26eSSascha Wildner
665db2f26eSSascha Wildner #define DEV_HPET(x) (acpi_get_magic(x) == (uintptr_t)&acpi_hpet_devclass)
675db2f26eSSascha Wildner
685db2f26eSSascha Wildner static sysclock_t acpi_hpet_get_timecount(void);
695db2f26eSSascha Wildner static void acpi_hpet_construct(struct cputimer *, sysclock_t);
705db2f26eSSascha Wildner
715db2f26eSSascha Wildner static int acpi_hpet_identify(driver_t *, device_t);
725db2f26eSSascha Wildner static int acpi_hpet_probe(device_t);
735db2f26eSSascha Wildner static int acpi_hpet_attach(device_t);
745db2f26eSSascha Wildner static int acpi_hpet_resume(device_t);
755db2f26eSSascha Wildner static int acpi_hpet_suspend(device_t);
765db2f26eSSascha Wildner
775db2f26eSSascha Wildner static void acpi_hpet_test(struct acpi_hpet_softc *sc);
785db2f26eSSascha Wildner static u_int acpi_hpet_read(void);
795db2f26eSSascha Wildner static void acpi_hpet_enable(struct acpi_hpet_softc *);
805db2f26eSSascha Wildner static void acpi_hpet_disable(struct acpi_hpet_softc *);
815db2f26eSSascha Wildner
825db2f26eSSascha Wildner static char *hpet_ids[] = { "PNP0103", NULL };
835db2f26eSSascha Wildner
845db2f26eSSascha Wildner static struct cputimer acpi_hpet_timer = {
850087561dSSepherosa Ziehau .next = SLIST_ENTRY_INITIALIZER,
860087561dSSepherosa Ziehau .name = "HPET",
870087561dSSepherosa Ziehau .pri = CPUTIMER_PRI_HPET,
880087561dSSepherosa Ziehau .type = CPUTIMER_HPET,
890087561dSSepherosa Ziehau .count = acpi_hpet_get_timecount,
900087561dSSepherosa Ziehau .fromhz = cputimer_default_fromhz,
910087561dSSepherosa Ziehau .fromus = cputimer_default_fromus,
920087561dSSepherosa Ziehau .construct = acpi_hpet_construct,
930087561dSSepherosa Ziehau .destruct = cputimer_default_destruct,
940087561dSSepherosa Ziehau .freq = 0 /* determined later */
955db2f26eSSascha Wildner };
965db2f26eSSascha Wildner
975db2f26eSSascha Wildner static device_method_t acpi_hpet_methods[] = {
985db2f26eSSascha Wildner DEVMETHOD(device_identify, acpi_hpet_identify),
995db2f26eSSascha Wildner DEVMETHOD(device_probe, acpi_hpet_probe),
1005db2f26eSSascha Wildner DEVMETHOD(device_attach, acpi_hpet_attach),
1015db2f26eSSascha Wildner DEVMETHOD(device_suspend, acpi_hpet_suspend),
1025db2f26eSSascha Wildner DEVMETHOD(device_resume, acpi_hpet_resume),
103d3c9c58eSSascha Wildner DEVMETHOD_END
1045db2f26eSSascha Wildner };
1055db2f26eSSascha Wildner
1065db2f26eSSascha Wildner static driver_t acpi_hpet_driver = {
1075db2f26eSSascha Wildner "acpi_hpet",
1085db2f26eSSascha Wildner acpi_hpet_methods,
1095db2f26eSSascha Wildner sizeof(struct acpi_hpet_softc),
110df21e16dSImre Vadász .gpri = KOBJ_GPRI_ACPI+2
1115db2f26eSSascha Wildner };
1125db2f26eSSascha Wildner
1135db2f26eSSascha Wildner static devclass_t acpi_hpet_devclass;
1145db2f26eSSascha Wildner DRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, NULL, NULL);
1155db2f26eSSascha Wildner MODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
1165db2f26eSSascha Wildner
1175db2f26eSSascha Wildner static u_int
acpi_hpet_read(void)1185db2f26eSSascha Wildner acpi_hpet_read(void)
1195db2f26eSSascha Wildner {
1205db2f26eSSascha Wildner return bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh,
1215db2f26eSSascha Wildner HPET_MAIN_COUNTER);
1225db2f26eSSascha Wildner }
1235db2f26eSSascha Wildner
124c4984bacSImre Vadász #if !defined(KLD_MODULE)
125c4984bacSImre Vadász static vm_offset_t ptr = 0;
126c4984bacSImre Vadász
127c4984bacSImre Vadász static int acpi_hpet_for_calibration = 1;
128c4984bacSImre Vadász TUNABLE_INT("hw.calibrate_timers_with_hpet", &acpi_hpet_for_calibration);
129c4984bacSImre Vadász
130c4984bacSImre Vadász static sysclock_t
acpi_hpet_early_get_timecount(void)131c4984bacSImre Vadász acpi_hpet_early_get_timecount(void)
132c4984bacSImre Vadász {
1338fbc264dSMatthew Dillon sysclock_t last_counter;
1348fbc264dSMatthew Dillon sysclock_t next_counter;
1358fbc264dSMatthew Dillon uint32_t counter;
1368fbc264dSMatthew Dillon
1378fbc264dSMatthew Dillon last_counter = acpi_hpet_timer.base;
1388fbc264dSMatthew Dillon for (;;) {
1398fbc264dSMatthew Dillon cpu_ccfence();
1408fbc264dSMatthew Dillon counter = readl(ptr + HPET_MAIN_COUNTER);
1418fbc264dSMatthew Dillon if (counter < (last_counter & 0xFFFFFFFFU))
1428fbc264dSMatthew Dillon next_counter = ((last_counter + 0x0100000000U) &
1438fbc264dSMatthew Dillon 0xFFFFFFFF00000000LU) | counter;
1448fbc264dSMatthew Dillon else
1458fbc264dSMatthew Dillon next_counter = (last_counter &
1468fbc264dSMatthew Dillon 0xFFFFFFFF00000000LU) | counter;
1478fbc264dSMatthew Dillon if (atomic_fcmpset_long(&acpi_hpet_timer.base, &last_counter,
1488fbc264dSMatthew Dillon next_counter)) {
1498fbc264dSMatthew Dillon break;
1508fbc264dSMatthew Dillon }
1518fbc264dSMatthew Dillon }
1528fbc264dSMatthew Dillon return next_counter;
153c4984bacSImre Vadász }
154c4984bacSImre Vadász
155c4984bacSImre Vadász static void
acpi_hpet_early_construct(struct cputimer * timer,sysclock_t oldclock)156c4984bacSImre Vadász acpi_hpet_early_construct(struct cputimer *timer, sysclock_t oldclock)
157c4984bacSImre Vadász {
158c4984bacSImre Vadász uint32_t val;
159c4984bacSImre Vadász
160c4984bacSImre Vadász val = readl(ptr + HPET_CONFIG);
161c4984bacSImre Vadász writel(ptr + HPET_CONFIG, val | HPET_CNF_ENABLE);
162c4984bacSImre Vadász
163c4984bacSImre Vadász timer->base = 0;
164c4984bacSImre Vadász timer->base = oldclock - acpi_hpet_early_get_timecount();
165c4984bacSImre Vadász }
166c4984bacSImre Vadász
167c4984bacSImre Vadász static void
acpi_hpet_early_destruct(struct cputimer * timer)168c4984bacSImre Vadász acpi_hpet_early_destruct(struct cputimer *timer)
169c4984bacSImre Vadász {
170c4984bacSImre Vadász uint32_t val;
171c4984bacSImre Vadász
172c4984bacSImre Vadász val = readl(ptr + HPET_CONFIG);
173c4984bacSImre Vadász writel(ptr + HPET_CONFIG, val & ~HPET_CNF_ENABLE);
174c4984bacSImre Vadász }
175c4984bacSImre Vadász
176c4984bacSImre Vadász static int
acpi_hpet_early_init(void)177c4984bacSImre Vadász acpi_hpet_early_init(void)
178c4984bacSImre Vadász {
179c4984bacSImre Vadász uintmax_t freq;
180c4984bacSImre Vadász uint64_t old_tsc, new_tsc;
181c4984bacSImre Vadász uint32_t val, val2;
182c4984bacSImre Vadász
183c4984bacSImre Vadász val = readl(ptr + HPET_CONFIG);
184c4984bacSImre Vadász writel(ptr + HPET_CONFIG, val | HPET_CNF_ENABLE);
185c4984bacSImre Vadász
186c4984bacSImre Vadász /* Read basic statistics about the timer. */
187c4984bacSImre Vadász val = readl(ptr + HPET_PERIOD);
188c4984bacSImre Vadász if (val == 0) {
189c4984bacSImre Vadász kprintf("acpi_hpet: invalid period\n");
190c4984bacSImre Vadász val = readl(ptr + HPET_CONFIG);
191c4984bacSImre Vadász writel(ptr + HPET_CONFIG, val & ~HPET_CNF_ENABLE);
192c4984bacSImre Vadász return ENXIO;
193c4984bacSImre Vadász }
194c4984bacSImre Vadász
195c4984bacSImre Vadász freq = (1000000000000000LL + val / 2) / val;
196c4984bacSImre Vadász if (bootverbose) {
197c4984bacSImre Vadász val = readl(ptr + HPET_CAPABILITIES);
198c4984bacSImre Vadász kprintf("acpi_hpet: "
199c4984bacSImre Vadász "vend: 0x%x, rev: 0x%x, num: %d, opts:%s%s\n",
200c4984bacSImre Vadász val >> 16, val & HPET_CAP_REV_ID,
201c4984bacSImre Vadász (val & HPET_CAP_NUM_TIM) >> 8,
202c4984bacSImre Vadász (val & HPET_CAP_LEG_RT) ? " legacy_route" : "",
203c4984bacSImre Vadász (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : "");
204c4984bacSImre Vadász }
205c4984bacSImre Vadász
206c4984bacSImre Vadász #if 0
207c4984bacSImre Vadász if (ktestenv("debug.acpi.hpet_test"))
208c4984bacSImre Vadász acpi_hpet_test(sc);
209c4984bacSImre Vadász #endif
210c4984bacSImre Vadász
211c4984bacSImre Vadász /*
212c4984bacSImre Vadász * Don't attach if the timer never increments. Since the spec
213c4984bacSImre Vadász * requires it to be at least 10 MHz, it has to change in 1 us.
214c4984bacSImre Vadász */
215c4984bacSImre Vadász val = readl(ptr + HPET_MAIN_COUNTER);
216c4984bacSImre Vadász /* This delay correspond to 1us, even at 6 GHz TSC. */
217c4984bacSImre Vadász old_tsc = rdtsc();
218c4984bacSImre Vadász do {
219c4984bacSImre Vadász cpu_pause();
220c4984bacSImre Vadász new_tsc = rdtsc();
221c4984bacSImre Vadász } while (new_tsc - old_tsc < 6000);
222c4984bacSImre Vadász val2 = readl(ptr + HPET_MAIN_COUNTER);
223c4984bacSImre Vadász if (val == val2) {
224c4984bacSImre Vadász kprintf("acpi_hpet: HPET never increments, disabling\n");
225c4984bacSImre Vadász val = readl(ptr + HPET_CONFIG);
226c4984bacSImre Vadász writel(ptr + HPET_CONFIG, val & ~HPET_CNF_ENABLE);
227c4984bacSImre Vadász return ENXIO;
228c4984bacSImre Vadász }
229c4984bacSImre Vadász
230c4984bacSImre Vadász val = readl(ptr + HPET_CONFIG);
231c4984bacSImre Vadász writel(ptr + HPET_CONFIG, val & ~HPET_CNF_ENABLE);
232c4984bacSImre Vadász acpi_hpet_timer.freq = freq;
2338fbc264dSMatthew Dillon kprintf("acpi_hpet: frequency %lu\n", acpi_hpet_timer.freq);
234c4984bacSImre Vadász
235c4984bacSImre Vadász acpi_hpet_timer.count = acpi_hpet_early_get_timecount;
236c4984bacSImre Vadász acpi_hpet_timer.construct = acpi_hpet_early_construct;
237c4984bacSImre Vadász acpi_hpet_timer.destruct = acpi_hpet_early_destruct;
238c4984bacSImre Vadász
239c4984bacSImre Vadász cputimer_register(&acpi_hpet_timer);
240c4984bacSImre Vadász cputimer_select(&acpi_hpet_timer, 0);
241c4984bacSImre Vadász return 0;
242c4984bacSImre Vadász }
243c4984bacSImre Vadász
244c4984bacSImre Vadász static void
acpi_hpet_cputimer_register(void)245c4984bacSImre Vadász acpi_hpet_cputimer_register(void)
246c4984bacSImre Vadász {
247c4984bacSImre Vadász ACPI_TABLE_HPET *hpet;
248c4984bacSImre Vadász vm_paddr_t hpet_paddr;
249c4984bacSImre Vadász
250c4984bacSImre Vadász if (acpi_hpet_for_calibration == 0)
251c4984bacSImre Vadász return;
252c4984bacSImre Vadász
253c4984bacSImre Vadász if (acpi_disabled("hpet"))
254c4984bacSImre Vadász return;
255c4984bacSImre Vadász
256c4984bacSImre Vadász hpet_paddr = sdt_search(ACPI_SIG_HPET);
257c4984bacSImre Vadász if (hpet_paddr == 0) {
258c4984bacSImre Vadász if (bootverbose)
259c4984bacSImre Vadász kprintf("acpi_hpet: can't locate HPET\n");
260c4984bacSImre Vadász return;
261c4984bacSImre Vadász }
262c4984bacSImre Vadász
263c4984bacSImre Vadász hpet = sdt_sdth_map(hpet_paddr);
264c4984bacSImre Vadász if (hpet == NULL)
265c4984bacSImre Vadász return;
266c4984bacSImre Vadász
267c4984bacSImre Vadász if (hpet->Header.Length < 56) {
268c4984bacSImre Vadász kprintf("acpi_hpet: HPET table too short. Length: 0x%x\n",
269c4984bacSImre Vadász hpet->Header.Length);
270c4984bacSImre Vadász return;
271c4984bacSImre Vadász }
272c4984bacSImre Vadász
273c4984bacSImre Vadász if (hpet->Sequence != 0) {
274c4984bacSImre Vadász kprintf("acpi_hpet: "
275c4984bacSImre Vadász "HPET table Sequence not 0. Sequence: 0x%x\n", hpet->Id);
276c4984bacSImre Vadász goto done;
277c4984bacSImre Vadász }
278c4984bacSImre Vadász
279c4984bacSImre Vadász acpi_hpet_res_start = hpet->Address.Address;
280c4984bacSImre Vadász if (acpi_hpet_res_start == 0)
281c4984bacSImre Vadász goto done;
282c4984bacSImre Vadász
283c4984bacSImre Vadász ptr = (vm_offset_t)pmap_mapdev(acpi_hpet_res_start, HPET_MEM_WIDTH);
284c4984bacSImre Vadász if (acpi_hpet_early_init() == 0) {
285c4984bacSImre Vadász i8254_cputimer_disable = 1;
286c4984bacSImre Vadász } else {
287c4984bacSImre Vadász pmap_unmapdev(ptr, HPET_MEM_WIDTH);
288c4984bacSImre Vadász ptr = 0;
289c4984bacSImre Vadász }
290c4984bacSImre Vadász
291c4984bacSImre Vadász done:
292c4984bacSImre Vadász sdt_sdth_unmap(&hpet->Header);
293c4984bacSImre Vadász return;
294c4984bacSImre Vadász }
295c4984bacSImre Vadász
296c4984bacSImre Vadász TIMECOUNTER_INIT(acpi_hpet_init, acpi_hpet_cputimer_register);
297c4984bacSImre Vadász #endif
298c4984bacSImre Vadász
2995db2f26eSSascha Wildner /*
3005db2f26eSSascha Wildner * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
3015db2f26eSSascha Wildner * we will be using.
3025db2f26eSSascha Wildner */
3035db2f26eSSascha Wildner static int
acpi_hpet_identify(driver_t * driver,device_t parent)3045db2f26eSSascha Wildner acpi_hpet_identify(driver_t *driver, device_t parent)
3055db2f26eSSascha Wildner {
3065db2f26eSSascha Wildner ACPI_TABLE_HPET *hpet;
3075db2f26eSSascha Wildner ACPI_TABLE_HEADER *hdr;
3085db2f26eSSascha Wildner ACPI_STATUS status;
3095db2f26eSSascha Wildner device_t child;
3105db2f26eSSascha Wildner
3115db2f26eSSascha Wildner /*
3125db2f26eSSascha Wildner * Just try once, do nothing if the 'acpi' bus is rescanned.
3135db2f26eSSascha Wildner */
3145db2f26eSSascha Wildner if (device_get_state(parent) == DS_ATTACHED)
3155db2f26eSSascha Wildner return 0;
3165db2f26eSSascha Wildner
3175db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
3185db2f26eSSascha Wildner
3195db2f26eSSascha Wildner /* Only one HPET device can be added. */
3205db2f26eSSascha Wildner if (devclass_get_device(acpi_hpet_devclass, 0))
3215db2f26eSSascha Wildner return ENXIO;
3225db2f26eSSascha Wildner
323c4984bacSImre Vadász #if !defined(KLD_MODULE)
324c4984bacSImre Vadász if (ptr != 0) {
325c4984bacSImre Vadász /* Use data from early boot for attachment. */
326c4984bacSImre Vadász child = BUS_ADD_CHILD(parent, parent, 0, "acpi_hpet", 0);
327c4984bacSImre Vadász if (child == NULL) {
328c4984bacSImre Vadász device_printf(parent, "%s: can't add acpi_hpet0\n",
329c4984bacSImre Vadász __func__);
330c4984bacSImre Vadász return ENXIO;
331c4984bacSImre Vadász }
332c4984bacSImre Vadász
333c4984bacSImre Vadász /* Record a magic value so we can detect this device later. */
334c4984bacSImre Vadász acpi_set_magic(child, (uintptr_t)&acpi_hpet_devclass);
335c4984bacSImre Vadász
336c4984bacSImre Vadász if (bus_set_resource(child, SYS_RES_MEMORY, 0,
337c4984bacSImre Vadász acpi_hpet_res_start, HPET_MEM_WIDTH, -1)) {
338c4984bacSImre Vadász device_printf(child,
339c4984bacSImre Vadász "could not set iomem resources: 0x%jx, %d\n",
340c4984bacSImre Vadász (uintmax_t)acpi_hpet_res_start, HPET_MEM_WIDTH);
341c4984bacSImre Vadász return ENOMEM;
342c4984bacSImre Vadász }
343c4984bacSImre Vadász
344c4984bacSImre Vadász return 0;
345c4984bacSImre Vadász }
346c4984bacSImre Vadász #endif
347c4984bacSImre Vadász
3485db2f26eSSascha Wildner /* Currently, ID and minimum clock tick info is unused. */
3495db2f26eSSascha Wildner
3505db2f26eSSascha Wildner status = AcpiGetTable(ACPI_SIG_HPET, 1, &hdr);
3515db2f26eSSascha Wildner if (ACPI_FAILURE(status))
3525db2f26eSSascha Wildner return ENXIO;
3535db2f26eSSascha Wildner
3545db2f26eSSascha Wildner /*
3555db2f26eSSascha Wildner * The unit number could be derived from hdr->Sequence but we only
3565db2f26eSSascha Wildner * support one HPET device.
3575db2f26eSSascha Wildner */
3585db2f26eSSascha Wildner hpet = (ACPI_TABLE_HPET *)hdr;
3595db2f26eSSascha Wildner if (hpet->Sequence != 0) {
3605db2f26eSSascha Wildner kprintf("ACPI HPET table warning: Sequence is non-zero (%d)\n",
3615db2f26eSSascha Wildner hpet->Sequence);
3625db2f26eSSascha Wildner }
3635db2f26eSSascha Wildner
3645db2f26eSSascha Wildner child = BUS_ADD_CHILD(parent, parent, 0, "acpi_hpet", 0);
3655db2f26eSSascha Wildner if (child == NULL) {
3665db2f26eSSascha Wildner device_printf(parent, "%s: can't add acpi_hpet0\n", __func__);
3675db2f26eSSascha Wildner return ENXIO;
3685db2f26eSSascha Wildner }
3695db2f26eSSascha Wildner
3705db2f26eSSascha Wildner /* Record a magic value so we can detect this device later. */
3715db2f26eSSascha Wildner acpi_set_magic(child, (uintptr_t)&acpi_hpet_devclass);
3725db2f26eSSascha Wildner
3735db2f26eSSascha Wildner acpi_hpet_res_start = hpet->Address.Address;
3745db2f26eSSascha Wildner if (bus_set_resource(child, SYS_RES_MEMORY, 0,
3755db2f26eSSascha Wildner hpet->Address.Address, HPET_MEM_WIDTH, -1)) {
3765db2f26eSSascha Wildner device_printf(child, "could not set iomem resources: "
3775db2f26eSSascha Wildner "0x%jx, %d\n", (uintmax_t)hpet->Address.Address,
3785db2f26eSSascha Wildner HPET_MEM_WIDTH);
3795db2f26eSSascha Wildner return ENOMEM;
3805db2f26eSSascha Wildner }
3815db2f26eSSascha Wildner return 0;
3825db2f26eSSascha Wildner }
3835db2f26eSSascha Wildner
3845db2f26eSSascha Wildner static int
acpi_hpet_probe(device_t dev)3855db2f26eSSascha Wildner acpi_hpet_probe(device_t dev)
3865db2f26eSSascha Wildner {
3875db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
3885db2f26eSSascha Wildner
3895db2f26eSSascha Wildner if (acpi_disabled("hpet"))
3905db2f26eSSascha Wildner return ENXIO;
3915db2f26eSSascha Wildner
3925db2f26eSSascha Wildner if (!DEV_HPET(dev) &&
3935db2f26eSSascha Wildner (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
3945db2f26eSSascha Wildner device_get_unit(dev) != 0))
3955db2f26eSSascha Wildner return ENXIO;
3965db2f26eSSascha Wildner
3975db2f26eSSascha Wildner device_set_desc(dev, "High Precision Event Timer");
3985db2f26eSSascha Wildner return 0;
3995db2f26eSSascha Wildner }
4005db2f26eSSascha Wildner
4015db2f26eSSascha Wildner static int
acpi_hpet_attach(device_t dev)4025db2f26eSSascha Wildner acpi_hpet_attach(device_t dev)
4035db2f26eSSascha Wildner {
4045db2f26eSSascha Wildner struct acpi_hpet_softc *sc;
4055db2f26eSSascha Wildner int rid;
4065db2f26eSSascha Wildner uint32_t val, val2;
4075db2f26eSSascha Wildner uintmax_t freq;
4085db2f26eSSascha Wildner
4095db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
4105db2f26eSSascha Wildner
4115db2f26eSSascha Wildner sc = device_get_softc(dev);
4125db2f26eSSascha Wildner sc->dev = dev;
4135db2f26eSSascha Wildner sc->handle = acpi_get_handle(dev);
4145db2f26eSSascha Wildner
4155db2f26eSSascha Wildner rid = 0;
4165db2f26eSSascha Wildner sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
4175db2f26eSSascha Wildner RF_ACTIVE);
4185db2f26eSSascha Wildner if (sc->mem_res == NULL) {
4195db2f26eSSascha Wildner /*
4205db2f26eSSascha Wildner * We only need to make sure that main counter
4215db2f26eSSascha Wildner * is accessable.
4225db2f26eSSascha Wildner */
4235db2f26eSSascha Wildner device_printf(dev, "can't map %dB register space, try %dB\n",
4245db2f26eSSascha Wildner HPET_MEM_WIDTH, HPET_MEM_WIDTH_MIN);
4255db2f26eSSascha Wildner rid = 0;
4265db2f26eSSascha Wildner sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
4275db2f26eSSascha Wildner acpi_hpet_res_start,
4285db2f26eSSascha Wildner acpi_hpet_res_start + HPET_MEM_WIDTH_MIN - 1,
4295db2f26eSSascha Wildner HPET_MEM_WIDTH_MIN, RF_ACTIVE);
4305db2f26eSSascha Wildner if (sc->mem_res == NULL)
4315db2f26eSSascha Wildner return ENOMEM;
4325db2f26eSSascha Wildner }
4335db2f26eSSascha Wildner
4345db2f26eSSascha Wildner /* Validate that we can access the whole region. */
4355db2f26eSSascha Wildner if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH_MIN) {
4365db2f26eSSascha Wildner device_printf(dev, "memory region width %ld too small\n",
4375db2f26eSSascha Wildner rman_get_size(sc->mem_res));
4385db2f26eSSascha Wildner bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
4395db2f26eSSascha Wildner return ENXIO;
4405db2f26eSSascha Wildner }
4415db2f26eSSascha Wildner
4425db2f26eSSascha Wildner acpi_hpet_bsh = rman_get_bushandle(sc->mem_res);
4435db2f26eSSascha Wildner acpi_hpet_bst = rman_get_bustag(sc->mem_res);
4445db2f26eSSascha Wildner
445c4984bacSImre Vadász #if !defined(KLD_MODULE)
446c4984bacSImre Vadász if (ptr != 0) {
447c4984bacSImre Vadász /* Use data from early boot for attachment. */
448c4984bacSImre Vadász if (ktestenv("debug.acpi.hpet_test"))
449c4984bacSImre Vadász acpi_hpet_test(sc);
450c4984bacSImre Vadász return 0;
451c4984bacSImre Vadász }
452c4984bacSImre Vadász #endif
453c4984bacSImre Vadász
4545db2f26eSSascha Wildner /* Be sure timer is enabled. */
4555db2f26eSSascha Wildner acpi_hpet_enable(sc);
4565db2f26eSSascha Wildner
4575db2f26eSSascha Wildner /* Read basic statistics about the timer. */
4585db2f26eSSascha Wildner val = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_PERIOD);
4595db2f26eSSascha Wildner if (val == 0) {
4605db2f26eSSascha Wildner device_printf(dev, "invalid period\n");
4615db2f26eSSascha Wildner acpi_hpet_disable(sc);
4625db2f26eSSascha Wildner bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
4635db2f26eSSascha Wildner return ENXIO;
4645db2f26eSSascha Wildner }
4655db2f26eSSascha Wildner
4665db2f26eSSascha Wildner freq = (1000000000000000LL + val / 2) / val;
4675db2f26eSSascha Wildner if (bootverbose) {
4685db2f26eSSascha Wildner val = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh,
4695db2f26eSSascha Wildner HPET_CAPABILITIES);
4705db2f26eSSascha Wildner device_printf(dev,
4715db2f26eSSascha Wildner "vend: 0x%x, rev: 0x%x, num: %d, opts:%s%s\n",
4725db2f26eSSascha Wildner val >> 16, val & HPET_CAP_REV_ID,
4735db2f26eSSascha Wildner (val & HPET_CAP_NUM_TIM) >> 8,
4745db2f26eSSascha Wildner (val & HPET_CAP_LEG_RT) ? " legacy_route" : "",
4755db2f26eSSascha Wildner (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : "");
4765db2f26eSSascha Wildner }
4775db2f26eSSascha Wildner
4785db2f26eSSascha Wildner if (ktestenv("debug.acpi.hpet_test"))
4795db2f26eSSascha Wildner acpi_hpet_test(sc);
4805db2f26eSSascha Wildner
4815db2f26eSSascha Wildner /*
4825db2f26eSSascha Wildner * Don't attach if the timer never increments. Since the spec
4835db2f26eSSascha Wildner * requires it to be at least 10 MHz, it has to change in 1 us.
4845db2f26eSSascha Wildner */
4855db2f26eSSascha Wildner val = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh,
4865db2f26eSSascha Wildner HPET_MAIN_COUNTER);
4875db2f26eSSascha Wildner DELAY(1);
4885db2f26eSSascha Wildner val2 = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh,
4895db2f26eSSascha Wildner HPET_MAIN_COUNTER);
4905db2f26eSSascha Wildner if (val == val2) {
4915db2f26eSSascha Wildner device_printf(dev, "HPET never increments, disabling\n");
4925db2f26eSSascha Wildner acpi_hpet_disable(sc);
4935db2f26eSSascha Wildner bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
4945db2f26eSSascha Wildner return ENXIO;
4955db2f26eSSascha Wildner }
4965db2f26eSSascha Wildner
4975db2f26eSSascha Wildner acpi_hpet_timer.freq = freq;
4988fbc264dSMatthew Dillon device_printf(dev, "frequency %lu\n", acpi_hpet_timer.freq);
4995db2f26eSSascha Wildner
5005db2f26eSSascha Wildner cputimer_register(&acpi_hpet_timer);
5015db2f26eSSascha Wildner cputimer_select(&acpi_hpet_timer, 0);
5025db2f26eSSascha Wildner
5035db2f26eSSascha Wildner return 0;
5045db2f26eSSascha Wildner }
5055db2f26eSSascha Wildner
5065db2f26eSSascha Wildner /*
5075db2f26eSSascha Wildner * Construct the timer. Adjust the base so the system clock does not
5085db2f26eSSascha Wildner * jump weirdly.
5095db2f26eSSascha Wildner */
5105db2f26eSSascha Wildner static void
acpi_hpet_construct(struct cputimer * timer,sysclock_t oldclock)5115db2f26eSSascha Wildner acpi_hpet_construct(struct cputimer *timer, sysclock_t oldclock)
5125db2f26eSSascha Wildner {
5135db2f26eSSascha Wildner timer->base = 0;
5145db2f26eSSascha Wildner timer->base = oldclock - acpi_hpet_get_timecount();
5155db2f26eSSascha Wildner }
5165db2f26eSSascha Wildner
5175db2f26eSSascha Wildner static sysclock_t
acpi_hpet_get_timecount(void)5185db2f26eSSascha Wildner acpi_hpet_get_timecount(void)
5195db2f26eSSascha Wildner {
5208fbc264dSMatthew Dillon sysclock_t last_counter;
5218fbc264dSMatthew Dillon sysclock_t next_counter;
5228fbc264dSMatthew Dillon uint32_t counter;
5238fbc264dSMatthew Dillon
5248fbc264dSMatthew Dillon last_counter = acpi_hpet_timer.base;
5258fbc264dSMatthew Dillon for (;;) {
5268fbc264dSMatthew Dillon cpu_ccfence();
5278fbc264dSMatthew Dillon counter = acpi_hpet_read();
5288fbc264dSMatthew Dillon if (counter < (last_counter & 0xFFFFFFFFU))
5298fbc264dSMatthew Dillon next_counter = ((last_counter + 0x0100000000U) &
5308fbc264dSMatthew Dillon 0xFFFFFFFF00000000LU) | counter;
5318fbc264dSMatthew Dillon else
5328fbc264dSMatthew Dillon next_counter = (last_counter &
5338fbc264dSMatthew Dillon 0xFFFFFFFF00000000LU) | counter;
5348fbc264dSMatthew Dillon if (atomic_fcmpset_long(&acpi_hpet_timer.base, &last_counter,
5358fbc264dSMatthew Dillon next_counter)) {
5368fbc264dSMatthew Dillon break;
5378fbc264dSMatthew Dillon }
5388fbc264dSMatthew Dillon }
5398fbc264dSMatthew Dillon return next_counter;
5405db2f26eSSascha Wildner }
5415db2f26eSSascha Wildner
5425db2f26eSSascha Wildner static void
acpi_hpet_enable(struct acpi_hpet_softc * sc)5435db2f26eSSascha Wildner acpi_hpet_enable(struct acpi_hpet_softc *sc)
5445db2f26eSSascha Wildner {
5455db2f26eSSascha Wildner uint32_t val;
5465db2f26eSSascha Wildner
5475db2f26eSSascha Wildner val = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_CONFIG);
5485db2f26eSSascha Wildner bus_space_write_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_CONFIG,
5495db2f26eSSascha Wildner val | HPET_CNF_ENABLE);
5505db2f26eSSascha Wildner }
5515db2f26eSSascha Wildner
5525db2f26eSSascha Wildner static void
acpi_hpet_disable(struct acpi_hpet_softc * sc)5535db2f26eSSascha Wildner acpi_hpet_disable(struct acpi_hpet_softc *sc)
5545db2f26eSSascha Wildner {
5555db2f26eSSascha Wildner uint32_t val;
5565db2f26eSSascha Wildner
5575db2f26eSSascha Wildner val = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_CONFIG);
5585db2f26eSSascha Wildner bus_space_write_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_CONFIG,
5595db2f26eSSascha Wildner val & ~HPET_CNF_ENABLE);
5605db2f26eSSascha Wildner }
5615db2f26eSSascha Wildner
5625db2f26eSSascha Wildner static int
acpi_hpet_suspend(device_t dev)5635db2f26eSSascha Wildner acpi_hpet_suspend(device_t dev)
5645db2f26eSSascha Wildner {
5655db2f26eSSascha Wildner /*
5665db2f26eSSascha Wildner * According to IA-PC HPET specification rev 1.0a
5675db2f26eSSascha Wildner *
5685db2f26eSSascha Wildner * Page 10, 2.3.3:
5695db2f26eSSascha Wildner * "1. The Event Timer registers (including the main counter)
5705db2f26eSSascha Wildner * are not expected to be preserved through an S3, S4, or S5
5715db2f26eSSascha Wildner * state."
5725db2f26eSSascha Wildner *
5735db2f26eSSascha Wildner * Page 11, 2.3.3:
5745db2f26eSSascha Wildner * "3. The main counter is permitted, but not required, to run
5755db2f26eSSascha Wildner * during S1 or S2 states. ..."
5765db2f26eSSascha Wildner *
5775db2f26eSSascha Wildner * These mean we are not allowed to enter any of Sx states,
5785db2f26eSSascha Wildner * if HPET is used as the sys_cputimer.
5795db2f26eSSascha Wildner */
5805db2f26eSSascha Wildner if (sys_cputimer != &acpi_hpet_timer) {
5815db2f26eSSascha Wildner struct acpi_hpet_softc *sc;
5825db2f26eSSascha Wildner
5835db2f26eSSascha Wildner sc = device_get_softc(dev);
5845db2f26eSSascha Wildner acpi_hpet_disable(sc);
5855db2f26eSSascha Wildner
5865db2f26eSSascha Wildner return 0;
5875db2f26eSSascha Wildner } else {
5885db2f26eSSascha Wildner return EOPNOTSUPP;
5895db2f26eSSascha Wildner }
5905db2f26eSSascha Wildner }
5915db2f26eSSascha Wildner
5925db2f26eSSascha Wildner static int
acpi_hpet_resume(device_t dev)5935db2f26eSSascha Wildner acpi_hpet_resume(device_t dev)
5945db2f26eSSascha Wildner {
5955db2f26eSSascha Wildner if (sys_cputimer != &acpi_hpet_timer) {
5965db2f26eSSascha Wildner struct acpi_hpet_softc *sc;
5975db2f26eSSascha Wildner
5985db2f26eSSascha Wildner sc = device_get_softc(dev);
5995db2f26eSSascha Wildner acpi_hpet_enable(sc);
6005db2f26eSSascha Wildner }
6015db2f26eSSascha Wildner return 0;
6025db2f26eSSascha Wildner }
6035db2f26eSSascha Wildner
6045db2f26eSSascha Wildner /* Print some basic latency/rate information to assist in debugging. */
6055db2f26eSSascha Wildner static void
acpi_hpet_test(struct acpi_hpet_softc * sc)6065db2f26eSSascha Wildner acpi_hpet_test(struct acpi_hpet_softc *sc)
6075db2f26eSSascha Wildner {
6085db2f26eSSascha Wildner int i;
6095db2f26eSSascha Wildner uint32_t u1, u2;
6105db2f26eSSascha Wildner struct timeval b0, b1, b2;
6115db2f26eSSascha Wildner struct timespec ts;
6125db2f26eSSascha Wildner
6135db2f26eSSascha Wildner microuptime(&b0);
6145db2f26eSSascha Wildner microuptime(&b0);
6155db2f26eSSascha Wildner microuptime(&b1);
6165db2f26eSSascha Wildner u1 = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_MAIN_COUNTER);
6175db2f26eSSascha Wildner for (i = 1; i < 1000; i++) {
6185db2f26eSSascha Wildner u2 = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh,
6195db2f26eSSascha Wildner HPET_MAIN_COUNTER);
6205db2f26eSSascha Wildner }
6215db2f26eSSascha Wildner microuptime(&b2);
6225db2f26eSSascha Wildner u2 = bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_MAIN_COUNTER);
6235db2f26eSSascha Wildner
6245db2f26eSSascha Wildner timevalsub(&b2, &b1);
6255db2f26eSSascha Wildner timevalsub(&b1, &b0);
6265db2f26eSSascha Wildner timevalsub(&b2, &b1);
6275db2f26eSSascha Wildner
6285db2f26eSSascha Wildner TIMEVAL_TO_TIMESPEC(&b2, &ts);
6295db2f26eSSascha Wildner
6305db2f26eSSascha Wildner device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
6315db2f26eSSascha Wildner (long)b2.tv_sec, b2.tv_usec, u1, u2, u2 - u1);
6325db2f26eSSascha Wildner
6335db2f26eSSascha Wildner device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
6345db2f26eSSascha Wildner }
635