19312900fSEmmanuel Vadot /*- 29312900fSEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39312900fSEmmanuel Vadot * 49312900fSEmmanuel Vadot * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> 59312900fSEmmanuel Vadot * 69312900fSEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 79312900fSEmmanuel Vadot * modification, are permitted provided that the following conditions 89312900fSEmmanuel Vadot * are met: 99312900fSEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 109312900fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 119312900fSEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 129312900fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 139312900fSEmmanuel Vadot * documentation and/or other materials provided with the distribution. 149312900fSEmmanuel Vadot * 159312900fSEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 169312900fSEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 179312900fSEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 189312900fSEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 199312900fSEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 209312900fSEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 219312900fSEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 229312900fSEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 239312900fSEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 249312900fSEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 259312900fSEmmanuel Vadot * SUCH DAMAGE. 269312900fSEmmanuel Vadot */ 279312900fSEmmanuel Vadot 289312900fSEmmanuel Vadot #include <sys/cdefs.h> 299312900fSEmmanuel Vadot __FBSDID("$FreeBSD$"); 309312900fSEmmanuel Vadot 319312900fSEmmanuel Vadot #include "opt_platform.h" 329312900fSEmmanuel Vadot 339312900fSEmmanuel Vadot #include <sys/param.h> 349312900fSEmmanuel Vadot #include <sys/systm.h> 359312900fSEmmanuel Vadot #include <sys/bus.h> 369312900fSEmmanuel Vadot #include <sys/conf.h> 379312900fSEmmanuel Vadot #include <sys/endian.h> 389312900fSEmmanuel Vadot #include <sys/kernel.h> 390af7a9a4SIan Lepore #include <sys/malloc.h> 409312900fSEmmanuel Vadot #include <sys/module.h> 41ddfc9c4cSWarner Losh #include <sys/sbuf.h> 429312900fSEmmanuel Vadot 430af7a9a4SIan Lepore #include <dev/pwm/pwmbus.h> 449312900fSEmmanuel Vadot 459312900fSEmmanuel Vadot #include "pwmbus_if.h" 469312900fSEmmanuel Vadot 470af7a9a4SIan Lepore /* 480af7a9a4SIan Lepore * bus_if methods... 490af7a9a4SIan Lepore */ 500af7a9a4SIan Lepore 510af7a9a4SIan Lepore static device_t 520af7a9a4SIan Lepore pwmbus_add_child(device_t dev, u_int order, const char *name, int unit) 530af7a9a4SIan Lepore { 540af7a9a4SIan Lepore device_t child; 550af7a9a4SIan Lepore struct pwmbus_ivars *ivars; 560af7a9a4SIan Lepore 570af7a9a4SIan Lepore child = device_add_child_ordered(dev, order, name, unit); 580af7a9a4SIan Lepore if (child == NULL) 590af7a9a4SIan Lepore return (child); 600af7a9a4SIan Lepore 610af7a9a4SIan Lepore ivars = malloc(sizeof(struct pwmbus_ivars), M_DEVBUF, M_NOWAIT | M_ZERO); 620af7a9a4SIan Lepore if (ivars == NULL) { 630af7a9a4SIan Lepore device_delete_child(dev, child); 640af7a9a4SIan Lepore return (NULL); 650af7a9a4SIan Lepore } 660af7a9a4SIan Lepore device_set_ivars(child, ivars); 670af7a9a4SIan Lepore 680af7a9a4SIan Lepore return (child); 690af7a9a4SIan Lepore } 700af7a9a4SIan Lepore 710af7a9a4SIan Lepore static int 72ddfc9c4cSWarner Losh pwmbus_child_location(device_t dev, device_t child, struct sbuf *sb) 730af7a9a4SIan Lepore { 740af7a9a4SIan Lepore struct pwmbus_ivars *ivars; 750af7a9a4SIan Lepore 760af7a9a4SIan Lepore ivars = device_get_ivars(child); 77ddfc9c4cSWarner Losh sbuf_printf(sb, "hwdev=%s channel=%u", 780af7a9a4SIan Lepore device_get_nameunit(device_get_parent(dev)), ivars->pi_channel); 790af7a9a4SIan Lepore 800af7a9a4SIan Lepore return (0); 810af7a9a4SIan Lepore } 820af7a9a4SIan Lepore 830af7a9a4SIan Lepore static void 840af7a9a4SIan Lepore pwmbus_hinted_child(device_t dev, const char *dname, int dunit) 850af7a9a4SIan Lepore { 860af7a9a4SIan Lepore struct pwmbus_ivars *ivars; 870af7a9a4SIan Lepore device_t child; 880af7a9a4SIan Lepore 890af7a9a4SIan Lepore child = pwmbus_add_child(dev, 0, dname, dunit); 900af7a9a4SIan Lepore 910af7a9a4SIan Lepore /* 920af7a9a4SIan Lepore * If there is a channel hint, use it. Otherwise pi_channel was 930af7a9a4SIan Lepore * initialized to zero, so that's the channel we'll use. 940af7a9a4SIan Lepore */ 950af7a9a4SIan Lepore ivars = device_get_ivars(child); 960af7a9a4SIan Lepore resource_int_value(dname, dunit, "channel", &ivars->pi_channel); 970af7a9a4SIan Lepore } 980af7a9a4SIan Lepore 990af7a9a4SIan Lepore static int 1000af7a9a4SIan Lepore pwmbus_print_child(device_t dev, device_t child) 1010af7a9a4SIan Lepore { 1020af7a9a4SIan Lepore struct pwmbus_ivars *ivars; 1030af7a9a4SIan Lepore int rv; 1040af7a9a4SIan Lepore 1050af7a9a4SIan Lepore ivars = device_get_ivars(child); 1060af7a9a4SIan Lepore 1070af7a9a4SIan Lepore rv = bus_print_child_header(dev, child); 1080af7a9a4SIan Lepore rv += printf(" channel %u", ivars->pi_channel); 1090af7a9a4SIan Lepore rv += bus_print_child_footer(dev, child); 1100af7a9a4SIan Lepore 1110af7a9a4SIan Lepore return (rv); 1120af7a9a4SIan Lepore } 1130af7a9a4SIan Lepore 1140af7a9a4SIan Lepore static void 1150af7a9a4SIan Lepore pwmbus_probe_nomatch(device_t dev, device_t child) 1160af7a9a4SIan Lepore { 1170af7a9a4SIan Lepore struct pwmbus_ivars *ivars; 1180af7a9a4SIan Lepore 1190af7a9a4SIan Lepore ivars = device_get_ivars(child); 1200af7a9a4SIan Lepore if (ivars != NULL) 1210af7a9a4SIan Lepore device_printf(dev, "<unknown> on channel %u\n", 1220af7a9a4SIan Lepore ivars->pi_channel); 1230af7a9a4SIan Lepore 1240af7a9a4SIan Lepore return; 1250af7a9a4SIan Lepore } 1260af7a9a4SIan Lepore 1270af7a9a4SIan Lepore static int 1280af7a9a4SIan Lepore pwmbus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 1290af7a9a4SIan Lepore { 1300af7a9a4SIan Lepore struct pwmbus_ivars *ivars; 1310af7a9a4SIan Lepore 1320af7a9a4SIan Lepore ivars = device_get_ivars(child); 1330af7a9a4SIan Lepore 1340af7a9a4SIan Lepore switch (which) { 1350af7a9a4SIan Lepore case PWMBUS_IVAR_CHANNEL: 1360af7a9a4SIan Lepore *(u_int *)result = ivars->pi_channel; 1370af7a9a4SIan Lepore break; 1380af7a9a4SIan Lepore default: 1390af7a9a4SIan Lepore return (EINVAL); 1400af7a9a4SIan Lepore } 1410af7a9a4SIan Lepore 1420af7a9a4SIan Lepore return (0); 1430af7a9a4SIan Lepore } 1440af7a9a4SIan Lepore 1450af7a9a4SIan Lepore /* 1460af7a9a4SIan Lepore * device_if methods... 1470af7a9a4SIan Lepore */ 1480af7a9a4SIan Lepore 1499312900fSEmmanuel Vadot static int 1509312900fSEmmanuel Vadot pwmbus_probe(device_t dev) 1519312900fSEmmanuel Vadot { 1529312900fSEmmanuel Vadot device_set_desc(dev, "PWM bus"); 1539312900fSEmmanuel Vadot return (BUS_PROBE_GENERIC); 1549312900fSEmmanuel Vadot } 1559312900fSEmmanuel Vadot 1569312900fSEmmanuel Vadot static int 1579312900fSEmmanuel Vadot pwmbus_attach(device_t dev) 1589312900fSEmmanuel Vadot { 1599312900fSEmmanuel Vadot struct pwmbus_softc *sc; 1600af7a9a4SIan Lepore struct pwmbus_ivars *ivars; 1610af7a9a4SIan Lepore device_t child, parent; 1620af7a9a4SIan Lepore u_int chan; 1639312900fSEmmanuel Vadot 1649312900fSEmmanuel Vadot sc = device_get_softc(dev); 165f8f8d87cSIan Lepore sc->dev = dev; 1660af7a9a4SIan Lepore parent = device_get_parent(dev); 1679312900fSEmmanuel Vadot 1680af7a9a4SIan Lepore if (PWMBUS_CHANNEL_COUNT(parent, &sc->nchannels) != 0 || 169f8f8d87cSIan Lepore sc->nchannels == 0) { 170f8f8d87cSIan Lepore device_printf(sc->dev, "No channels on parent %s\n", 1710af7a9a4SIan Lepore device_get_nameunit(parent)); 1729312900fSEmmanuel Vadot return (ENXIO); 173f8f8d87cSIan Lepore } 1749312900fSEmmanuel Vadot 1750af7a9a4SIan Lepore /* Add a pwmc(4) child for each channel. */ 1760af7a9a4SIan Lepore for (chan = 0; chan < sc->nchannels; ++chan) { 1770af7a9a4SIan Lepore if ((child = pwmbus_add_child(sc->dev, 0, "pwmc", -1)) == NULL) { 1780af7a9a4SIan Lepore device_printf(dev, "failed to add pwmc child device " 1790af7a9a4SIan Lepore "for channel %u\n", chan); 1800af7a9a4SIan Lepore continue; 1810af7a9a4SIan Lepore } 1820af7a9a4SIan Lepore ivars = device_get_ivars(child); 1830af7a9a4SIan Lepore ivars->pi_channel = chan; 1840af7a9a4SIan Lepore } 185f8f8d87cSIan Lepore 1860af7a9a4SIan Lepore bus_enumerate_hinted_children(dev); 1879312900fSEmmanuel Vadot bus_generic_probe(dev); 1889312900fSEmmanuel Vadot 1899312900fSEmmanuel Vadot return (bus_generic_attach(dev)); 1909312900fSEmmanuel Vadot } 1919312900fSEmmanuel Vadot 1929312900fSEmmanuel Vadot static int 1939312900fSEmmanuel Vadot pwmbus_detach(device_t dev) 1949312900fSEmmanuel Vadot { 1951e76aee8SIan Lepore int rv; 1969312900fSEmmanuel Vadot 1971e76aee8SIan Lepore if ((rv = bus_generic_detach(dev)) == 0) 1981e76aee8SIan Lepore rv = device_delete_children(dev); 1991e76aee8SIan Lepore 2009312900fSEmmanuel Vadot return (rv); 2019312900fSEmmanuel Vadot } 2029312900fSEmmanuel Vadot 2030af7a9a4SIan Lepore /* 2040af7a9a4SIan Lepore * pwmbus_if methods... 2050af7a9a4SIan Lepore */ 2060af7a9a4SIan Lepore 2079312900fSEmmanuel Vadot static int 2086cdbe2bfSIan Lepore pwmbus_channel_config(device_t dev, u_int chan, u_int period, u_int duty) 2099312900fSEmmanuel Vadot { 210f8f8d87cSIan Lepore return (PWMBUS_CHANNEL_CONFIG(device_get_parent(dev), chan, period, duty)); 2119312900fSEmmanuel Vadot } 2129312900fSEmmanuel Vadot 2139312900fSEmmanuel Vadot static int 2146cdbe2bfSIan Lepore pwmbus_channel_get_config(device_t dev, u_int chan, u_int *period, u_int *duty) 2159312900fSEmmanuel Vadot { 216f8f8d87cSIan Lepore return (PWMBUS_CHANNEL_GET_CONFIG(device_get_parent(dev), chan, period, duty)); 2179312900fSEmmanuel Vadot } 2189312900fSEmmanuel Vadot 2199312900fSEmmanuel Vadot static int 2206cdbe2bfSIan Lepore pwmbus_channel_get_flags(device_t dev, u_int chan, uint32_t *flags) 2219312900fSEmmanuel Vadot { 222f8f8d87cSIan Lepore return (PWMBUS_CHANNEL_GET_FLAGS(device_get_parent(dev), chan, flags)); 2239312900fSEmmanuel Vadot } 2249312900fSEmmanuel Vadot 2259312900fSEmmanuel Vadot static int 2266cdbe2bfSIan Lepore pwmbus_channel_enable(device_t dev, u_int chan, bool enable) 2279312900fSEmmanuel Vadot { 228f8f8d87cSIan Lepore return (PWMBUS_CHANNEL_ENABLE(device_get_parent(dev), chan, enable)); 2299312900fSEmmanuel Vadot } 2309312900fSEmmanuel Vadot 2319312900fSEmmanuel Vadot static int 2326cdbe2bfSIan Lepore pwmbus_channel_set_flags(device_t dev, u_int chan, uint32_t flags) 2339312900fSEmmanuel Vadot { 234f8f8d87cSIan Lepore return (PWMBUS_CHANNEL_SET_FLAGS(device_get_parent(dev), chan, flags)); 2359312900fSEmmanuel Vadot } 2369312900fSEmmanuel Vadot 2379312900fSEmmanuel Vadot static int 2386cdbe2bfSIan Lepore pwmbus_channel_is_enabled(device_t dev, u_int chan, bool *enable) 2399312900fSEmmanuel Vadot { 240f8f8d87cSIan Lepore return (PWMBUS_CHANNEL_IS_ENABLED(device_get_parent(dev), chan, enable)); 241f8f8d87cSIan Lepore } 2429312900fSEmmanuel Vadot 243f8f8d87cSIan Lepore static int 2446cdbe2bfSIan Lepore pwmbus_channel_count(device_t dev, u_int *nchannel) 245f8f8d87cSIan Lepore { 246f8f8d87cSIan Lepore return (PWMBUS_CHANNEL_COUNT(device_get_parent(dev), nchannel)); 2479312900fSEmmanuel Vadot } 2489312900fSEmmanuel Vadot 2499312900fSEmmanuel Vadot static device_method_t pwmbus_methods[] = { 2509312900fSEmmanuel Vadot /* device_if */ 2519312900fSEmmanuel Vadot DEVMETHOD(device_probe, pwmbus_probe), 2529312900fSEmmanuel Vadot DEVMETHOD(device_attach, pwmbus_attach), 2539312900fSEmmanuel Vadot DEVMETHOD(device_detach, pwmbus_detach), 2549312900fSEmmanuel Vadot 2550af7a9a4SIan Lepore /* bus_if */ 2560af7a9a4SIan Lepore DEVMETHOD(bus_add_child, pwmbus_add_child), 257ddfc9c4cSWarner Losh DEVMETHOD(bus_child_location, pwmbus_child_location), 2580af7a9a4SIan Lepore DEVMETHOD(bus_hinted_child, pwmbus_hinted_child), 2590af7a9a4SIan Lepore DEVMETHOD(bus_print_child, pwmbus_print_child), 2600af7a9a4SIan Lepore DEVMETHOD(bus_probe_nomatch, pwmbus_probe_nomatch), 2610af7a9a4SIan Lepore DEVMETHOD(bus_read_ivar, pwmbus_read_ivar), 2620af7a9a4SIan Lepore 263f8f8d87cSIan Lepore /* pwmbus_if */ 264f8f8d87cSIan Lepore DEVMETHOD(pwmbus_channel_count, pwmbus_channel_count), 2659312900fSEmmanuel Vadot DEVMETHOD(pwmbus_channel_config, pwmbus_channel_config), 2669312900fSEmmanuel Vadot DEVMETHOD(pwmbus_channel_get_config, pwmbus_channel_get_config), 2679312900fSEmmanuel Vadot DEVMETHOD(pwmbus_channel_set_flags, pwmbus_channel_set_flags), 2689312900fSEmmanuel Vadot DEVMETHOD(pwmbus_channel_get_flags, pwmbus_channel_get_flags), 2699312900fSEmmanuel Vadot DEVMETHOD(pwmbus_channel_enable, pwmbus_channel_enable), 2709312900fSEmmanuel Vadot DEVMETHOD(pwmbus_channel_is_enabled, pwmbus_channel_is_enabled), 2719312900fSEmmanuel Vadot 2729312900fSEmmanuel Vadot DEVMETHOD_END 2739312900fSEmmanuel Vadot }; 2749312900fSEmmanuel Vadot 2750af7a9a4SIan Lepore driver_t pwmbus_driver = { 2769312900fSEmmanuel Vadot "pwmbus", 2779312900fSEmmanuel Vadot pwmbus_methods, 2789312900fSEmmanuel Vadot sizeof(struct pwmbus_softc), 2799312900fSEmmanuel Vadot }; 2809312900fSEmmanuel Vadot 281024d9473SJohn Baldwin EARLY_DRIVER_MODULE(pwmbus, pwm, pwmbus_driver, 0, 0, 2820af7a9a4SIan Lepore BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 2839312900fSEmmanuel Vadot MODULE_VERSION(pwmbus, 1); 284