aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/pwm
diff options
context:
space:
mode:
authorIan Lepore <ian@FreeBSD.org>2019-06-16 19:44:42 +0000
committerIan Lepore <ian@FreeBSD.org>2019-06-16 19:44:42 +0000
commit0af7a9a45179bc98c1f62c21c5272d7cae74e9ed (patch)
treec86430705e016e0bf53f71e92d3a588d9c4abea3 /sys/dev/pwm
parent83743daeadac3e81cc8f8e7665f7543a62883b51 (diff)
Notes
Diffstat (limited to 'sys/dev/pwm')
-rw-r--r--sys/dev/pwm/pwmbus.c161
-rw-r--r--sys/dev/pwm/pwmbus.h62
-rw-r--r--sys/dev/pwm/pwmc.c40
3 files changed, 234 insertions, 29 deletions
diff --git a/sys/dev/pwm/pwmbus.c b/sys/dev/pwm/pwmbus.c
index 7039fde4b07d..2046baade085 100644
--- a/sys/dev/pwm/pwmbus.c
+++ b/sys/dev/pwm/pwmbus.c
@@ -37,28 +37,131 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/endian.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/module.h>
-#include <machine/bus.h>
+#include <dev/pwm/pwmbus.h>
#include "pwmbus_if.h"
-struct pwmbus_channel_data {
- int reserved;
- char *name;
-};
-
struct pwmbus_softc {
device_t dev;
- device_t parent;
-
u_int nchannels;
};
+/*
+ * bus_if methods...
+ */
+
+static device_t
+pwmbus_add_child(device_t dev, u_int order, const char *name, int unit)
+{
+ device_t child;
+ struct pwmbus_ivars *ivars;
+
+ child = device_add_child_ordered(dev, order, name, unit);
+ if (child == NULL)
+ return (child);
+
+ ivars = malloc(sizeof(struct pwmbus_ivars), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ivars == NULL) {
+ device_delete_child(dev, child);
+ return (NULL);
+ }
+ device_set_ivars(child, ivars);
+
+ return (child);
+}
+
static int
-pwmbus_probe(device_t dev)
+pwmbus_child_location_str(device_t dev, device_t child, char *buf, size_t blen)
+{
+ struct pwmbus_ivars *ivars;
+
+ ivars = device_get_ivars(child);
+ snprintf(buf, blen, "hwdev=%s channel=%u",
+ device_get_nameunit(device_get_parent(dev)), ivars->pi_channel);
+
+ return (0);
+}
+
+static int
+pwmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf,
+ size_t buflen)
+{
+ *buf = '\0';
+ return (0);
+}
+
+static void
+pwmbus_hinted_child(device_t dev, const char *dname, int dunit)
+{
+ struct pwmbus_ivars *ivars;
+ device_t child;
+
+ child = pwmbus_add_child(dev, 0, dname, dunit);
+
+ /*
+ * If there is a channel hint, use it. Otherwise pi_channel was
+ * initialized to zero, so that's the channel we'll use.
+ */
+ ivars = device_get_ivars(child);
+ resource_int_value(dname, dunit, "channel", &ivars->pi_channel);
+}
+
+static int
+pwmbus_print_child(device_t dev, device_t child)
{
+ struct pwmbus_ivars *ivars;
+ int rv;
+
+ ivars = device_get_ivars(child);
+
+ rv = bus_print_child_header(dev, child);
+ rv += printf(" channel %u", ivars->pi_channel);
+ rv += bus_print_child_footer(dev, child);
+
+ return (rv);
+}
+
+static void
+pwmbus_probe_nomatch(device_t dev, device_t child)
+{
+ struct pwmbus_ivars *ivars;
+
+ ivars = device_get_ivars(child);
+ if (ivars != NULL)
+ device_printf(dev, "<unknown> on channel %u\n",
+ ivars->pi_channel);
+
+ return;
+}
+
+static int
+pwmbus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct pwmbus_ivars *ivars;
+ ivars = device_get_ivars(child);
+
+ switch (which) {
+ case PWMBUS_IVAR_CHANNEL:
+ *(u_int *)result = ivars->pi_channel;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+/*
+ * device_if methods...
+ */
+
+static int
+pwmbus_probe(device_t dev)
+{
device_set_desc(dev, "PWM bus");
return (BUS_PROBE_GENERIC);
}
@@ -67,20 +170,33 @@ static int
pwmbus_attach(device_t dev)
{
struct pwmbus_softc *sc;
+ struct pwmbus_ivars *ivars;
+ device_t child, parent;
+ u_int chan;
sc = device_get_softc(dev);
sc->dev = dev;
- sc->parent = device_get_parent(dev);
+ parent = device_get_parent(dev);
- if (PWMBUS_CHANNEL_COUNT(sc->parent, &sc->nchannels) != 0 ||
+ if (PWMBUS_CHANNEL_COUNT(parent, &sc->nchannels) != 0 ||
sc->nchannels == 0) {
device_printf(sc->dev, "No channels on parent %s\n",
- device_get_nameunit(sc->parent));
+ device_get_nameunit(parent));
return (ENXIO);
}
- device_add_child(sc->dev, "pwmc", -1);
+ /* Add a pwmc(4) child for each channel. */
+ for (chan = 0; chan < sc->nchannels; ++chan) {
+ if ((child = pwmbus_add_child(sc->dev, 0, "pwmc", -1)) == NULL) {
+ device_printf(dev, "failed to add pwmc child device "
+ "for channel %u\n", chan);
+ continue;
+ }
+ ivars = device_get_ivars(child);
+ ivars->pi_channel = chan;
+ }
+ bus_enumerate_hinted_children(dev);
bus_generic_probe(dev);
return (bus_generic_attach(dev));
@@ -97,6 +213,10 @@ pwmbus_detach(device_t dev)
return (rv);
}
+/*
+ * pwmbus_if methods...
+ */
+
static int
pwmbus_channel_config(device_t dev, u_int chan, u_int period, u_int duty)
{
@@ -145,6 +265,15 @@ static device_method_t pwmbus_methods[] = {
DEVMETHOD(device_attach, pwmbus_attach),
DEVMETHOD(device_detach, pwmbus_detach),
+ /* bus_if */
+ DEVMETHOD(bus_add_child, pwmbus_add_child),
+ DEVMETHOD(bus_child_location_str, pwmbus_child_location_str),
+ DEVMETHOD(bus_child_pnpinfo_str, pwmbus_child_pnpinfo_str),
+ DEVMETHOD(bus_hinted_child, pwmbus_hinted_child),
+ DEVMETHOD(bus_print_child, pwmbus_print_child),
+ DEVMETHOD(bus_probe_nomatch, pwmbus_probe_nomatch),
+ DEVMETHOD(bus_read_ivar, pwmbus_read_ivar),
+
/* pwmbus_if */
DEVMETHOD(pwmbus_channel_count, pwmbus_channel_count),
DEVMETHOD(pwmbus_channel_config, pwmbus_channel_config),
@@ -157,13 +286,13 @@ static device_method_t pwmbus_methods[] = {
DEVMETHOD_END
};
-static driver_t pwmbus_driver = {
+driver_t pwmbus_driver = {
"pwmbus",
pwmbus_methods,
sizeof(struct pwmbus_softc),
};
-static devclass_t pwmbus_devclass;
+devclass_t pwmbus_devclass;
EARLY_DRIVER_MODULE(pwmbus, pwm, pwmbus_driver, pwmbus_devclass, 0, 0,
- BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(pwmbus, 1);
diff --git a/sys/dev/pwm/pwmbus.h b/sys/dev/pwm/pwmbus.h
new file mode 100644
index 000000000000..ce37ad1ff966
--- /dev/null
+++ b/sys/dev/pwm/pwmbus.h
@@ -0,0 +1,62 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Ian Lepore <ian@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PWMBUS_H_
+#define _PWMBUS_H_
+
+struct pwmbus_ivars {
+ u_int pi_channel;
+};
+
+enum {
+ PWMBUS_IVAR_CHANNEL, /* Channel used by child dev */
+};
+
+#define PWMBUS_ACCESSOR(A, B, T) \
+static inline int \
+pwmbus_get_ ## A(device_t dev, T *t) \
+{ \
+ return BUS_READ_IVAR(device_get_parent(dev), dev, \
+ PWMBUS_IVAR_ ## B, (uintptr_t *) t); \
+} \
+static inline int \
+pwmbus_set_ ## A(device_t dev, T t) \
+{ \
+ return BUS_WRITE_IVAR(device_get_parent(dev), dev, \
+ PWMBUS_IVAR_ ## B, (uintptr_t) t); \
+}
+
+PWMBUS_ACCESSOR(channel, CHANNEL, u_int)
+
+extern driver_t pwmbus_driver;
+extern devclass_t pwmbus_devclass;
+extern driver_t ofw_pwmbus_driver;
+extern devclass_t ofw_pwmbus_devclass;
+
+#endif /* _PWMBUS_H_ */
diff --git a/sys/dev/pwm/pwmc.c b/sys/dev/pwm/pwmc.c
index 18b60388a7bc..6b41a51997e4 100644
--- a/sys/dev/pwm/pwmc.c
+++ b/sys/dev/pwm/pwmc.c
@@ -37,14 +37,15 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/time.h>
+#include <dev/pwm/pwmbus.h>
#include <dev/pwm/pwmc.h>
#include "pwmbus_if.h"
struct pwmc_softc {
device_t dev;
- struct cdev *pwm_dev;
- char name[32];
+ struct cdev *cdev;
+ u_int chan;
};
static int
@@ -68,19 +69,19 @@ pwm_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
break;
case PWMSETSTATE:
bcopy(data, &state, sizeof(state));
- rv = PWMBUS_CHANNEL_CONFIG(bus, state.channel,
+ rv = PWMBUS_CHANNEL_CONFIG(bus, sc->chan,
state.period, state.duty);
if (rv == 0)
- rv = PWMBUS_CHANNEL_ENABLE(bus, state.channel,
+ rv = PWMBUS_CHANNEL_ENABLE(bus, sc->chan,
state.enable);
break;
case PWMGETSTATE:
bcopy(data, &state, sizeof(state));
- rv = PWMBUS_CHANNEL_GET_CONFIG(bus, state.channel,
+ rv = PWMBUS_CHANNEL_GET_CONFIG(bus, sc->chan,
&state.period, &state.duty);
if (rv != 0)
return (rv);
- rv = PWMBUS_CHANNEL_IS_ENABLED(bus, state.channel,
+ rv = PWMBUS_CHANNEL_IS_ENABLED(bus, sc->chan,
&state.enable);
if (rv != 0)
return (rv);
@@ -93,7 +94,7 @@ pwm_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
static struct cdevsw pwm_cdevsw = {
.d_version = D_VERSION,
- .d_name = "pwm",
+ .d_name = "pwmc",
.d_ioctl = pwm_ioctl
};
@@ -101,7 +102,7 @@ static int
pwmc_probe(device_t dev)
{
- device_set_desc(dev, "PWM Controller");
+ device_set_desc(dev, "PWM Control");
return (BUS_PROBE_NOWILDCARD);
}
@@ -110,22 +111,35 @@ pwmc_attach(device_t dev)
{
struct pwmc_softc *sc;
struct make_dev_args args;
+ const char *label;
+ int error;
sc = device_get_softc(dev);
sc->dev = dev;
- snprintf(sc->name, sizeof(sc->name), "pwmc%d", device_get_unit(dev));
+ if ((error = pwmbus_get_channel(dev, &sc->chan)) != 0)
+ return (error);
+
make_dev_args_init(&args);
args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
args.mda_devsw = &pwm_cdevsw;
args.mda_uid = UID_ROOT;
args.mda_gid = GID_OPERATOR;
- args.mda_mode = 0600;
+ args.mda_mode = 0660;
args.mda_si_drv1 = sc;
- if (make_dev_s(&args, &sc->pwm_dev, "%s", sc->name) != 0) {
+ error = make_dev_s(&args, &sc->cdev, "pwmc%d.%d",
+ device_get_unit(device_get_parent(dev)), sc->chan);
+ if (error != 0) {
device_printf(dev, "Failed to make PWM device\n");
- return (ENXIO);
+ return (error);
+ }
+
+ /* If there is a label hint, create an alias with that name. */
+ if (resource_string_value(device_get_name(dev), device_get_unit(dev),
+ "label", &label) == 0) {
+ make_dev_alias(sc->cdev, "pwm/%s", label);
}
+
return (0);
}
@@ -135,7 +149,7 @@ pwmc_detach(device_t dev)
struct pwmc_softc *sc;
sc = device_get_softc(dev);
- destroy_dev(sc->pwm_dev);
+ destroy_dev(sc->cdev);
return (0);
}