diff --git a/sys/arm/conf/AM335X b/sys/arm/conf/AM335X --- a/sys/arm/conf/AM335X +++ b/sys/arm/conf/AM335X @@ -140,7 +140,7 @@ # Pinmux device fdt_pinctrl -#device pinctrl_single +device pinctrl_single # TI Programmable Realtime Unit support device ti_pruss diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -2625,6 +2625,7 @@ dev/phy/phynode_if.m optional phy dev/phy/phy_usb.c optional phy dev/phy/phynode_usb_if.m optional phy +dev/pinctrl/pinctrl_single.c optional fdt fdt_pinctrl pinctrl_single dev/pms/freebsd/driver/ini/src/agtiapi.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sadisc.c optional pmspcv \ diff --git a/sys/dev/pinctrl/pinctrl_single.c b/sys/dev/pinctrl/pinctrl_single.c new file mode 100644 --- /dev/null +++ b/sys/dev/pinctrl/pinctrl_single.c @@ -0,0 +1,386 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2023 Oskar Holmlund + * + * 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. + */ + +/* + * sys/contrib/device-tree/Bindings/pinctrl/pinctrl-single.txt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "syscon_if.h" + +MALLOC_DECLARE(M_SYSCON); + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +#define PINCTRL_SINGLE 2 +#define PINCONF_SINGLE 1 + +static struct ofw_compat_data compat_data[] = { + {"pinctrl-single", PINCTRL_SINGLE}, + {"pinconf-single", PINCONF_SINGLE}, + {NULL, 0}, +}; + +static struct resource_spec pinctrl_single_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +struct pinctrl_single_softc { + device_t sc_dev; + struct resource *sc_res[1]; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + struct syscon *sc_syscon; + + uint32_t sc_ncells; + uint32_t sc_regwidth; + uint32_t sc_funcmask; +}; + +/* --- dev/syscon syscon_method_t interface --- */ +static int +pinctrl_single_syscon_unlocked_write_4(struct syscon *syscon, bus_size_t offset, + uint32_t value) +{ + struct pinctrl_single_softc *sc; + + sc = device_get_softc(syscon->pdev); + DPRINTF(sc->sc_dev, "offset=%lx write %x\n", offset, value); + switch (sc->sc_regwidth) { + case 8: + bus_space_write_1(sc->sc_bst, sc->sc_bsh, offset, value); + break; + case 16: + bus_space_write_2(sc->sc_bst, sc->sc_bsh, offset, value); + break; + case 32: + bus_space_write_4(sc->sc_bst, sc->sc_bsh, offset, value); + break; + } + return (0); +} + +static uint32_t +pinctrl_single_syscon_unlocked_read_4(struct syscon *syscon, bus_size_t offset) +{ + struct pinctrl_single_softc *sc; + uint32_t value; + + sc = device_get_softc(syscon->pdev); + + switch (sc->sc_regwidth) { + case 8: + value = bus_space_read_1(sc->sc_bst, sc->sc_bsh, offset); + case 16: + value = bus_space_read_2(sc->sc_bst, sc->sc_bsh, offset); + case 32: + value = bus_space_read_4(sc->sc_bst, sc->sc_bsh, offset); + } + DPRINTF(sc->sc_dev, "offset=%lx Read %x\n", offset, value); + + return (value); +} + +static syscon_method_t pinctrl_single_syscon_reg_methods[] = { + SYSCONMETHOD(syscon_unlocked_read_4, + pinctrl_single_syscon_unlocked_read_4), + SYSCONMETHOD(syscon_unlocked_write_4, + pinctrl_single_syscon_unlocked_write_4), + + SYSCONMETHOD_END +}; + +DEFINE_CLASS_1(pinctrl_single_syscon_reg, pinctrl_single_syscon_reg_class, + pinctrl_single_syscon_reg_methods, 0, syscon_class); + +static int +pinctrl_single_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) + { + case PINCTRL_SINGLE: + device_set_desc(dev, "pinctrl-single"); + break; + + case PINCONF_SINGLE: + device_set_desc(dev, "pinconf-single"); + break; + + default: + return (ENXIO); + } + + if (bootverbose == 0) + device_quiet(dev); + + return (BUS_PROBE_DEFAULT); +} + +static int +pinctrl_single_attach(device_t dev) +{ + phandle_t node; + int err; + struct pinctrl_single_softc *sc = device_get_softc(dev); + + sc->sc_dev = dev; + + node = ofw_bus_get_node(sc->sc_dev); + + err = OF_getencprop(node, "#pinctrl-cells", + &sc->sc_ncells, sizeof(sc->sc_ncells)); + if (err == -1) { + device_printf(sc->sc_dev, "Missing #pinctrl-cells\n"); + return (ENXIO); + } + if (!(sc->sc_ncells == 1 || sc->sc_ncells == 2)) { + device_printf(sc->sc_dev, "Expect #pinctrl-cells=[1||2]\n"); + return (ENXIO); + } + + err = OF_getencprop(node, "pinctrl-single,register-width", + &sc->sc_regwidth, sizeof(sc->sc_regwidth)); + if (err == -1) { + device_printf(sc->sc_dev, + "Missing pinctrl-single,register-width\n"); + return (ENXIO); + } + if (!(sc->sc_regwidth == 8 || + sc->sc_regwidth == 16 || + sc->sc_regwidth == 32)) { + device_printf(sc->sc_dev, + "Expect pinctrl-single,register-width=[8||16|32]\n"); + return (ENXIO); + } + + err = OF_getencprop(node, "pinctrl-single,function-mask", + &sc->sc_funcmask, sizeof(sc->sc_funcmask)); + if (err == -1) { + device_printf(sc->sc_dev, + "Missing pinctrl-single,function-mask\n"); + return (ENXIO); + } + + + if (bus_alloc_resources(dev, pinctrl_single_res_spec, sc->sc_res)) { + device_printf(dev, "Could not allocate memory resources\n"); + return (ENXIO); + } + + sc->sc_bst = rman_get_bustag(sc->sc_res[0]); + sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); + + /* dev/syscon interface */ + sc->sc_syscon = syscon_create_ofw_node(dev, + &pinctrl_single_syscon_reg_class, node); + if (sc->sc_syscon == NULL) { + device_printf(dev, "Failed to create/register syscon\n"); + bus_release_resources(dev, pinctrl_single_res_spec, + sc->sc_res); + return (ENXIO); + } + + + err = fdt_pinctrl_register(sc->sc_dev, "pinctrl-single,pins"); + if (err != 0) { + device_printf(sc->sc_dev, + "Failed to register pinctrl-single,pins\n"); + + syscon_unregister(sc->sc_syscon); + free(sc->sc_syscon, M_SYSCON); + + bus_release_resources(dev, pinctrl_single_res_spec, + sc->sc_res); + return (ENXIO); + } + fdt_pinctrl_configure_tree(sc->sc_dev); + + return (0); +} + +static int +pinctrl_single_detach(device_t dev) +{ + /* Dont allow it to detach */ + return (EBUSY); +} + +static int +pinctrl_single_pins_configure_pins(device_t dev, phandle_t cfgxref) +{ + uint32_t *values; + uint32_t drive_strength[2]; + uint32_t bias_pullup[4]; + uint32_t bias_pulldown[4]; + bool has_drive_strength = false; + bool has_bias_pullup = false; + bool has_bias_pulldown = false; + int nvalues, i, dI, len; + struct pinctrl_single_softc *sc; + phandle_t cfgnode; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + nvalues = OF_getencprop_alloc_multi(cfgnode, "pinctrl-single,pins", + sizeof(uint32_t), (void **)&values); + + if (nvalues < 0) + return (ENOENT); + if (sc->sc_ncells == 1 && (nvalues % 2) != 0) { + device_printf(sc->sc_dev, + "expect pinctrl-single,pins values to be multiple of 2(%d)\n", + nvalues); + return (ENOENT); + } + else if (sc->sc_ncells == 2 && (nvalues % 3) != 0) { + device_printf(sc->sc_dev, + "expect pinctrl-single,pins values to be multiple of 3(%d)\n", + nvalues); + return (ENOENT); + } + + if (OF_hasprop(cfgnode, "pinctrl-single,drive-strength")) { + len = OF_getencprop(cfgnode, "pinctrl-single,drive-strength", + drive_strength, sizeof(drive_strength)); + if (len == sizeof(drive_strength)) + has_drive_strength = true; + } + + if (OF_hasprop(cfgnode, "pinctrl-single,bias-pullup")) { + len = OF_getencprop(cfgnode, "pinctrl-single,bias-pullup", + bias_pullup, sizeof(bias_pullup)); + if (len == sizeof(bias_pullup)) + has_bias_pullup = true; + } + + if (OF_hasprop(cfgnode, "pinctrl-single,bias-pulldown")) { + len = OF_getencprop(cfgnode, "pinctrl-single,bias-pulldown", + bias_pulldown, sizeof(bias_pulldown)); + if (len == sizeof(bias_pulldown)) + has_bias_pulldown = true; + } + + dI = sc->sc_ncells + 1; + for (i = 0; i < nvalues; i += dI) { + uint32_t value; + uint32_t offset = values[i+0]; + uint32_t conf_value = values[i+1]; + if (sc->sc_ncells == 2) + conf_value |= values[i+2]; + + value = pinctrl_single_syscon_unlocked_read_4(sc->sc_syscon, + offset); + value &= ~sc->sc_funcmask; + value |= (conf_value & sc->sc_funcmask); + + if (has_drive_strength) { + value &= ~drive_strength[1]; + value |= (drive_strength[0] & drive_strength[1]); + } + + if (has_bias_pullup) { + value &= ~bias_pullup[3]; + value |= (bias_pullup[0] & bias_pullup[3]); + } + + if (has_bias_pulldown) { + value &= ~bias_pulldown[3]; + value |= (bias_pulldown[0] & bias_pulldown[3]); + } + + pinctrl_single_syscon_unlocked_write_4(sc->sc_syscon, + offset, value); + } + + OF_prop_free(values); + + return (0); +} + +/* syscon interface */ +static int +pinctrl_single_syscon_get_handle(device_t dev, struct syscon **syscon) +{ + struct pinctrl_single_softc *sc; + + sc = device_get_softc(dev); + *syscon = sc->sc_syscon; + if (*syscon == NULL) + return (ENODEV); + return (0); +} + +static device_method_t pinctrl_single_methods[] = { + DEVMETHOD(device_probe, pinctrl_single_probe), + DEVMETHOD(device_attach, pinctrl_single_attach), + DEVMETHOD(device_detach, pinctrl_single_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, pinctrl_single_pins_configure_pins), + + /* syscon interface */ + DEVMETHOD(syscon_get_handle, pinctrl_single_syscon_get_handle), + + DEVMETHOD_END +}; + +static driver_t pinctrl_single_driver = { + "pinctrl_single", + pinctrl_single_methods, + sizeof(struct pinctrl_single_softc), +}; + +EARLY_DRIVER_MODULE(pinctrl_single, simplebus, pinctrl_single_driver, 0, 0, + BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(pinctrl_single, 1);