1 /* $NetBSD: fdt_spi.c,v 1.13 2025/09/14 16:00:04 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2021, 2022, 2025 The NetBSD Foundation, Inc. 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright (c) 2019 The NetBSD Foundation, Inc. 31 * All rights reserved. 32 * 33 * This code is derived from software contributed to The NetBSD Foundation 34 * by Tobias Nygren. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 46 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 47 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 48 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 49 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 50 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 51 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 52 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 53 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 55 * POSSIBILITY OF SUCH DAMAGE. 56 */ 57 58 #include <sys/cdefs.h> 59 __KERNEL_RCSID(0, "$NetBSD: fdt_spi.c,v 1.13 2025/09/14 16:00:04 thorpej Exp $"); 60 61 #include <sys/param.h> 62 #include <sys/device.h> 63 #include <sys/kmem.h> 64 #include <sys/queue.h> 65 66 #include <dev/fdt/fdtvar.h> 67 68 #include <dev/ofw/openfirm.h> 69 70 #include <dev/spi/spi_calls.h> 71 72 struct fdtbus_spi_controller { 73 device_t spi_dev; 74 const struct spi_controller *spi_controller; 75 LIST_ENTRY(fdtbus_spi_controller) spi_next; 76 }; 77 78 static LIST_HEAD(, fdtbus_spi_controller) fdtbus_spi_controllers = 79 LIST_HEAD_INITIALIZER(fdtbus_spi_controllers); 80 81 /* XXX Maybe garbage-collect this. */ 82 int 83 fdtbus_register_spi_controller(device_t dev, 84 const struct spi_controller *controller) 85 { 86 struct fdtbus_spi_controller *spi; 87 88 spi = kmem_alloc(sizeof(*spi), KM_SLEEP); 89 spi->spi_dev = dev; 90 spi->spi_controller = controller; 91 92 LIST_INSERT_HEAD(&fdtbus_spi_controllers, spi, spi_next); 93 94 return 0; 95 } 96 97 #if 0 98 static const struct spi_controller * 99 fdtbus_get_spi_controller(int phandle) 100 { 101 struct fdtbus_spi_controller *spi; 102 103 LIST_FOREACH(spi, &fdtbus_spi_controllers, spi_next) { 104 if (phandle == 105 devhandle_to_of(device_handle(spi->spi_dev))) { 106 return spi->spi_controller; 107 } 108 } 109 return NULL; 110 } 111 #endif 112 113 static int 114 fdtbus_spi_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) 115 { 116 struct spi_enumerate_devices_args *args = v; 117 int spi_node, node; 118 char name[32], compat_buf[32]; 119 bus_addr_t chip_select; 120 char *clist; 121 int clist_size; 122 bool cbrv; 123 124 spi_node = devhandle_to_of(call_handle); 125 126 for (node = OF_child(spi_node); node != 0; node = OF_peer(node)) { 127 if (OF_getprop(node, "name", name, sizeof(name)) <= 0) { 128 continue; 129 } 130 131 if (fdtbus_get_reg(node, 0, &chip_select, NULL) != 0) { 132 continue; 133 } 134 135 /* Device Tree bindings specify a max chip select of 256. */ 136 if (chip_select > 256) { 137 continue; 138 } 139 140 clist_size = OF_getproplen(node, "compatible"); 141 if (clist_size <= 0) { 142 continue; 143 } 144 145 clist = kmem_tmpbuf_alloc(clist_size, 146 compat_buf, sizeof(compat_buf), KM_SLEEP); 147 if (OF_getprop(node, "compatible", clist, clist_size) < 148 clist_size) { 149 kmem_tmpbuf_free(clist, clist_size, compat_buf); 150 continue; 151 } 152 153 args->chip_select = (int)chip_select; 154 args->sa->sa_name = name; 155 args->sa->sa_clist = clist; 156 args->sa->sa_clist_size = clist_size; 157 args->sa->sa_devhandle = devhandle_from_of(call_handle, node); 158 159 cbrv = args->callback(dev, args); 160 161 kmem_tmpbuf_free(clist, clist_size, compat_buf); 162 163 if (!cbrv) { 164 break; 165 } 166 } 167 168 return 0; 169 } 170 OF_DEVICE_CALL_REGISTER(SPI_ENUMERATE_DEVICES_STR, 171 fdtbus_spi_enumerate_devices); 172 173 static bool 174 fdtbus_spi_bus_width_valid_p(uint32_t val) 175 { 176 switch (val) { 177 case 0: 178 case 1: 179 case 2: 180 case 4: 181 case 8: 182 return true; 183 184 default: 185 return false; 186 } 187 } 188 189 static int 190 fdtbus_spi_get_transfer_mode(device_t dev, devhandle_t call_handle, void *v) 191 { 192 struct spi_get_transfer_mode_args *args = v; 193 uint32_t val32; 194 int node; 195 196 node = devhandle_to_of(call_handle); 197 198 args->mode = SPI_MODE_0; 199 if (of_hasprop(node, "spi-cpha")) { 200 args->mode |= SPI_MODE_CPHA; 201 } 202 if (of_hasprop(node, "spi-cpol")) { 203 args->mode |= SPI_MODE_CPOL; 204 } 205 206 if (of_getprop_uint32(node, "spi-max-frequency", &val32) == 0) { 207 args->max_frequency = val32; 208 } 209 210 if (of_hasprop(node, "spi-3wire")) { 211 args->flags |= SPI_F_3WIRE; 212 } 213 if (of_hasprop(node, "spi-cs-high")) { 214 args->flags |= SPI_F_CS_HIGH; 215 } 216 if (of_hasprop(node, "spi-lsb-first")) { 217 args->flags |= SPI_F_LSB; 218 } 219 220 if (of_getprop_uint32(node, "spi-cs-setup-delay-ns", &val32) == 0) { 221 args->cs_setup_delay_ns = val32; 222 } 223 if (of_getprop_uint32(node, "spi-cs-hold-delay-ns", &val32) == 0) { 224 args->cs_hold_delay_ns = val32; 225 } 226 if (of_getprop_uint32(node, "spi-cs-inactive-delay-ns", &val32) == 0) { 227 args->cs_inact_delay_ns = val32; 228 } 229 230 args->rx_bus_width = 1; 231 if (of_getprop_uint32(node, "spi-rx-bus-width", &val32) == 0) { 232 if (!fdtbus_spi_bus_width_valid_p(val32)) { 233 aprint_error_dev(dev, 234 "device tree error: \"spi-rx-bus-width\" " 235 "value (%u) invalid\n", val32); 236 return EINVAL; 237 } 238 args->rx_bus_width = val32; 239 } 240 241 if (of_getprop_uint32(node, "spi-rx-delay-us", &val32) == 0) { 242 args->rx_delay_us = val32; 243 } 244 245 /* 246 * XXX Yes, the Device Tree bindings for SPI peripherals really 247 * leave off the spi- prefix for this particular property. 248 */ 249 if (of_getprop_uint32(node, "rx-sample-delay-ns", &val32) == 0) { 250 args->rx_sample_delay_ns = val32; 251 } 252 253 args->tx_bus_width = 1; 254 if (of_getprop_uint32(node, "spi-tx-bus-width", &val32) == 0) { 255 if (!fdtbus_spi_bus_width_valid_p(val32)) { 256 aprint_error_dev(dev, 257 "device tree error: \"spi-tx-bus-width\" " 258 "value (%u) invalid\n", val32); 259 return EINVAL; 260 } 261 args->tx_bus_width = val32; 262 } 263 264 if (of_getprop_uint32(node, "spi-tx-delay-us", &val32) == 0) { 265 args->tx_delay_us = val32; 266 } 267 268 return 0; 269 } 270 OF_DEVICE_CALL_REGISTER(SPI_GET_TRANSFER_MODE_STR, 271 fdtbus_spi_get_transfer_mode); 272