1b43e2c8bSIan Lepore /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3b43e2c8bSIan Lepore * 4b43e2c8bSIan Lepore * Copyright (c) 2019 Ian Lepore <ian@FreeBSD.org> 5b43e2c8bSIan Lepore * 6b43e2c8bSIan Lepore * Redistribution and use in source and binary forms, with or without 7b43e2c8bSIan Lepore * modification, are permitted provided that the following conditions 8b43e2c8bSIan Lepore * are met: 9b43e2c8bSIan Lepore * 1. Redistributions of source code must retain the above copyright 10b43e2c8bSIan Lepore * notice, this list of conditions and the following disclaimer. 11b43e2c8bSIan Lepore * 2. Redistributions in binary form must reproduce the above copyright 12b43e2c8bSIan Lepore * notice, this list of conditions and the following disclaimer in the 13b43e2c8bSIan Lepore * documentation and/or other materials provided with the distribution. 14b43e2c8bSIan Lepore * 15b43e2c8bSIan Lepore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16b43e2c8bSIan Lepore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17b43e2c8bSIan Lepore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18b43e2c8bSIan Lepore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19b43e2c8bSIan Lepore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20b43e2c8bSIan Lepore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21b43e2c8bSIan Lepore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22b43e2c8bSIan Lepore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23b43e2c8bSIan Lepore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24b43e2c8bSIan Lepore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25b43e2c8bSIan Lepore * SUCH DAMAGE. 26b43e2c8bSIan Lepore */ 27b43e2c8bSIan Lepore 28b43e2c8bSIan Lepore #include <sys/param.h> 29b43e2c8bSIan Lepore #include <sys/bus.h> 30b43e2c8bSIan Lepore #include <sys/kernel.h> 31b43e2c8bSIan Lepore #include <sys/libkern.h> 32b43e2c8bSIan Lepore #include <sys/lock.h> 33b43e2c8bSIan Lepore #include <sys/module.h> 34b43e2c8bSIan Lepore 35b43e2c8bSIan Lepore #include <dev/fdt/fdt_common.h> 36b43e2c8bSIan Lepore #include <dev/ofw/ofw_bus.h> 37b43e2c8bSIan Lepore #include <dev/ofw/ofw_bus_subr.h> 38b43e2c8bSIan Lepore #include <dev/ofw/openfirm.h> 39b43e2c8bSIan Lepore #include <dev/pwm/pwmbus.h> 40b43e2c8bSIan Lepore 41b43e2c8bSIan Lepore #include "pwmbus_if.h" 42b43e2c8bSIan Lepore 43b43e2c8bSIan Lepore struct ofw_pwmbus_ivars { 44b43e2c8bSIan Lepore struct pwmbus_ivars base; 45b43e2c8bSIan Lepore struct ofw_bus_devinfo devinfo; 46b43e2c8bSIan Lepore }; 47b43e2c8bSIan Lepore 48b43e2c8bSIan Lepore struct ofw_pwmbus_softc { 49b43e2c8bSIan Lepore struct pwmbus_softc base; 50b43e2c8bSIan Lepore }; 51b43e2c8bSIan Lepore 52b43e2c8bSIan Lepore /* 53b43e2c8bSIan Lepore * bus_if methods... 54b43e2c8bSIan Lepore */ 55b43e2c8bSIan Lepore 56b43e2c8bSIan Lepore static device_t 57b43e2c8bSIan Lepore ofw_pwmbus_add_child(device_t dev, u_int order, const char *name, int unit) 58b43e2c8bSIan Lepore { 59b43e2c8bSIan Lepore device_t child; 60b43e2c8bSIan Lepore struct ofw_pwmbus_ivars *ivars; 61b43e2c8bSIan Lepore 62b43e2c8bSIan Lepore if ((ivars = malloc(sizeof(struct ofw_pwmbus_ivars), M_DEVBUF, 63b43e2c8bSIan Lepore M_NOWAIT | M_ZERO)) == NULL) { 64b43e2c8bSIan Lepore return (NULL); 65b43e2c8bSIan Lepore } 66b43e2c8bSIan Lepore 67b43e2c8bSIan Lepore if ((child = device_add_child_ordered(dev, order, name, unit)) == NULL) { 68b43e2c8bSIan Lepore free(ivars, M_DEVBUF); 69b43e2c8bSIan Lepore return (NULL); 70b43e2c8bSIan Lepore } 71b43e2c8bSIan Lepore 72b43e2c8bSIan Lepore ivars->devinfo.obd_node = -1; 73b43e2c8bSIan Lepore device_set_ivars(child, ivars); 74b43e2c8bSIan Lepore 75b43e2c8bSIan Lepore return (child); 76b43e2c8bSIan Lepore } 77b43e2c8bSIan Lepore 78b43e2c8bSIan Lepore static void 79b43e2c8bSIan Lepore ofw_pwmbus_child_deleted(device_t dev, device_t child) 80b43e2c8bSIan Lepore { 81b43e2c8bSIan Lepore struct ofw_pwmbus_ivars *ivars; 82b43e2c8bSIan Lepore 83b43e2c8bSIan Lepore ivars = device_get_ivars(child); 84b43e2c8bSIan Lepore if (ivars != NULL) { 85b43e2c8bSIan Lepore ofw_bus_gen_destroy_devinfo(&ivars->devinfo); 86b43e2c8bSIan Lepore free(ivars, M_DEVBUF); 87b43e2c8bSIan Lepore } 88b43e2c8bSIan Lepore } 89b43e2c8bSIan Lepore 90b43e2c8bSIan Lepore static const struct ofw_bus_devinfo * 91b43e2c8bSIan Lepore ofw_pwmbus_get_devinfo(device_t bus, device_t dev) 92b43e2c8bSIan Lepore { 93b43e2c8bSIan Lepore struct ofw_pwmbus_ivars *ivars; 94b43e2c8bSIan Lepore 95b43e2c8bSIan Lepore ivars = device_get_ivars(dev); 96b43e2c8bSIan Lepore return (&ivars->devinfo); 97b43e2c8bSIan Lepore } 98b43e2c8bSIan Lepore 99b43e2c8bSIan Lepore /* 100b43e2c8bSIan Lepore * device_if methods... 101b43e2c8bSIan Lepore */ 102b43e2c8bSIan Lepore 103b43e2c8bSIan Lepore static int 104b43e2c8bSIan Lepore ofw_pwmbus_probe(device_t dev) 105b43e2c8bSIan Lepore { 106b43e2c8bSIan Lepore 107b43e2c8bSIan Lepore if (ofw_bus_get_node(dev) == -1) { 108b43e2c8bSIan Lepore return (ENXIO); 109b43e2c8bSIan Lepore } 110b43e2c8bSIan Lepore device_set_desc(dev, "OFW PWM bus"); 111b43e2c8bSIan Lepore 112b43e2c8bSIan Lepore return (BUS_PROBE_DEFAULT); 113b43e2c8bSIan Lepore } 114b43e2c8bSIan Lepore 115b43e2c8bSIan Lepore static int 116b43e2c8bSIan Lepore ofw_pwmbus_attach(device_t dev) 117b43e2c8bSIan Lepore { 118b43e2c8bSIan Lepore struct ofw_pwmbus_softc *sc; 119b43e2c8bSIan Lepore struct ofw_pwmbus_ivars *ivars; 120b43e2c8bSIan Lepore phandle_t node; 121b43e2c8bSIan Lepore device_t child, parent; 122b43e2c8bSIan Lepore pcell_t chan; 123b43e2c8bSIan Lepore bool any_children; 124b43e2c8bSIan Lepore 125b43e2c8bSIan Lepore sc = device_get_softc(dev); 126b43e2c8bSIan Lepore sc->base.dev = dev; 127b43e2c8bSIan Lepore parent = device_get_parent(dev); 128b43e2c8bSIan Lepore 129b43e2c8bSIan Lepore if (PWMBUS_CHANNEL_COUNT(parent, &sc->base.nchannels) != 0 || 130b43e2c8bSIan Lepore sc->base.nchannels == 0) { 131b43e2c8bSIan Lepore device_printf(dev, "No channels on parent %s\n", 132b43e2c8bSIan Lepore device_get_nameunit(parent)); 133b43e2c8bSIan Lepore return (ENXIO); 134b43e2c8bSIan Lepore } 135b43e2c8bSIan Lepore 136b43e2c8bSIan Lepore /* 137b43e2c8bSIan Lepore * Attach the children found in the fdt node of the hardware controller. 138b43e2c8bSIan Lepore * Hardware controllers must implement the ofw_bus_get_node method so 139b43e2c8bSIan Lepore * that our call to ofw_bus_get_node() gets back the controller's node. 140b43e2c8bSIan Lepore */ 141b43e2c8bSIan Lepore any_children = false; 142b43e2c8bSIan Lepore node = ofw_bus_get_node(dev); 143b43e2c8bSIan Lepore for (node = OF_child(node); node != 0; node = OF_peer(node)) { 144b43e2c8bSIan Lepore /* 145b43e2c8bSIan Lepore * The child has to have a reg property; its value is the 146b43e2c8bSIan Lepore * channel number so range-check it. 147b43e2c8bSIan Lepore */ 148b43e2c8bSIan Lepore if (OF_getencprop(node, "reg", &chan, sizeof(chan)) == -1) 149b43e2c8bSIan Lepore continue; 150b43e2c8bSIan Lepore if (chan >= sc->base.nchannels) 151b43e2c8bSIan Lepore continue; 152b43e2c8bSIan Lepore 153b43e2c8bSIan Lepore if ((child = ofw_pwmbus_add_child(dev, 0, NULL, -1)) == NULL) 154b43e2c8bSIan Lepore continue; 155b43e2c8bSIan Lepore 156b43e2c8bSIan Lepore ivars = device_get_ivars(child); 157b43e2c8bSIan Lepore ivars->base.pi_channel = chan; 158b43e2c8bSIan Lepore 159b43e2c8bSIan Lepore /* Set up the standard ofw devinfo. */ 160b43e2c8bSIan Lepore if (ofw_bus_gen_setup_devinfo(&ivars->devinfo, node) != 0) { 161b43e2c8bSIan Lepore device_delete_child(dev, child); 162b43e2c8bSIan Lepore continue; 163b43e2c8bSIan Lepore } 164b43e2c8bSIan Lepore any_children = true; 165b43e2c8bSIan Lepore } 166b43e2c8bSIan Lepore 167b43e2c8bSIan Lepore /* 168b43e2c8bSIan Lepore * If we didn't find any children in the fdt data, add a pwmc(4) child 169b43e2c8bSIan Lepore * for each channel, like the base pwmbus does. The idea is that if 170b43e2c8bSIan Lepore * there is any fdt data, then we do exactly what it says and nothing 171b43e2c8bSIan Lepore * more, otherwise we just provide generic userland access to all the 172b43e2c8bSIan Lepore * pwm channels that exist like the base pwmbus's attach code does. 173b43e2c8bSIan Lepore */ 174b43e2c8bSIan Lepore if (!any_children) { 175b43e2c8bSIan Lepore for (chan = 0; chan < sc->base.nchannels; ++chan) { 176b43e2c8bSIan Lepore child = ofw_pwmbus_add_child(dev, 0, "pwmc", -1); 177b43e2c8bSIan Lepore if (child == NULL) { 178b43e2c8bSIan Lepore device_printf(dev, "failed to add pwmc child " 179b43e2c8bSIan Lepore " device for channel %u\n", chan); 180b43e2c8bSIan Lepore continue; 181b43e2c8bSIan Lepore } 182b43e2c8bSIan Lepore ivars = device_get_ivars(child); 183b43e2c8bSIan Lepore ivars->base.pi_channel = chan; 184b43e2c8bSIan Lepore } 185b43e2c8bSIan Lepore } 186b43e2c8bSIan Lepore bus_enumerate_hinted_children(dev); 187723da5d9SJohn Baldwin bus_identify_children(dev); 188*18250ec6SJohn Baldwin bus_attach_children(dev); 189b43e2c8bSIan Lepore 190*18250ec6SJohn Baldwin return (0); 191b43e2c8bSIan Lepore } 192b43e2c8bSIan Lepore 193b43e2c8bSIan Lepore static device_method_t ofw_pwmbus_methods[] = { 194b43e2c8bSIan Lepore /* Device interface */ 195b43e2c8bSIan Lepore DEVMETHOD(device_probe, ofw_pwmbus_probe), 196b43e2c8bSIan Lepore DEVMETHOD(device_attach, ofw_pwmbus_attach), 197b43e2c8bSIan Lepore 198b43e2c8bSIan Lepore /* Bus interface */ 199ddfc9c4cSWarner Losh DEVMETHOD(bus_child_pnpinfo, ofw_bus_gen_child_pnpinfo), 200b43e2c8bSIan Lepore DEVMETHOD(bus_add_child, ofw_pwmbus_add_child), 201b43e2c8bSIan Lepore DEVMETHOD(bus_child_deleted, ofw_pwmbus_child_deleted), 202b43e2c8bSIan Lepore 203b43e2c8bSIan Lepore /* ofw_bus interface */ 204b43e2c8bSIan Lepore DEVMETHOD(ofw_bus_get_devinfo, ofw_pwmbus_get_devinfo), 205b43e2c8bSIan Lepore DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 206b43e2c8bSIan Lepore DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 207b43e2c8bSIan Lepore DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 208b43e2c8bSIan Lepore DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 209b43e2c8bSIan Lepore DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 210b43e2c8bSIan Lepore 211b43e2c8bSIan Lepore DEVMETHOD_END 212b43e2c8bSIan Lepore }; 213b43e2c8bSIan Lepore 214b43e2c8bSIan Lepore DEFINE_CLASS_1(pwmbus, ofw_pwmbus_driver, ofw_pwmbus_methods, 215b43e2c8bSIan Lepore sizeof(struct pwmbus_softc), pwmbus_driver); 216024d9473SJohn Baldwin EARLY_DRIVER_MODULE(ofw_pwmbus, pwm, ofw_pwmbus_driver, 0, 0, 217024d9473SJohn Baldwin BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 218b43e2c8bSIan Lepore MODULE_VERSION(ofw_pwmbus, 1); 219b43e2c8bSIan Lepore MODULE_DEPEND(ofw_pwmbus, pwmbus, 1, 1, 1); 220