diff options
Diffstat (limited to 'sys/arm/ti/am335x/am335x_ecap.c')
-rw-r--r-- | sys/arm/ti/am335x/am335x_ecap.c | 100 |
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); |