1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "opt_platform.h" 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/bus.h> 37 #include <sys/conf.h> 38 #include <sys/endian.h> 39 #include <sys/kernel.h> 40 #include <sys/module.h> 41 42 #include <machine/bus.h> 43 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include <dev/pwm/pwmbus.h> 48 49 #include "pwmbus_if.h" 50 #include "pwm_if.h" 51 52 struct pwmbus_channel_data { 53 int reserved; 54 char *name; 55 }; 56 57 struct pwmbus_softc { 58 device_t busdev; 59 device_t dev; 60 61 int nchannels; 62 }; 63 64 device_t 65 pwmbus_attach_bus(device_t dev) 66 { 67 device_t busdev; 68 #ifdef FDT 69 phandle_t node; 70 #endif 71 72 busdev = device_add_child(dev, "pwmbus", -1); 73 if (busdev == NULL) { 74 device_printf(dev, "Cannot add child pwmbus\n"); 75 return (NULL); 76 } 77 if (device_add_child(dev, "pwmc", -1) == NULL) { 78 device_printf(dev, "Cannot add pwmc\n"); 79 device_delete_child(dev, busdev); 80 return (NULL); 81 } 82 83 #ifdef FDT 84 node = ofw_bus_get_node(dev); 85 OF_device_register_xref(OF_xref_from_node(node), dev); 86 #endif 87 88 bus_generic_attach(dev); 89 90 return (busdev); 91 } 92 93 static int 94 pwmbus_probe(device_t dev) 95 { 96 97 device_set_desc(dev, "PWM bus"); 98 return (BUS_PROBE_GENERIC); 99 } 100 101 static int 102 pwmbus_attach(device_t dev) 103 { 104 struct pwmbus_softc *sc; 105 106 sc = device_get_softc(dev); 107 sc->busdev = dev; 108 sc->dev = device_get_parent(dev); 109 110 if (PWM_CHANNEL_MAX(sc->dev, &sc->nchannels) != 0 || 111 sc->nchannels == 0) 112 return (ENXIO); 113 114 if (bootverbose) 115 device_printf(dev, "Registering %d channel(s)\n", sc->nchannels); 116 bus_generic_probe(dev); 117 118 return (bus_generic_attach(dev)); 119 } 120 121 static int 122 pwmbus_detach(device_t dev) 123 { 124 device_t *devlist; 125 int i, rv, ndevs; 126 127 rv = bus_generic_detach(dev); 128 if (rv != 0) 129 return (rv); 130 131 rv = device_get_children(dev, &devlist, &ndevs); 132 if (rv != 0) 133 return (rv); 134 for (i = 0; i < ndevs; i++) 135 device_delete_child(dev, devlist[i]); 136 137 return (0); 138 } 139 140 static int 141 pwmbus_channel_config(device_t bus, int channel, unsigned int period, unsigned int duty) 142 { 143 struct pwmbus_softc *sc; 144 145 sc = device_get_softc(bus); 146 147 if (channel > sc->nchannels) 148 return (EINVAL); 149 150 return (PWM_CHANNEL_CONFIG(sc->dev, channel, period, duty)); 151 } 152 153 static int 154 pwmbus_channel_get_config(device_t bus, int channel, unsigned int *period, unsigned int *duty) 155 { 156 struct pwmbus_softc *sc; 157 158 sc = device_get_softc(bus); 159 160 if (channel > sc->nchannels) 161 return (EINVAL); 162 163 return (PWM_CHANNEL_GET_CONFIG(sc->dev, channel, period, duty)); 164 } 165 166 static int 167 pwmbus_channel_set_flags(device_t bus, int channel, uint32_t flags) 168 { 169 struct pwmbus_softc *sc; 170 171 sc = device_get_softc(bus); 172 173 if (channel > sc->nchannels) 174 return (EINVAL); 175 176 return (PWM_CHANNEL_SET_FLAGS(sc->dev, channel, flags)); 177 } 178 179 static int 180 pwmbus_channel_get_flags(device_t bus, int channel, uint32_t *flags) 181 { 182 struct pwmbus_softc *sc; 183 184 sc = device_get_softc(bus); 185 186 if (channel > sc->nchannels) 187 return (EINVAL); 188 189 return (PWM_CHANNEL_GET_FLAGS(sc->dev, channel, flags)); 190 } 191 192 static int 193 pwmbus_channel_enable(device_t bus, int channel, bool enable) 194 { 195 struct pwmbus_softc *sc; 196 197 sc = device_get_softc(bus); 198 199 if (channel > sc->nchannels) 200 return (EINVAL); 201 202 return (PWM_CHANNEL_ENABLE(sc->dev, channel, enable)); 203 } 204 205 static int 206 pwmbus_channel_is_enabled(device_t bus, int channel, bool *enable) 207 { 208 struct pwmbus_softc *sc; 209 210 sc = device_get_softc(bus); 211 212 if (channel > sc->nchannels) 213 return (EINVAL); 214 215 return (PWM_CHANNEL_IS_ENABLED(sc->dev, channel, enable)); 216 } 217 218 static device_method_t pwmbus_methods[] = { 219 /* device_if */ 220 DEVMETHOD(device_probe, pwmbus_probe), 221 DEVMETHOD(device_attach, pwmbus_attach), 222 DEVMETHOD(device_detach, pwmbus_detach), 223 224 /* pwm interface */ 225 DEVMETHOD(pwmbus_channel_config, pwmbus_channel_config), 226 DEVMETHOD(pwmbus_channel_get_config, pwmbus_channel_get_config), 227 DEVMETHOD(pwmbus_channel_set_flags, pwmbus_channel_set_flags), 228 DEVMETHOD(pwmbus_channel_get_flags, pwmbus_channel_get_flags), 229 DEVMETHOD(pwmbus_channel_enable, pwmbus_channel_enable), 230 DEVMETHOD(pwmbus_channel_is_enabled, pwmbus_channel_is_enabled), 231 232 DEVMETHOD_END 233 }; 234 235 driver_t pwmbus_driver = { 236 "pwmbus", 237 pwmbus_methods, 238 sizeof(struct pwmbus_softc), 239 }; 240 devclass_t pwmbus_devclass; 241 242 EARLY_DRIVER_MODULE(pwmbus, pwm, pwmbus_driver, pwmbus_devclass, 0, 0, 243 BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); 244 MODULE_VERSION(pwmbus, 1); 245