summaryrefslogtreecommitdiffstats
path: root/sys/arm/ti/am335x/am335x_ecap.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/ti/am335x/am335x_ecap.c')
-rw-r--r--sys/arm/ti/am335x/am335x_ecap.c100
1 files changed, 99 insertions, 1 deletions
diff --git a/sys/arm/ti/am335x/am335x_ecap.c b/sys/arm/ti/am335x/am335x_ecap.c
index eba5f72..ea05a70 100644
--- a/sys/arm/ti/am335x/am335x_ecap.c
+++ b/sys/arm/ti/am335x/am335x_ecap.c
@@ -25,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/arm/ti/am335x/am335x_ecap.c 283276 2015-05-22 03:16:18Z gonzo $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -53,10 +53,27 @@ __FBSDID("$FreeBSD$");
#define ECAP_CAP2 0x0C
#define ECAP_CAP3 0x10
#define ECAP_CAP4 0x14
+#define ECAP_ECCTL1 0x28
+#define ECCTL1_CAPLDEN (1 << 8)
+#define ECCTL1_CTRRST4 (1 << 7)
+#define ECCTL1_CTRRST3 (1 << 5)
+#define ECCTL1_CTRRST2 (1 << 3)
+#define ECCTL1_CTRRST1 (1 << 1)
#define ECAP_ECCTL2 0x2A
#define ECCTL2_MODE_APWM (1 << 9)
#define ECCTL2_SYNCO_SEL (3 << 6)
#define ECCTL2_TSCTRSTOP_FREERUN (1 << 4)
+#define ECCTL2_REARM (1 << 3)
+#define ECCTL2_STOP_WRAP_EVENT1 (0 << 1)
+#define ECCTL2_STOP_WRAP_EVENT2 (1 << 1)
+#define ECCTL2_STOP_WRAP_EVENT3 (2 << 1)
+#define ECCTL2_STOP_WRAP_EVENT4 (3 << 1)
+#define ECCTL2_CONT_ONESHT (1 << 0)
+#define ECAP_ECEINT 0x2C
+#define ECEINT_CEVT4 (1 << 4)
+#define ECAP_ECFLG 0x2E
+#define ECAP_ECCLR 0x30
+#define ECCLR_MASK 0xff
#define ECAP_READ2(_sc, reg) bus_read_2((_sc)->sc_mem_res, reg);
#define ECAP_WRITE2(_sc, reg, value) \
@@ -78,8 +95,13 @@ static device_detach_t am335x_ecap_detach;
struct am335x_ecap_softc {
device_t sc_dev;
struct mtx sc_mtx;
+ struct resource *sc_irq_res;
struct resource *sc_mem_res;
+ int sc_ecap_mode;
+ int sc_irq_rid;
int sc_mem_rid;
+ uint32_t sc_period;
+ void *sc_intrhand;
};
static device_method_t am335x_ecap_methods[] = {
@@ -119,6 +141,9 @@ am335x_pwm_config_ecap(int unit, int period, int duty)
return (EINVAL);
sc = device_get_softc(dev);
+ if (sc->sc_ecap_mode)
+ return (EINVAL);
+
PWM_LOCK(sc);
reg = ECAP_READ2(sc, ECAP_ECCTL2);
@@ -138,6 +163,31 @@ am335x_pwm_config_ecap(int unit, int period, int duty)
return (0);
}
+static void
+am335x_ecap_intr(void *arg)
+{
+ struct am335x_ecap_softc *sc;
+ uint16_t reg;
+ uint64_t v;
+
+ sc = (struct am335x_ecap_softc *)arg;
+ PWM_LOCK(sc);
+ v = ECAP_READ4(sc, ECAP_CAP1);
+ v += ECAP_READ4(sc, ECAP_CAP2);
+ v += ECAP_READ4(sc, ECAP_CAP3);
+ v += ECAP_READ4(sc, ECAP_CAP4);
+ v /= 4;
+ sc->sc_period = (uint32_t)v;
+
+ reg = ECAP_READ2(sc, ECAP_ECFLG);
+ ECAP_WRITE2(sc, ECAP_ECCLR, ECCLR_MASK);
+
+ reg = ECAP_READ2(sc, ECAP_ECCTL2);
+ reg |= ECCTL2_REARM;
+ ECAP_WRITE2(sc, ECAP_ECCTL2, reg);
+ PWM_UNLOCK(sc);
+}
+
static int
am335x_ecap_probe(device_t dev)
{
@@ -157,9 +207,11 @@ static int
am335x_ecap_attach(device_t dev)
{
struct am335x_ecap_softc *sc;
+ uint16_t reg;
sc = device_get_softc(dev);
sc->sc_dev = dev;
+ sc->sc_ecap_mode = 1;
PWM_LOCK_INIT(sc);
@@ -169,6 +221,45 @@ am335x_ecap_attach(device_t dev)
device_printf(dev, "cannot allocate memory resources\n");
goto fail;
}
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_irq_rid, RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+ sc->sc_mem_res);
+ device_printf(dev, "cannot allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ /* Hook up our interrupt handler. */
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, am335x_ecap_intr, sc, &sc->sc_intrhand)) {
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+ sc->sc_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+ sc->sc_mem_res);
+ device_printf(dev, "cannot setup the interrupt handler\n");
+ return (ENXIO);
+ }
+
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->sc_dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)),
+ OID_AUTO, "period", CTLFLAG_RD, &sc->sc_period, 0, "eCMP period");
+
+ /* One shot, wrap counter after read event 4, no sync, stopped. */
+ ECAP_WRITE2(sc, ECAP_ECCTL2, ECCTL2_SYNCO_SEL |
+ ECCTL2_STOP_WRAP_EVENT4 | ECCTL2_CONT_ONESHT);
+ /* Delta mode, rising edge. */
+ ECAP_WRITE2(sc, ECAP_ECCTL1, ECCTL1_CAPLDEN | ECCTL1_CTRRST1 |
+ ECCTL1_CTRRST2 | ECCTL1_CTRRST3 | ECCTL1_CTRRST4);
+ /* Restart counter */
+ ECAP_WRITE4(sc, ECAP_TSCTR, 0);
+ /* Enable overflow interrupt. */
+ ECAP_WRITE2(sc, ECAP_ECCLR, ECCLR_MASK);
+ ECAP_WRITE2(sc, ECAP_ECEINT, ECEINT_CEVT4);
+ /* Start count. */
+ reg = ECAP_READ2(sc, ECAP_ECCTL2);
+ reg |= ECCTL2_TSCTRSTOP_FREERUN;
+ ECAP_WRITE2(sc, ECAP_ECCTL2, reg);
return (0);
@@ -185,6 +276,13 @@ am335x_ecap_detach(device_t dev)
sc = device_get_softc(dev);
PWM_LOCK(sc);
+ ECAP_WRITE2(sc, ECAP_ECEINT, 0);
+ ECAP_WRITE2(sc, ECAP_ECCLR, ECCLR_MASK);
+ if (sc->sc_intrhand)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->sc_irq_rid, sc->sc_irq_res);
if (sc->sc_mem_res)
bus_release_resource(dev, SYS_RES_MEMORY,
sc->sc_mem_rid, sc->sc_mem_res);
OpenPOWER on IntegriCloud