Home | History | Annotate | Line # | Download | only in fdt
      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