1 /* $NetBSD: pinctrl_single.c,v 1.7 2026/08/27 20:06:29 yurix Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared McNeill <jmcneill (at) invisible.ca> 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 ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * 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 __KERNEL_RCSID(0, "$NetBSD: pinctrl_single.c,v 1.7 2026/08/27 20:06:29 yurix Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/device.h> 35 #include <sys/systm.h> 36 #include <sys/gpio.h> 37 38 #include <dev/fdt/fdtvar.h> 39 40 #define PINCTRL_FLAG_PINCONF __BIT(0) /* supports generic pinconf */ 41 #define PINCTRL_FLAG_BIT_PER_MUX __BIT(1) /* bit-per-mux flag from DT */ 42 43 struct pinctrl_single_config { 44 uint32_t flags; 45 }; 46 47 static const struct pinctrl_single_config pinctrl_config = { 48 }; 49 50 static const struct pinctrl_single_config pinconf_config = { 51 .flags = PINCTRL_FLAG_PINCONF 52 }; 53 54 static const struct device_compatible_entry compat_data[] = { 55 { .compat = "pinctrl-single", .data = &pinctrl_config }, 56 { .compat = "pinconf-single", .data = &pinconf_config }, 57 DEVICE_COMPAT_EOL 58 }; 59 60 struct pinctrl_single_softc { 61 device_t sc_dev; 62 int sc_phandle; 63 bus_space_tag_t sc_bst; 64 bus_space_handle_t sc_bsh; 65 uint32_t sc_flags; 66 u_int sc_regwidth; 67 u_int sc_funcmask; 68 }; 69 70 static void 71 pinctrl_single_pins_write(struct pinctrl_single_softc *sc, u_int off, u_int val, 72 u_int mask) 73 { 74 union { 75 uint32_t reg32; 76 uint16_t reg16; 77 uint8_t reg8; 78 } u; 79 80 aprint_debug_dev(sc->sc_dev, 81 "writing %#x mask %#x to %#x\n", val, mask, off); 82 83 switch (sc->sc_regwidth) { 84 case 8: 85 u.reg8 = bus_space_read_1(sc->sc_bst, sc->sc_bsh, off); 86 u.reg8 &= ~mask; 87 u.reg8 |= val; 88 bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, u.reg8); 89 break; 90 case 16: 91 u.reg16 = bus_space_read_2(sc->sc_bst, sc->sc_bsh, off); 92 u.reg16 &= ~mask; 93 u.reg16 |= val; 94 bus_space_write_2(sc->sc_bst, sc->sc_bsh, off, u.reg16); 95 break; 96 case 32: 97 u.reg32 = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); 98 u.reg32 &= ~mask; 99 u.reg32 |= val; 100 bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, u.reg32); 101 break; 102 default: 103 device_printf(sc->sc_dev, "%s: unsupported reg width %d\n", 104 __func__, sc->sc_regwidth); 105 break; 106 } 107 } 108 109 static int 110 pinctrl_single_pins_set_config(device_t dev, const void *data, size_t len) 111 { 112 struct pinctrl_single_softc * const sc = device_private(dev); 113 const u_int *pins; 114 int pinslen; 115 116 switch (len) { 117 case 4: 118 if (ISSET(sc->sc_flags, PINCTRL_FLAG_BIT_PER_MUX)) 119 return -1; 120 break; 121 case 8: 122 break; 123 default: 124 return -1; 125 } 126 127 const int phandle = fdtbus_get_phandle_from_native(be32dec(data)); 128 129 if (ISSET(sc->sc_flags, PINCTRL_FLAG_BIT_PER_MUX)) { 130 pins = fdtbus_get_prop(phandle, "pinctrl-single,bits", 131 &pinslen); 132 } else { 133 pins = fdtbus_get_prop(phandle, "pinctrl-single,pins", 134 &pinslen); 135 } 136 if (pins == NULL) 137 return -1; 138 139 while (pinslen >= 4 + len) { 140 if (ISSET(sc->sc_flags, PINCTRL_FLAG_BIT_PER_MUX)) { 141 const int off = be32toh(pins[0]); 142 const int val = be32toh(pins[1]); 143 const int mask = be32toh(pins[2]); 144 145 pinctrl_single_pins_write(sc, off, val, mask); 146 } else { 147 const int off = be32toh(pins[0]); 148 const int val = be32toh(pins[1]); 149 const int mux = len == 4 ? 0 : be32toh(pins[2]); 150 151 pinctrl_single_pins_write(sc, off, val | mux, 152 sc->sc_funcmask); 153 } 154 155 pins += 1 + (len / 4); 156 pinslen -= (4 + len); 157 } 158 159 return 0; 160 } 161 162 static struct fdtbus_pinctrl_controller_func pinctrl_single_pins_funcs = { 163 .set_config = pinctrl_single_pins_set_config, 164 }; 165 166 static int 167 pinctrl_single_match(device_t parent, cfdata_t cf, void *aux) 168 { 169 struct fdt_attach_args * const faa = aux; 170 171 return of_compatible_match(faa->faa_phandle, compat_data); 172 } 173 174 static void 175 pinctrl_single_attach(device_t parent, device_t self, void *aux) 176 { 177 struct pinctrl_single_softc * const sc = device_private(self); 178 struct fdt_attach_args * const faa = aux; 179 const int phandle = faa->faa_phandle; 180 const struct pinctrl_single_config *conf; 181 bus_addr_t addr; 182 bus_size_t size; 183 int child; 184 185 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 186 aprint_error(": couldn't get registers\n"); 187 return; 188 } 189 190 conf = of_compatible_lookup(phandle, compat_data)->data; 191 192 sc->sc_dev = self; 193 sc->sc_phandle = phandle; 194 sc->sc_bst = faa->faa_bst; 195 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 196 aprint_error(": couldn't map registers\n"); 197 return; 198 } 199 sc->sc_flags = conf->flags; 200 201 if (of_getprop_uint32(phandle, "pinctrl-single,register-width", &sc->sc_regwidth) != 0) { 202 aprint_error(": missing 'pinctrl-single,register-width' property\n"); 203 return; 204 } 205 if (of_getprop_uint32(phandle, "pinctrl-single,function-mask", &sc->sc_funcmask) != 0) { 206 aprint_error(": missing 'pinctrl-single,function-mask' property\n"); 207 return; 208 } 209 210 switch (sc->sc_regwidth) { 211 case 8: 212 case 16: 213 case 32: 214 break; 215 default: 216 aprint_error(": register width %d not supported\n", sc->sc_regwidth); 217 return; 218 } 219 220 if (of_hasprop(phandle, "pinctrl-single,bit-per-mux")) { 221 sc->sc_flags |= PINCTRL_FLAG_BIT_PER_MUX; 222 } 223 224 aprint_naive("\n"); 225 aprint_normal("\n"); 226 227 for (child = OF_child(phandle); child; child = OF_peer(child)) { 228 if (of_hasprop(child, "pinctrl-single,pins") 229 || of_hasprop(child, "pinctrl-single,bits")) { 230 fdtbus_register_pinctrl_config(self, child, 231 &pinctrl_single_pins_funcs); 232 } 233 } 234 235 fdtbus_pinctrl_set_config(phandle, "default"); 236 } 237 238 CFATTACH_DECL_NEW(pinctrl_single, sizeof(struct pinctrl_single_softc), 239 pinctrl_single_match, pinctrl_single_attach, NULL, NULL); 240