diff --git a/sys/conf/files b/sys/conf/files index c9f7786fdef6..3c5146977747 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2704,6 +2704,7 @@ dev/pwm/pwmc.c optional pwm | pwmc dev/pwm/pwmbus.c optional pwm | pwmbus dev/pwm/pwmbus_if.m optional pwm | pwmbus dev/pwm/ofw_pwm.c optional pwm fdt | pwmbus fdt +dev/pwm/ofw_pwmbus.c optional pwm fdt | pwmbus fdt dev/quicc/quicc_core.c optional quicc dev/ral/rt2560.c optional ral dev/ral/rt2661.c optional ral diff --git a/sys/dev/pwm/ofw_pwmbus.c b/sys/dev/pwm/ofw_pwmbus.c new file mode 100644 index 000000000000..359c4f11272b --- /dev/null +++ b/sys/dev/pwm/ofw_pwmbus.c @@ -0,0 +1,223 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Ian Lepore + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pwmbus_if.h" + +struct ofw_pwmbus_ivars { + struct pwmbus_ivars base; + struct ofw_bus_devinfo devinfo; +}; + +struct ofw_pwmbus_softc { + struct pwmbus_softc base; +}; + +/* + * bus_if methods... + */ + +static device_t +ofw_pwmbus_add_child(device_t dev, u_int order, const char *name, int unit) +{ + device_t child; + struct ofw_pwmbus_ivars *ivars; + + if ((ivars = malloc(sizeof(struct ofw_pwmbus_ivars), M_DEVBUF, + M_NOWAIT | M_ZERO)) == NULL) { + return (NULL); + } + + if ((child = device_add_child_ordered(dev, order, name, unit)) == NULL) { + free(ivars, M_DEVBUF); + return (NULL); + } + + ivars->devinfo.obd_node = -1; + device_set_ivars(child, ivars); + + return (child); +} + +static void +ofw_pwmbus_child_deleted(device_t dev, device_t child) +{ + struct ofw_pwmbus_ivars *ivars; + + ivars = device_get_ivars(child); + if (ivars != NULL) { + ofw_bus_gen_destroy_devinfo(&ivars->devinfo); + free(ivars, M_DEVBUF); + } +} + +static const struct ofw_bus_devinfo * +ofw_pwmbus_get_devinfo(device_t bus, device_t dev) +{ + struct ofw_pwmbus_ivars *ivars; + + ivars = device_get_ivars(dev); + return (&ivars->devinfo); +} + +/* + * device_if methods... + */ + +static int +ofw_pwmbus_probe(device_t dev) +{ + + if (ofw_bus_get_node(dev) == -1) { + return (ENXIO); + } + device_set_desc(dev, "OFW PWM bus"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ofw_pwmbus_attach(device_t dev) +{ + struct ofw_pwmbus_softc *sc; + struct ofw_pwmbus_ivars *ivars; + phandle_t node; + device_t child, parent; + pcell_t chan; + bool any_children; + + sc = device_get_softc(dev); + sc->base.dev = dev; + parent = device_get_parent(dev); + + if (PWMBUS_CHANNEL_COUNT(parent, &sc->base.nchannels) != 0 || + sc->base.nchannels == 0) { + device_printf(dev, "No channels on parent %s\n", + device_get_nameunit(parent)); + return (ENXIO); + } + + /* + * Attach the children found in the fdt node of the hardware controller. + * Hardware controllers must implement the ofw_bus_get_node method so + * that our call to ofw_bus_get_node() gets back the controller's node. + */ + any_children = false; + node = ofw_bus_get_node(dev); + for (node = OF_child(node); node != 0; node = OF_peer(node)) { + /* + * The child has to have a reg property; its value is the + * channel number so range-check it. + */ + if (OF_getencprop(node, "reg", &chan, sizeof(chan)) == -1) + continue; + if (chan >= sc->base.nchannels) + continue; + + if ((child = ofw_pwmbus_add_child(dev, 0, NULL, -1)) == NULL) + continue; + + ivars = device_get_ivars(child); + ivars->base.pi_channel = chan; + + /* Set up the standard ofw devinfo. */ + if (ofw_bus_gen_setup_devinfo(&ivars->devinfo, node) != 0) { + device_delete_child(dev, child); + continue; + } + any_children = true; + } + + /* + * If we didn't find any children in the fdt data, add a pwmc(4) child + * for each channel, like the base pwmbus does. The idea is that if + * there is any fdt data, then we do exactly what it says and nothing + * more, otherwise we just provide generic userland access to all the + * pwm channels that exist like the base pwmbus's attach code does. + */ + if (!any_children) { + for (chan = 0; chan < sc->base.nchannels; ++chan) { + child = ofw_pwmbus_add_child(dev, 0, "pwmc", -1); + if (child == NULL) { + device_printf(dev, "failed to add pwmc child " + " device for channel %u\n", chan); + continue; + } + ivars = device_get_ivars(child); + ivars->base.pi_channel = chan; + } + } + bus_enumerate_hinted_children(dev); + bus_generic_probe(dev); + + return (bus_generic_attach(dev)); +} + +static device_method_t ofw_pwmbus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_pwmbus_probe), + DEVMETHOD(device_attach, ofw_pwmbus_attach), + + /* Bus interface */ + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), + DEVMETHOD(bus_add_child, ofw_pwmbus_add_child), + DEVMETHOD(bus_child_deleted, ofw_pwmbus_child_deleted), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, ofw_pwmbus_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +devclass_t ofw_pwmbus_devclass; + +DEFINE_CLASS_1(pwmbus, ofw_pwmbus_driver, ofw_pwmbus_methods, + sizeof(struct pwmbus_softc), pwmbus_driver); +EARLY_DRIVER_MODULE(ofw_pwmbus, pwm, ofw_pwmbus_driver, ofw_pwmbus_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ofw_pwmbus, 1); +MODULE_DEPEND(ofw_pwmbus, pwmbus, 1, 1, 1); diff --git a/sys/dev/pwm/pwmbus.c b/sys/dev/pwm/pwmbus.c index 2046baade085..e5ecfee08bbd 100644 --- a/sys/dev/pwm/pwmbus.c +++ b/sys/dev/pwm/pwmbus.c @@ -44,11 +44,6 @@ __FBSDID("$FreeBSD$"); #include "pwmbus_if.h" -struct pwmbus_softc { - device_t dev; - u_int nchannels; -}; - /* * bus_if methods... */ diff --git a/sys/dev/pwm/pwmbus.h b/sys/dev/pwm/pwmbus.h index ce37ad1ff966..7ace2ef41b1b 100644 --- a/sys/dev/pwm/pwmbus.h +++ b/sys/dev/pwm/pwmbus.h @@ -30,6 +30,11 @@ #ifndef _PWMBUS_H_ #define _PWMBUS_H_ +struct pwmbus_softc { + device_t dev; + u_int nchannels; +}; + struct pwmbus_ivars { u_int pi_channel; }; @@ -54,6 +59,12 @@ pwmbus_set_ ## A(device_t dev, T t) \ PWMBUS_ACCESSOR(channel, CHANNEL, u_int) +#ifdef FDT +#define PWMBUS_FDT_PNP_INFO(t) FDTCOMPAT_PNP_INFO(t, pwmbus) +#else +#define PWMBUS_FDT_PNP_INFO(t) +#endif + extern driver_t pwmbus_driver; extern devclass_t pwmbus_devclass; extern driver_t ofw_pwmbus_driver; diff --git a/sys/dev/pwm/pwmc.c b/sys/dev/pwm/pwmc.c index 6b41a51997e4..0d9786cd701d 100644 --- a/sys/dev/pwm/pwmc.c +++ b/sys/dev/pwm/pwmc.c @@ -29,6 +29,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include @@ -42,6 +44,20 @@ __FBSDID("$FreeBSD$"); #include "pwmbus_if.h" +#ifdef FDT +#include +#include +#include + +static struct ofw_compat_data compat_data[] = { + {"freebsd,pwmc", true}, + {NULL, false}, +}; + +PWMBUS_FDT_PNP_INFO(compat_data); + +#endif + struct pwmc_softc { device_t dev; struct cdev *cdev; @@ -98,12 +114,52 @@ static struct cdevsw pwm_cdevsw = { .d_ioctl = pwm_ioctl }; +#ifdef FDT + +static void +pwmc_setup_label(struct pwmc_softc *sc) +{ + void *label; + + if (OF_getprop_alloc(ofw_bus_get_node(sc->dev), "label", &label) > 0) { + make_dev_alias(sc->cdev, "pwm/%s", (char *)label); + OF_prop_free(label); + } +} + +#else /* FDT */ + +static void +pwmc_setup_label(struct pwmc_softc *sc) +{ + char *label; + + if (resource_string_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "label", &label) == 0) { + make_dev_alias(sc->cdev, "pwm/%s", label); + } +} + +#endif /* FDT */ + static int pwmc_probe(device_t dev) { + int rv; + + rv = BUS_PROBE_NOWILDCARD; + +#ifdef FDT + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + rv = BUS_PROBE_DEFAULT; + } +#endif device_set_desc(dev, "PWM Control"); - return (BUS_PROBE_NOWILDCARD); + return (rv); } static int @@ -111,7 +167,6 @@ pwmc_attach(device_t dev) { struct pwmc_softc *sc; struct make_dev_args args; - const char *label; int error; sc = device_get_softc(dev); @@ -134,11 +189,7 @@ pwmc_attach(device_t dev) 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); - } + pwmc_setup_label(sc); return (0); } diff --git a/sys/modules/pwm/pwmbus/Makefile b/sys/modules/pwm/pwmbus/Makefile index db6a7d34794e..3cf56697f200 100644 --- a/sys/modules/pwm/pwmbus/Makefile +++ b/sys/modules/pwm/pwmbus/Makefile @@ -6,7 +6,7 @@ KMOD= pwmbus SRCS= pwmbus.c .if !empty(OPT_FDT) -SRCS+= ofw_pwm.c ofw_bus_if.h +SRCS+= ofw_pwm.c ofw_pwmbus.c ofw_bus_if.h .endif # Generated files...