Attached are some patches that update the recently added tcpcib(4)
driver with support for using the HPET as a timecounter, based on
amdpcib(4).

Here's the updated dmesg:

---8<---
tcpcib0 at pci0 dev 31 function 0 "Intel E600 LPC" rev 0x00: 64-bit 14318179 Hz 
timer rev 1: watchdog
---8<---

Here's the sysctl output from my Soekris net6501:

# sysctl kern.timecounter
kern.timecounter.tick=1
kern.timecounter.timestepwarnings=0
kern.timecounter.hardware=tcpcib0
kern.timecounter.choice=i8254(0) tcpcib0(1000) dummy(-1000000)

I'm aware there's also acpihpet(4) which might normally take control of
this bit of the hardware however the Soekris net6501 at least lacks any
ACPI support at present so the hardware is currently unused. I presume
on an E600-based system with ACPI, acpihpet(4) will bagsy the hardware
first and so the bus_space_map() call in tcpcib(4) will silently fail.

I also removed a couple of overzealous tcpcib_wdt_unlock()'s that are
not needed before writing to the registers that follow, I re-tested and
the watchdog still triggers as expected.

Matt

--- sys/dev/pci/tcpcib.c.orig   Wed May 30 12:19:41 2012
+++ sys/dev/pci/tcpcib.c        Wed May 30 12:59:23 2012
@@ -17,12 +17,13 @@
  */
 
 /*
- * Intel Atom E600 series LPC bridge also containing watchdog
+ * Intel Atom E600 series LPC bridge also containing HPET and watchdog
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/device.h>
+#include <sys/timetc.h>
 
 #include <machine/bus.h>
 
@@ -52,18 +53,42 @@
 #define        E600_WDT_WDTLR_ENABLE   (1 << 1)        /* Watchdog Timer 
Enable */
 #define        E600_WDT_WDTLR_TIMEOUT  (1 << 2)        /* WDT Timeout 
Configuration */
 
+#define        E600_HPET_BASE          0xfed00000      /* HPET register base */
+#define        E600_HPET_SIZE          0x00000400      /* HPET register size */
+
+#define        E600_HPET_GCID          0x000           /* Capabilities and ID 
*/
+#define        E600_HPET_GCID_WIDTH    (1 << 13)       /* Counter Size */
+#define        E600_HPET_PERIOD        0x004           /* Counter Tick Period 
*/
+#define        E600_HPET_GC            0x010           /* General 
Configuration */
+#define        E600_HPET_GC_ENABLE     (1 << 0)        /* Overall Enable */
+#define        E600_HPET_GIS           0x020           /* General Interrupt 
Status */
+#define        E600_HPET_MCV           0x0f0           /* Main Counter Value */
+#define        E600_HPET_T0C           0x100           /* Timer 0 Config and 
Capabilities */
+#define        E600_HPET_T0CV          0x108           /* Timer 0 Comparator 
Value */
+#define        E600_HPET_T1C           0x120           /* Timer 1 Config and 
Capabilities */
+#define        E600_HPET_T1CV          0x128           /* Timer 1 Comparator 
Value */
+#define        E600_HPET_T2C           0x140           /* Timer 2 Config and 
Capabilities */
+#define        E600_HPET_T2CV          0x148           /* Timer 2 Comparator 
Value */
+
 struct tcpcib_softc {
        struct device sc_dev;
 
        /* Keep track of which parts of the hardware are active */
        int sc_active;
 #define        E600_WDT_ACTIVE         (1 << 0)
+#define        E600_HPET_ACTIVE        (1 << 1)
 
        /* Watchdog interface */
        bus_space_tag_t sc_wdt_iot;
        bus_space_handle_t sc_wdt_ioh;
 
        int sc_wdt_period;
+
+       /* High Precision Event Timer */
+       bus_space_tag_t sc_hpet_iot;
+       bus_space_handle_t sc_hpet_ioh;
+
+       struct timecounter sc_hpet_timecounter;
 };
 
 struct cfdriver tcpcib_cd = {
@@ -79,6 +104,8 @@
 void    tcpcib_wdt_start(struct tcpcib_softc *);
 void    tcpcib_wdt_stop(struct tcpcib_softc *);
 
+u_int   tcpcib_hpet_get_timecount(struct timecounter *tc);
+
 struct cfattach tcpcib_ca = {
        sizeof(struct tcpcib_softc), tcpcib_match, tcpcib_attach,
        NULL, tcpcib_activate
@@ -112,7 +139,6 @@
         * Set watchdog to perform a cold reset toggling the GPIO pin and the
         * prescaler set to 1ms-10m resolution
         */
-       tcpcib_wdt_unlock(sc);
        bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTCR,
            E600_WDT_WDTCR_ENABLE);
        tcpcib_wdt_unlock(sc);
@@ -129,7 +155,6 @@
 tcpcib_wdt_start(struct tcpcib_softc *sc)
 {
        /* Enable watchdog */
-       tcpcib_wdt_unlock(sc);
        bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR,
            E600_WDT_WDTLR_ENABLE);
 }
@@ -141,7 +166,6 @@
        tcpcib_wdt_unlock(sc);
        bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1,
            E600_WDT_RR1_RELOAD);
-       tcpcib_wdt_unlock(sc);
        bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR, 0);
 }
 
@@ -160,10 +184,43 @@
 {
        struct tcpcib_softc *sc = (struct tcpcib_softc *)self;
        struct pci_attach_args *pa = aux;
-       u_int32_t reg, wdtbase;
+       struct timecounter *tc = &sc->sc_hpet_timecounter;
+       u_int32_t reg, wdtbase, period;
 
        sc->sc_active = 0;
 
+       /* High Precision Event Timer */
+       sc->sc_hpet_iot = pa->pa_memt;
+       if (bus_space_map(sc->sc_hpet_iot, E600_HPET_BASE, E600_HPET_SIZE, 0,
+           &sc->sc_hpet_ioh) == 0) {
+               reg = bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
+                   E600_HPET_GCID);
+
+               tc->tc_get_timecount = tcpcib_hpet_get_timecount;
+               /* XXX 64-bit counter is not supported! */
+               tc->tc_counter_mask = 0xffffffff;
+
+               period = bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
+                   E600_HPET_PERIOD);
+               /* femtosecs -> Hz */
+               tc->tc_frequency = 1000000000000000ULL / period;
+
+               tc->tc_name = sc->sc_dev.dv_xname;
+               tc->tc_quality = 1000;
+               tc->tc_priv = sc;
+               tc_init(tc);
+
+               /* Enable counting */
+               bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
+                   E600_HPET_GC, E600_HPET_GC_ENABLE);
+
+               sc->sc_active |= E600_HPET_ACTIVE;
+
+               printf(": %d-bit %llu Hz timer rev %d",
+                   (reg & E600_HPET_GCID_WIDTH) ? 64 : 32, tc->tc_frequency,
+                   reg & 0xff);
+       }
+
        /* Map Watchdog I/O space */
        reg = pci_conf_read(pa->pa_pc, pa->pa_tag, E600_LPC_WDTBA);
        wdtbase = reg & 0xffff;
@@ -235,6 +292,9 @@
                        } else
                                tcpcib_wdt_stop(sc);
                }
+               if (sc->sc_active & E600_HPET_ACTIVE)
+                       bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
+                           E600_HPET_GC, E600_HPET_GC_ENABLE);
                break;
        }
        return (0);
@@ -266,4 +326,14 @@
        sc->sc_wdt_period = period;
 
        return (period);
+}
+
+u_int
+tcpcib_hpet_get_timecount(struct timecounter *tc)
+{
+       struct tcpcib_softc *sc = tc->tc_priv;
+
+       /* XXX 64-bit counter is not supported! */
+       return bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
+           E600_HPET_MCV);
 }
--- share/man/man4/man4.i386/tcpcib.4.orig      Wed May 30 12:38:14 2012
+++ share/man/man4/man4.i386/tcpcib.4   Wed May 30 13:03:26 2012
@@ -19,7 +19,7 @@
 .Os
 .Sh NAME
 .Nm tcpcib
-.Nd Intel Atom E600 series LPC bridge and watchdog timer
+.Nd Intel Atom E600 series LPC bridge, timecounter and watchdog timer
 .Sh SYNOPSIS
 .Cd "tcpcib* at pci?"
 .Cd "isa* at tcpcib?"
@@ -27,7 +27,11 @@
 The
 .Nm
 driver provides support for the Intel Atom E600 series LPC bridge and
-provides the standard
+implements a 64-bit 14.3 MHz timecounter using the HPET timer.
+.Pp
+The
+.Nm
+driver also provides the standard
 .Xr watchdog 4
 interface to the watchdog timer and may be used with
 .Xr watchdogd 8 .
@@ -60,6 +64,6 @@
 .Sh BUGS
 Apart from the core
 .Xr pcib 4
-functionality and the
+functionality, timecounter and the
 .Xr watchdog 4
 interface, the driver doesn't support any other aspects of the hardware.
--- share/man/man4/man4.amd64/tcpcib.4.orig     Wed May 30 12:55:24 2012
+++ share/man/man4/man4.amd64/tcpcib.4  Wed May 30 13:03:42 2012
@@ -19,7 +19,7 @@
 .Os
 .Sh NAME
 .Nm tcpcib
-.Nd Intel Atom E600 series LPC bridge and watchdog timer
+.Nd Intel Atom E600 series LPC bridge, timecounter and watchdog timer
 .Sh SYNOPSIS
 .Cd "tcpcib* at pci?"
 .Cd "isa* at tcpcib?"
@@ -27,7 +27,11 @@
 The
 .Nm
 driver provides support for the Intel Atom E600 series LPC bridge and
-provides the standard
+implements a 64-bit 14.3 MHz timecounter using the HPET timer.
+.Pp
+The
+.Nm
+driver also provides the standard
 .Xr watchdog 4
 interface to the watchdog timer and may be used with
 .Xr watchdogd 8 .
@@ -60,6 +64,6 @@
 .Sh BUGS
 Apart from the core
 .Xr pcib 4
-functionality and the
+functionality, timecounter and the
 .Xr watchdog 4
 interface, the driver doesn't support any other aspects of the hardware.

Reply via email to