1 /* $NetBSD: i2cmux_fdt.c,v 1.11 2025/09/16 13:09:13 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: i2cmux_fdt.c,v 1.11 2025/09/16 13:09:13 thorpej Exp $"); 34 35 #include <sys/types.h> 36 #include <sys/device.h> 37 #include <sys/kernel.h> 38 #include <sys/kmem.h> 39 #include <sys/bus.h> 40 #include <sys/gpio.h> 41 42 #include <dev/fdt/fdtvar.h> 43 #include <dev/i2c/i2cmuxvar.h> 44 45 /*****************************************************************************/ 46 47 struct mux_info_gpio { 48 struct fdtbus_gpio_pin **pins; 49 int npins; 50 uint32_t idle_value; 51 bool has_idle_value; 52 }; 53 54 struct bus_info_gpio { 55 bus_addr_t value; 56 }; 57 58 #define IICMUX_TO_PHANDLE(sc) \ 59 devhandle_to_of(device_handle((sc)->sc_dev)) 60 61 #define BUS_TO_PHANDLE(bus) \ 62 devhandle_to_of((bus)->devhandle) 63 64 static void * 65 iicmux_gpio_get_mux_info(struct iicmux_softc * const sc) 66 { 67 const int phandle = IICMUX_TO_PHANDLE(sc); 68 struct mux_info_gpio *mux_data; 69 int i; 70 71 mux_data = kmem_zalloc(sizeof(*mux_data), KM_SLEEP); 72 73 mux_data->npins = fdtbus_gpio_count(phandle, "mux-gpios"); 74 if (mux_data->npins == 0) { 75 aprint_error_dev(sc->sc_dev, 76 "unable to get mux-gpios property\n"); 77 goto bad; 78 } 79 80 mux_data->pins = 81 kmem_zalloc(sizeof(*mux_data->pins) * mux_data->npins, KM_SLEEP); 82 for (i = 0; i < mux_data->npins; i++) { 83 mux_data->pins[i] = fdtbus_gpio_acquire_index(phandle, 84 "mux-gpios", i, GPIO_PIN_OUTPUT); 85 if (mux_data->pins[i] == NULL) { 86 aprint_error_dev(sc->sc_dev, 87 "unable to acquire gpio #%d\n", i); 88 goto bad; 89 } 90 } 91 92 mux_data->has_idle_value = 93 of_getprop_uint32(phandle, "idle-state", 94 &mux_data->idle_value) == 0; 95 96 return mux_data; 97 98 bad: 99 for (i = 0; i < mux_data->npins; i++) { 100 if (mux_data->pins[i] != NULL) { 101 fdtbus_gpio_release(mux_data->pins[i]); 102 } 103 } 104 kmem_free(mux_data, sizeof(*mux_data)); 105 return NULL; 106 } 107 108 static void * 109 iicmux_gpio_get_bus_info(struct iicmux_bus * const bus) 110 { 111 struct iicmux_softc * const sc = bus->mux; 112 struct bus_info_gpio *bus_info; 113 const int bus_phandle = BUS_TO_PHANDLE(bus); 114 int error; 115 116 bus_info = kmem_zalloc(sizeof(*bus_info), KM_SLEEP); 117 118 error = fdtbus_get_reg(bus_phandle, 0, &bus_info->value, NULL); 119 if (error) { 120 aprint_error_dev(sc->sc_dev, 121 "unable to get reg property for bus %d\n", bus->busidx); 122 kmem_free(bus_info, sizeof(*bus_info)); 123 return NULL; 124 } 125 126 return bus_info; 127 } 128 129 static void 130 iicmux_gpio_set_value(struct iicmux_softc * const sc, bus_addr_t value) 131 { 132 struct mux_info_gpio * const mux_info = sc->sc_mux_data; 133 int i; 134 135 for (i = 0; i < mux_info->npins; i++, value >>= 1) { 136 fdtbus_gpio_write(mux_info->pins[i], value & 1); 137 } 138 } 139 140 static int 141 iicmux_gpio_acquire_bus(struct iicmux_bus * const bus, int const flags __unused) 142 { 143 struct bus_info_gpio * const bus_info = bus->bus_data; 144 145 iicmux_gpio_set_value(bus->mux, bus_info->value); 146 147 return 0; 148 } 149 150 static void 151 iicmux_gpio_release_bus(struct iicmux_bus * const bus, int const flags __unused) 152 { 153 struct iicmux_softc * const sc = bus->mux; 154 struct mux_info_gpio * const mux_info = sc->sc_mux_data; 155 156 if (mux_info->has_idle_value) { 157 iicmux_gpio_set_value(sc, mux_info->idle_value); 158 } 159 } 160 161 static const struct iicmux_config iicmux_gpio_config = { 162 .desc = "GPIO", 163 .get_mux_info = iicmux_gpio_get_mux_info, 164 .get_bus_info = iicmux_gpio_get_bus_info, 165 .acquire_bus = iicmux_gpio_acquire_bus, 166 .release_bus = iicmux_gpio_release_bus, 167 }; 168 169 /*****************************************************************************/ 170 171 struct mux_info_pinctrl { 172 u_int idle_idx; 173 bool has_idle_idx; 174 } sc_pinctrl; 175 176 struct bus_info_pinctrl { 177 bus_addr_t idx; 178 }; 179 180 static void * 181 iicmux_pinctrl_get_mux_info(struct iicmux_softc * const sc) 182 { 183 const int phandle = IICMUX_TO_PHANDLE(sc); 184 struct mux_info_pinctrl *mux_info; 185 186 mux_info = kmem_alloc(sizeof(*mux_info), KM_SLEEP); 187 188 mux_info->has_idle_idx = 189 fdtbus_get_index(phandle, "pinctrl-names", "idle", 190 &mux_info->idle_idx) == 0; 191 192 return mux_info; 193 } 194 195 static void * 196 iicmux_pinctrl_get_bus_info(struct iicmux_bus * const bus) 197 { 198 struct iicmux_softc * const sc = bus->mux; 199 struct bus_info_pinctrl *bus_info; 200 const int bus_phandle = BUS_TO_PHANDLE(bus); 201 int error; 202 203 bus_info = kmem_alloc(sizeof(*bus_info), KM_SLEEP); 204 205 error = fdtbus_get_reg(bus_phandle, 0, &bus_info->idx, NULL); 206 if (error) { 207 aprint_error_dev(sc->sc_dev, 208 "unable to get reg property for bus %d\n", bus->busidx); 209 kmem_free(bus_info, sizeof(*bus_info)); 210 return NULL; 211 } 212 213 return bus_info; 214 } 215 216 static int 217 iicmux_pinctrl_acquire_bus(struct iicmux_bus * const bus, 218 int const flags __unused) 219 { 220 struct iicmux_softc * const sc = bus->mux; 221 const int phandle = IICMUX_TO_PHANDLE(sc); 222 struct bus_info_pinctrl * const bus_info = bus->bus_data; 223 224 return fdtbus_pinctrl_set_config_index(phandle, bus_info->idx); 225 } 226 227 static void 228 iicmux_pinctrl_release_bus(struct iicmux_bus * const bus, 229 int const flags __unused) 230 { 231 struct iicmux_softc * const sc = bus->mux; 232 const int phandle = IICMUX_TO_PHANDLE(sc); 233 struct mux_info_pinctrl * const mux_info = sc->sc_mux_data; 234 235 if (mux_info->has_idle_idx) { 236 (void) fdtbus_pinctrl_set_config_index(phandle, 237 mux_info->idle_idx); 238 } 239 } 240 241 static const struct iicmux_config iicmux_pinctrl_config = { 242 .desc = "PinMux", 243 .get_mux_info = iicmux_pinctrl_get_mux_info, 244 .get_bus_info = iicmux_pinctrl_get_bus_info, 245 .acquire_bus = iicmux_pinctrl_acquire_bus, 246 .release_bus = iicmux_pinctrl_release_bus, 247 }; 248 249 /*****************************************************************************/ 250 251 static const struct device_compatible_entry compat_data[] = { 252 { .compat = "i2c-mux-gpio", 253 .data = &iicmux_gpio_config }, 254 255 { .compat = "i2c-mux-pinctrl", 256 .data = &iicmux_pinctrl_config }, 257 258 DEVICE_COMPAT_EOL 259 }; 260 261 static int 262 iicmux_fdt_match(device_t const parent, cfdata_t const match, void * const aux) 263 { 264 struct fdt_attach_args * const faa = aux; 265 266 return of_compatible_match(faa->faa_phandle, compat_data); 267 } 268 269 static void 270 iicmux_fdt_attach(device_t const parent, device_t const self, void * const aux) 271 { 272 struct iicmux_softc * const sc = device_private(self); 273 struct fdt_attach_args * const faa = aux; 274 const int phandle = faa->faa_phandle; 275 276 sc->sc_dev = self; 277 sc->sc_config = of_compatible_lookup(phandle, compat_data)->data; 278 279 aprint_naive("\n"); 280 aprint_normal(": %s I2C mux\n", sc->sc_config->desc); 281 282 sc->sc_i2c_parent = fdtbus_i2c_acquire(phandle, "i2c-parent"); 283 if (sc->sc_i2c_parent == NULL) { 284 aprint_error_dev(sc->sc_dev, "unable to acquire i2c-parent\n"); 285 return; 286 } 287 288 iicmux_attach(sc); 289 } 290 291 CFATTACH_DECL_NEW(iicmux_fdt, sizeof(struct iicmux_softc), 292 iicmux_fdt_match, iicmux_fdt_attach, NULL, NULL); 293