Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144531795
D43283.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D43283.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D43283: AM335x: Add PWM(9) api to am335x_ecap driver
Attached
Detach File
Event Timeline
Log In to Comment