From 59041529071d5aa235ff8b929e363a64073d74b9 Mon Sep 17 00:00:00 2001
From: Dmitry Komissaroff <aunoor@gmail.com>
Date: Mon, 12 Jan 2009 15:24:24 +0300
Subject: [PATCH] Adding support for HPET timer

---
 sys/dev/acpica5/Makefile    |    2 +-
 sys/dev/acpica5/acpi_hpet.c |  351 +++++++++++++++++++++++++++++++++++++++++++
 sys/dev/acpica5/acpi_hpet.h |   71 +++++++++
 3 files changed, 423 insertions(+), 1 deletions(-)
 create mode 100644 sys/dev/acpica5/acpi_hpet.c
 create mode 100644 sys/dev/acpica5/acpi_hpet.h

diff --git a/sys/dev/acpica5/Makefile b/sys/dev/acpica5/Makefile
index 958323d..ff7f8f9 100644
--- a/sys/dev/acpica5/Makefile
+++ b/sys/dev/acpica5/Makefile
@@ -69,7 +69,7 @@ SRCS+=  acpi_package.c
 # SRCS+=	acpi_pci.c acpi_pcib.c acpi_pcib_acpi.c acpi_pcib_pci.c
 # SRCS+=	acpi_pci_link.c
 SRCS+=  acpi_powerres.c acpi_resource.c acpi_thermal.c
-SRCS+=  acpi_timer.c acpi_if.c
+SRCS+=  acpi_timer.c acpi_hpet.c acpi_if.c
 SRCS+=  OsdDebug.c
 SRCS+=  OsdHardware.c OsdInterface.c OsdInterrupt.c OsdMemory.c OsdSchedule.c
 SRCS+=  OsdStream.c OsdSynch.c OsdTable.c OsdEnvironment.c
diff --git a/sys/dev/acpica5/acpi_hpet.c b/sys/dev/acpica5/acpi_hpet.c
new file mode 100644
index 0000000..da5d586
--- /dev/null
+++ b/sys/dev/acpica5/acpi_hpet.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 2005 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/acpica/acpi_hpet.c,v 1.12.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/systimer.h>
+#include <sys/rman.h>
+
+#include <machine/lock.h>
+#include <bus/pci/pcivar.h>
+
+#include "acpi.h"
+#include "acpivar.h"
+#include "acpi_hpet.h"
+
+/* Hooks for the ACPI CA debugging infrastructure */
+#define _COMPONENT	ACPI_TIMER
+ACPI_MODULE_NAME("HPET")
+
+static device_t			acpi_hpet_dev;
+static struct resource		*acpi_hpet_reg;
+static bus_space_handle_t	acpi_hpet_bsh;
+static bus_space_tag_t		acpi_hpet_bst;
+static sysclock_t		acpi_counter_mask;
+static sysclock_t		acpi_last_counter;
+
+struct acpi_hpet_softc {
+	device_t		dev;
+	struct resource		*mem_res;
+	ACPI_HANDLE		handle;
+};
+
+#define DEV_HPET(x)	(acpi_get_magic(x) == (uintptr_t)&acpi_hpet_devclass)
+
+static int acpi_hpet_resume(device_t dev);
+static int acpi_hpet_suspend(device_t dev);
+static void hpet_enable(struct acpi_hpet_softc *sc);
+static void hpet_disable(struct acpi_hpet_softc *sc);
+
+static sysclock_t acpi_hpet_get_timecount(void);
+static void acpi_hpet_construct(struct cputimer *timer, sysclock_t oldclock);
+
+static char *hpet_ids[] = { "PNP0103", NULL };
+
+static struct cputimer acpi_hpet_timer = {
+	SLIST_ENTRY_INITIALIZER,
+	"HPET",
+	CPUTIMER_PRI_ACPI,
+	CPUTIMER_ACPI,
+	acpi_hpet_get_timecount,
+	cputimer_default_fromhz,
+	cputimer_default_fromus,
+	acpi_hpet_construct,
+	cputimer_default_destruct,
+	900,
+	0, 0, 0
+};
+
+static int	acpi_hpet_identify(driver_t *driver, device_t parent);
+static int	acpi_hpet_probe(device_t dev);
+static int	acpi_hpet_attach(device_t dev);
+
+static void acpi_hpet_test(struct acpi_hpet_softc *sc);
+
+static device_method_t acpi_hpet_methods[] = {
+    DEVMETHOD(device_identify,	acpi_hpet_identify),
+    DEVMETHOD(device_probe,	acpi_hpet_probe),
+    DEVMETHOD(device_attach,	acpi_hpet_attach),
+    DEVMETHOD(device_suspend,	acpi_hpet_suspend),
+    DEVMETHOD(device_resume,	acpi_hpet_resume),
+    {0, 0}
+};
+
+static driver_t acpi_hpet_driver = {
+    "acpi_hpet",
+    acpi_hpet_methods,
+    0,
+};
+
+static devclass_t acpi_hpet_devclass;
+DRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
+MODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
+
+static u_int
+acpi_hpet_read(void)
+{
+    return (bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_MAIN_COUNTER));
+}
+
+/*
+ * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
+ * we will be using.
+ */
+static int
+acpi_hpet_identify(driver_t *driver, device_t parent)
+{
+	ACPI_TABLE_HPET *hpet;
+	ACPI_TABLE_HEADER *hdr;
+	ACPI_STATUS	status;
+	device_t	child;
+
+	/* Only one HPET device can be added. */
+	if (devclass_get_device(acpi_hpet_devclass, 0))
+		return (ENXIO);
+
+	/* Currently, ID and minimum clock tick info is unused. */
+
+	status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr);
+	if (ACPI_FAILURE(status))
+		return (ENXIO);
+
+	/*
+	 * The unit number could be derived from hdr->Sequence but we only
+	 * support one HPET device.
+	 */
+	hpet = (ACPI_TABLE_HPET *)hdr;
+	if (hpet->Sequence != 0)
+		kprintf("ACPI HPET table warning: Sequence is non-zero (%d)\n",
+		    hpet->Sequence);
+	child = BUS_ADD_CHILD(parent, parent, 0, "acpi_hpet", 0);
+	if (child == NULL) {
+		kprintf("%s: can't add child\n", __func__);
+		return (ENXIO);
+	}
+
+	/* Record a magic value so we can detect this device later. */
+	acpi_set_magic(child, (uintptr_t)&acpi_hpet_devclass);
+	bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
+	    HPET_MEM_WIDTH);
+	return (0);
+}
+
+static int
+acpi_hpet_probe(device_t dev)
+{
+	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
+
+	if (!acpi_enabled("hpet"))
+		return (ENXIO);
+	if (!DEV_HPET(dev) &&
+	    (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
+	    device_get_unit(dev) != 0))
+		return (ENXIO);
+
+	device_set_desc(dev, "High Precision Event Timer");
+	return (0);
+}
+
+static int
+acpi_hpet_attach(device_t dev)
+{
+	struct acpi_hpet_softc *sc;
+	int rid;
+	uint32_t val, val2;
+	uintmax_t freq;
+
+	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->handle = acpi_get_handle(dev);
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL)
+		return (ENOMEM);
+
+	/* Validate that we can access the whole region. */
+	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
+		device_printf(dev, "memory region width %ld too small\n",
+		    rman_get_size(sc->mem_res));
+		bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
+		return (ENXIO);
+	}
+
+	acpi_hpet_bsh = rman_get_bushandle(sc->mem_res);
+	acpi_hpet_bst = rman_get_bustag(sc->mem_res);
+
+	/* Be sure timer is enabled. */
+	hpet_enable(sc);
+
+	/* Read basic statistics about the timer. */
+	val = bus_read_4(sc->mem_res, HPET_PERIOD);
+	if (val == 0) {
+		device_printf(dev, "invalid period\n");
+		hpet_disable(sc);
+		bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
+		return (ENXIO);
+	}
+
+	freq = (1000000000000000LL + val / 2) / val;
+	if (bootverbose) {
+		val = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
+		device_printf(dev,
+		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
+		    val >> 16, val & HPET_CAP_REV_ID,
+		    (val & HPET_CAP_NUM_TIM) >> 8, freq,
+		    (val & HPET_CAP_LEG_RT) ? " legacy_route" : "",
+		    (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : "");
+	}
+
+	if (ktestenv("debug.acpi.hpet_test"))
+		acpi_hpet_test(sc);
+
+	/*
+	 * Don't attach if the timer never increments.  Since the spec
+	 * requires it to be at least 10 MHz, it has to change in 1 us.
+	 */
+	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
+	DELAY(1);
+	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
+	if (val == val2) {
+		device_printf(dev, "HPET never increments, disabling\n");
+		hpet_disable(sc);
+		bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
+		return (ENXIO);
+	}
+
+	acpi_hpet_timer.freq = freq;
+
+    cputimer_register(&acpi_hpet_timer);
+
+#if 0
+/* We don't want setting HEPT default timer*/
+    cputimer_select(&acpi_hpet_timer, 0);
+#endif
+
+	return (0);
+
+}
+
+/*
+ * Construct the timer.  Adjust the base so the system clock does not
+ * jump weirdly.
+ */
+static void
+acpi_hpet_construct(struct cputimer *timer, sysclock_t oldclock)
+{
+    timer->base = 0;
+    timer->base = oldclock - acpi_hpet_get_timecount();
+}
+
+static sysclock_t
+acpi_hpet_get_timecount(void)
+{
+    return acpi_hpet_read() + acpi_hpet_timer.base;  
+}
+
+static void
+hpet_enable(struct acpi_hpet_softc *sc)
+{
+	uint32_t val;
+
+	val = bus_read_4(sc->mem_res, HPET_CONFIG);
+	bus_write_4(sc->mem_res, HPET_CONFIG, val | HPET_CNF_ENABLE);
+}
+
+static void
+hpet_disable(struct acpi_hpet_softc *sc)
+{
+	uint32_t val;
+
+	val = bus_read_4(sc->mem_res, HPET_CONFIG);
+	bus_write_4(sc->mem_res, HPET_CONFIG, val & ~HPET_CNF_ENABLE);
+}
+
+static int
+acpi_hpet_suspend(device_t dev)
+{
+	struct acpi_hpet_softc *sc;
+
+	/*
+	 * Disable the timer during suspend.  The timer will not lose
+	 * its state in S1 or S2, but we are required to disable
+	 * it.
+	 */
+	sc = device_get_softc(dev);
+	hpet_disable(sc);
+
+	return (0);
+}
+
+static int
+acpi_hpet_resume(device_t dev)
+{
+	struct acpi_hpet_softc *sc;
+
+	/* Re-enable the timer after a resume to keep the clock advancing. */
+	sc = device_get_softc(dev);
+	hpet_enable(sc);
+
+	return (0);
+}
+ 
+/* Print some basic latency/rate information to assist in debugging. */
+static void
+acpi_hpet_test(struct acpi_hpet_softc *sc)
+{
+	int i;
+	uint32_t u1, u2;
+	struct timeval b0, b1, b2;
+	struct timespec ts;
+
+	microuptime(&b0);
+	microuptime(&b0);
+	microuptime(&b1);
+	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
+	for (i = 1; i < 1000; i++)
+		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
+	microuptime(&b2);
+	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
+
+	timevalsub(&b2, &b1);
+	timevalsub(&b1, &b0);
+	timevalsub(&b2, &b1);
+
+	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
+	    (long)b2.tv_sec, b2.tv_usec, u1, u2, u2 - u1);
+
+	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
+}
diff --git a/sys/dev/acpica5/acpi_hpet.h b/sys/dev/acpica5/acpi_hpet.h
new file mode 100644
index 0000000..f66c6c5
--- /dev/null
+++ b/sys/dev/acpica5/acpi_hpet.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2005 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/acpica/acpi_hpet.h,v 1.1.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $
+ */
+
+#ifndef __ACPI_HPET_H__
+#define	__ACPI_HPET_H__
+
+#define HPET_MEM_WIDTH		0x400	/* Expected memory region size */
+
+/* General registers */
+#define HPET_CAPABILITIES	0x0	/* General capabilities and ID */
+#define	HPET_CAP_VENDOR_ID	0xffff0000
+#define	HPET_CAP_LEG_RT		0x00008000
+#define	HPET_CAP_COUNT_SIZE	0x00002000 /* 1 = 64-bit, 0 = 32-bit */
+#define	HPET_CAP_NUM_TIM	0x00001f00
+#define	HPET_CAP_REV_ID		0x000000ff
+#define HPET_PERIOD		0x4	/* Period (1/hz) of timer */
+#define HPET_CONFIG		0x10	/* General configuration register */
+#define	HPET_CNF_LEG_RT		0x00000002
+#define	HPET_CNF_ENABLE		0x00000001
+#define	HPET_ISR		0x20	/* General interrupt status register */
+#define HPET_MAIN_COUNTER	0xf0	/* Main counter register */
+
+/* Timer registers */
+#define	HPET_TIMER_CAP_CNF(x)	((x) * 0x20 + 0x100)
+#define	HPET_TCAP_INT_ROUTE	0xffffffff00000000
+#define	HPET_TCAP_FSB_INT_DEL	0x00008000
+#define	HPET_TCNF_FSB_EN	0x00004000
+#define	HPET_TCNF_INT_ROUTE	0x00003e00
+#define	HPET_TCNF_32MODE	0x00000100
+#define	HPET_TCNF_VAL_SET	0x00000040
+#define	HPET_TCAP_SIZE		0x00000020 /* 1 = 64-bit, 0 = 32-bit */
+#define	HPET_TCAP_PER_INT	0x00000010 /* Supports periodic interrupts */
+#define	HPET_TCNF_TYPE		0x00000008 /* 1 = periodic, 0 = one-shot */
+#define	HPET_TCNF_INT_ENB	0x00000004
+#define	HPET_TCNT_INT_TYPE	0x00000002 /* 1 = level triggered, 0 = edge */
+#define	HPET_TIMER_COMPARATOR(x) ((x) * 0x20 + 0x108)
+#define	HPET_TIMER_FSB_VAL(x)	((x) * 0x20 + 0x110)
+#define	HPET_TIMER_FSB_ADDR(x)	((x) * 0x20 + 0x114)
+
+
+#define bus_read_4(r, o) \
+		   bus_space_read_4((r)->r_bustag, (r)->r_bushandle, (o))
+#define bus_write_4(r, o, v) \
+		    bus_space_write_4((r)->r_bustag, (r)->r_bushandle, (o), (v))
+
+#endif /* !__ACPI_HPET_H__ */
-- 
1.6.0

