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