Index: head/sys/dev/pwm/pwmbus.c =================================================================== --- head/sys/dev/pwm/pwmbus.c (revision 349118) +++ head/sys/dev/pwm/pwmbus.c (revision 349119) @@ -1,169 +1,298 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * 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 "opt_platform.h" #include #include #include #include #include #include +#include #include -#include +#include #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, " 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); } 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)); } static int pwmbus_detach(device_t dev) { int rv; if ((rv = bus_generic_detach(dev)) == 0) rv = device_delete_children(dev); return (rv); } +/* + * pwmbus_if methods... + */ + static int pwmbus_channel_config(device_t dev, u_int chan, u_int period, u_int duty) { return (PWMBUS_CHANNEL_CONFIG(device_get_parent(dev), chan, period, duty)); } static int pwmbus_channel_get_config(device_t dev, u_int chan, u_int *period, u_int *duty) { return (PWMBUS_CHANNEL_GET_CONFIG(device_get_parent(dev), chan, period, duty)); } static int pwmbus_channel_get_flags(device_t dev, u_int chan, uint32_t *flags) { return (PWMBUS_CHANNEL_GET_FLAGS(device_get_parent(dev), chan, flags)); } static int pwmbus_channel_enable(device_t dev, u_int chan, bool enable) { return (PWMBUS_CHANNEL_ENABLE(device_get_parent(dev), chan, enable)); } static int pwmbus_channel_set_flags(device_t dev, u_int chan, uint32_t flags) { return (PWMBUS_CHANNEL_SET_FLAGS(device_get_parent(dev), chan, flags)); } static int pwmbus_channel_is_enabled(device_t dev, u_int chan, bool *enable) { return (PWMBUS_CHANNEL_IS_ENABLED(device_get_parent(dev), chan, enable)); } static int pwmbus_channel_count(device_t dev, u_int *nchannel) { return (PWMBUS_CHANNEL_COUNT(device_get_parent(dev), nchannel)); } static device_method_t pwmbus_methods[] = { /* device_if */ DEVMETHOD(device_probe, pwmbus_probe), 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), DEVMETHOD(pwmbus_channel_get_config, pwmbus_channel_get_config), DEVMETHOD(pwmbus_channel_set_flags, pwmbus_channel_set_flags), DEVMETHOD(pwmbus_channel_get_flags, pwmbus_channel_get_flags), DEVMETHOD(pwmbus_channel_enable, pwmbus_channel_enable), DEVMETHOD(pwmbus_channel_is_enabled, pwmbus_channel_is_enabled), 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); Index: head/sys/dev/pwm/pwmbus.h =================================================================== --- head/sys/dev/pwm/pwmbus.h (nonexistent) +++ head/sys/dev/pwm/pwmbus.h (revision 349119) @@ -0,0 +1,62 @@ +/*- + * 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. + * + * $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_ */ Property changes on: head/sys/dev/pwm/pwmbus.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/pwm/pwmc.c =================================================================== --- head/sys/dev/pwm/pwmc.c (revision 349118) +++ head/sys/dev/pwm/pwmc.c (revision 349119) @@ -1,161 +1,175 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * 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 "pwmbus_if.h" struct pwmc_softc { device_t dev; - struct cdev *pwm_dev; - char name[32]; + struct cdev *cdev; + u_int chan; }; static int pwm_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct pwmc_softc *sc; struct pwm_state state; device_t bus; u_int nchannel; int rv = 0; sc = dev->si_drv1; bus = device_get_parent(sc->dev); switch (cmd) { case PWMMAXCHANNEL: nchannel = 0; rv = PWMBUS_CHANNEL_COUNT(bus, &nchannel); bcopy(&nchannel, data, sizeof(nchannel)); 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); bcopy(&state, data, sizeof(state)); break; } return (rv); } static struct cdevsw pwm_cdevsw = { .d_version = D_VERSION, - .d_name = "pwm", + .d_name = "pwmc", .d_ioctl = pwm_ioctl }; static int pwmc_probe(device_t dev) { - device_set_desc(dev, "PWM Controller"); + device_set_desc(dev, "PWM Control"); return (BUS_PROBE_NOWILDCARD); } static int 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); } static int 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); } static device_method_t pwmc_methods[] = { /* device_if */ DEVMETHOD(device_probe, pwmc_probe), DEVMETHOD(device_attach, pwmc_attach), DEVMETHOD(device_detach, pwmc_detach), DEVMETHOD_END }; static driver_t pwmc_driver = { "pwmc", pwmc_methods, sizeof(struct pwmc_softc), }; static devclass_t pwmc_devclass; DRIVER_MODULE(pwmc, pwmbus, pwmc_driver, pwmc_devclass, 0, 0); MODULE_DEPEND(pwmc, pwmbus, 1, 1, 1); MODULE_VERSION(pwmc, 1);