Page MenuHomeFreeBSD

D43283.diff
No OneTemporary

D43283.diff

diff --git a/sys/arm/ti/am335x/am335x_ecap.c b/sys/arm/ti/am335x/am335x_ecap.c
--- a/sys/arm/ti/am335x/am335x_ecap.c
+++ b/sys/arm/ti/am335x/am335x_ecap.c
@@ -42,7 +42,14 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#include "am335x_pwm.h"
+#include <dev/pwm/pwmc.h>
+#include <dev/extres/clk/clk.h>
+#include <machine/_inttypes.h>
+
+#include "pwmbus_if.h"
+
+#define NS_PER_SEC 1000000000
+#define NUM_CHANNELS 1
#define ECAP_TSCTR 0x00
#define ECAP_CAP1 0x08
@@ -50,6 +57,7 @@
#define ECAP_CAP3 0x10
#define ECAP_CAP4 0x14
#define ECAP_ECCTL2 0x2A
+#define ECCTL2_APWMPOL (1 << 10)
#define ECCTL2_MODE_APWM (1 << 9)
#define ECCTL2_SYNCO_SEL (3 << 6)
#define ECCTL2_TSCTRSTOP_FREERUN (1 << 4)
@@ -63,86 +71,191 @@
#define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define PWM_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
#define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
device_get_nameunit(_sc->sc_dev), "am335x_ecap softc", MTX_DEF)
#define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
-static device_probe_t am335x_ecap_probe;
-static device_attach_t am335x_ecap_attach;
-static device_detach_t am335x_ecap_detach;
-
struct am335x_ecap_softc {
device_t sc_dev;
+ device_t sc_busdev;
struct mtx sc_mtx;
struct resource *sc_mem_res;
int sc_mem_rid;
+
+ clk_t sc_clkfck;
+
+ /* Things used for configuration via pwm(9) api. */
+ uint64_t sc_clkfreq; /* frequency in Hz */
+ u_int sc_period; /* duration in ns */
+ u_int sc_duty; /* on duration, in ns */
+ bool sc_enabled; /* channel enabled? */
+ bool sc_inverted; /* signal inverted? */
};
static struct ofw_compat_data compat_data[] = {
{"ti,am3352-ecap", true},
{"ti,am33xx-ecap", true},
+ {"ti,da850-ecap", true},
+ {"ti,am4372-ecap", true},
+ {"ti,dra746-ecap", true},
+ {"ti,k2g-ecap", true},
+ {"ti,am654-ecap", true},
+ {"ti,am64-ecap", true},
{NULL, false},
};
SIMPLEBUS_PNP_INFO(compat_data);
-static device_method_t am335x_ecap_methods[] = {
- DEVMETHOD(device_probe, am335x_ecap_probe),
- DEVMETHOD(device_attach, am335x_ecap_attach),
- DEVMETHOD(device_detach, am335x_ecap_detach),
+static void
+am335x_ecap_cfg_enable(struct am335x_ecap_softc *sc, u_int chan, bool enable)
+{
+ uint16_t regval;
- DEVMETHOD_END
-};
+ sc->sc_enabled = enable;
-static driver_t am335x_ecap_driver = {
- "am335x_ecap",
- am335x_ecap_methods,
- sizeof(struct am335x_ecap_softc),
-};
+ PWM_LOCK_ASSERT(sc);
+ regval = ECAP_READ2(sc, ECAP_ECCTL2);
+ if (enable == true)
+ regval |= (ECCTL2_MODE_APWM | ECCTL2_TSCTRSTOP_FREERUN);
+ else
+ regval &= ~(ECCTL2_MODE_APWM | ECCTL2_TSCTRSTOP_FREERUN);
+ ECAP_WRITE2(sc, ECAP_ECCTL2, regval);
+}
-/*
- * API function to set period/duty cycles for ECAPx
- */
-int
-am335x_pwm_config_ecap(int unit, int period, int duty)
+static int
+am335x_ecap_channel_count(device_t dev, u_int *nchannel)
+{
+ *nchannel = NUM_CHANNELS;
+
+ return (0);
+}
+
+static int
+am335x_ecap_channel_config(device_t dev, u_int channel, u_int period_ns,
+ u_int duty_ns)
{
- device_t dev;
struct am335x_ecap_softc *sc;
- uint16_t reg;
+ uint32_t period, duty;
- dev = devclass_get_device(devclass_find(am335x_ecap_driver.name), unit);
- if (dev == NULL)
- return (ENXIO);
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ PWM_LOCK(sc);
- if (duty > period)
+ /* convert period_ns/duty_ns to counter values */
+ period = period_ns * sc->sc_clkfreq / NS_PER_SEC;
+ duty = duty_ns * sc->sc_clkfreq / NS_PER_SEC;
+
+ sc->sc_period = period_ns;
+ ECAP_WRITE2(sc, ECAP_CAP3, period - 1);
+
+ sc->sc_duty = duty_ns;
+ ECAP_WRITE2(sc, ECAP_CAP4, duty);
+
+ PWM_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+am335x_ecap_channel_get_config(device_t dev, u_int channel,
+ u_int *period_ns, u_int *duty_ns)
+{
+ struct am335x_ecap_softc *sc;
+
+ if (channel >= NUM_CHANNELS)
return (EINVAL);
- if (period == 0)
+ sc = device_get_softc(dev);
+ *period_ns = sc->sc_period;
+ *duty_ns = sc->sc_duty;
+
+ return (0);
+}
+
+static int
+am335x_ecap_channel_set_flags(device_t dev, u_int channel,
+ uint32_t flags)
+{
+ uint16_t regval;
+ struct am335x_ecap_softc *sc;
+
+ if (channel >= NUM_CHANNELS)
return (EINVAL);
sc = device_get_softc(dev);
+
PWM_LOCK(sc);
- reg = ECAP_READ2(sc, ECAP_ECCTL2);
- reg |= ECCTL2_MODE_APWM | ECCTL2_TSCTRSTOP_FREERUN | ECCTL2_SYNCO_SEL;
- ECAP_WRITE2(sc, ECAP_ECCTL2, reg);
+ sc->sc_inverted = flags & PWM_POLARITY_INVERTED;
- /* CAP3 in APWM mode is APRD shadow register */
- ECAP_WRITE4(sc, ECAP_CAP3, period - 1);
+ regval = ECAP_READ2(sc, ECAP_ECCTL2);
+ if (sc->sc_inverted == true)
+ regval |= (ECCTL2_APWMPOL);
+ else
+ regval &= ~(ECCTL2_APWMPOL);
+ ECAP_WRITE2(sc, ECAP_ECCTL2, regval);
+
+ PWM_UNLOCK(sc);
- /* CAP4 in APWM mode is ACMP shadow register */
- ECAP_WRITE4(sc, ECAP_CAP4, duty);
- /* Restart counter */
- ECAP_WRITE4(sc, ECAP_TSCTR, 0);
+ return (0);
+}
+
+static int
+am335x_ecap_channel_get_flags(device_t dev, u_int channel,
+ uint32_t *flags)
+{
+ struct am335x_ecap_softc *sc;
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+ sc = device_get_softc(dev);
+
+ if (sc->sc_inverted == true)
+ *flags = PWM_POLARITY_INVERTED;
+ else
+ *flags = 0;
+
+ return (0);
+}
+
+static int
+am335x_ecap_channel_enable(device_t dev, u_int channel, bool enable)
+{
+ struct am335x_ecap_softc *sc;
+
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ PWM_LOCK(sc);
+ am335x_ecap_cfg_enable(sc, channel, enable);
PWM_UNLOCK(sc);
return (0);
}
static int
-am335x_ecap_probe(device_t dev)
+am335x_ecap_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
{
+ struct am335x_ecap_softc *sc;
+
+ if (channel >= NUM_CHANNELS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ *enabled = sc->sc_enabled;
+ return (0);
+}
+
+static int
+am335x_ecap_probe(device_t dev)
+{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
@@ -158,6 +271,8 @@
am335x_ecap_attach(device_t dev)
{
struct am335x_ecap_softc *sc;
+ int err;
+ phandle_t node;
sc = device_get_softc(dev);
sc->sc_dev = dev;
@@ -171,10 +286,39 @@
goto fail;
}
- return (0);
+ err = clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clkfck);
+ if (err != 0) {
+ device_printf(dev, "Cant find clock index 0. err: %d\n",
+ err);
+ goto fail;
+ }
+ /* Get the base clock frequency. */
+ err = clk_get_freq(sc->sc_clkfck, &sc->sc_clkfreq);
+ if (err != 0) {
+ device_printf(dev, "Cant get sysclk frequency, err %d\n",
+ err);
+ goto fail;
+ }
+
+ /*
+ * Note that we don't check for failure to attach pwmbus -- even without
+ * it we can still service clients who connect via fdt xref data.
+ */
+ node = ofw_bus_get_node(dev);
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ if ((sc->sc_busdev = device_add_child(dev, "pwmbus", -1)) == NULL &&
+ bootverbose)
+ device_printf(dev, "Cannot add child pwmbus\n");
+
+ bus_generic_probe(dev);
+ return (bus_generic_attach(dev));
fail:
PWM_LOCK_DESTROY(sc);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->sc_mem_rid, sc->sc_mem_res);
return (ENXIO);
}
@@ -182,13 +326,22 @@
am335x_ecap_detach(device_t dev)
{
struct am335x_ecap_softc *sc;
+ int error;
sc = device_get_softc(dev);
+ if ((error = bus_generic_detach(sc->sc_dev)) != 0)
+ return (error);
+
PWM_LOCK(sc);
+
+ if (sc->sc_busdev != NULL)
+ return (error);
+
if (sc->sc_mem_res)
bus_release_resource(dev, SYS_RES_MEMORY,
sc->sc_mem_rid, sc->sc_mem_res);
+
PWM_UNLOCK(sc);
PWM_LOCK_DESTROY(sc);
@@ -196,6 +349,43 @@
return (0);
}
+static phandle_t
+am335x_ecap_get_node(device_t bus, device_t dev)
+{
+ /*
+ * Share our controller node with our pwmbus child; it instantiates
+ * devices by walking the children contained within our node.
+ */
+ return ofw_bus_get_node(bus);
+}
+
+static device_method_t am335x_ecap_methods[] = {
+ DEVMETHOD(device_probe, am335x_ecap_probe),
+ DEVMETHOD(device_attach, am335x_ecap_attach),
+ DEVMETHOD(device_detach, am335x_ecap_detach),
+
+ /* ofw_bus_if */
+ DEVMETHOD(ofw_bus_get_node, am335x_ecap_get_node),
+
+ /* pwm interface */
+ DEVMETHOD(pwmbus_channel_count, am335x_ecap_channel_count),
+ DEVMETHOD(pwmbus_channel_config, am335x_ecap_channel_config),
+ DEVMETHOD(pwmbus_channel_get_config, am335x_ecap_channel_get_config),
+ DEVMETHOD(pwmbus_channel_set_flags, am335x_ecap_channel_set_flags),
+ DEVMETHOD(pwmbus_channel_get_flags, am335x_ecap_channel_get_flags),
+ DEVMETHOD(pwmbus_channel_enable, am335x_ecap_channel_enable),
+ DEVMETHOD(pwmbus_channel_is_enabled, am335x_ecap_channel_is_enabled),
+
+ DEVMETHOD_END
+};
+
+static driver_t am335x_ecap_driver = {
+ "pwm",
+ am335x_ecap_methods,
+ sizeof(struct am335x_ecap_softc),
+};
+
DRIVER_MODULE(am335x_ecap, am335x_pwmss, am335x_ecap_driver, 0, 0);
MODULE_VERSION(am335x_ecap, 1);
MODULE_DEPEND(am335x_ecap, am335x_pwmss, 1, 1, 1);
+MODULE_DEPEND(am335x_ecap, pwmbus, 1, 1, 1);

File Metadata

Mime Type
text/plain
Expires
Tue, Feb 10, 6:27 AM (7 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28616238
Default Alt Text
D43283.diff (8 KB)

Event Timeline