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