1*0f9e9ec2Sjsg /* $OpenBSD: rkspi.c,v 1.2 2024/05/13 01:15:50 jsg Exp $ */
22e0a1eb9Skettenis /*
32e0a1eb9Skettenis * Copyright (c) 2018,2023 Patrick Wildt <patrick@blueri.se>
42e0a1eb9Skettenis * Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org>
52e0a1eb9Skettenis *
62e0a1eb9Skettenis * Permission to use, copy, modify, and distribute this software for any
72e0a1eb9Skettenis * purpose with or without fee is hereby granted, provided that the above
82e0a1eb9Skettenis * copyright notice and this permission notice appear in all copies.
92e0a1eb9Skettenis *
102e0a1eb9Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
112e0a1eb9Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
122e0a1eb9Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
132e0a1eb9Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
142e0a1eb9Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
152e0a1eb9Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
162e0a1eb9Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
172e0a1eb9Skettenis */
182e0a1eb9Skettenis
192e0a1eb9Skettenis #include <sys/param.h>
202e0a1eb9Skettenis #include <sys/systm.h>
212e0a1eb9Skettenis #include <sys/device.h>
222e0a1eb9Skettenis
232e0a1eb9Skettenis #include <machine/bus.h>
242e0a1eb9Skettenis #include <machine/fdt.h>
252e0a1eb9Skettenis
262e0a1eb9Skettenis #include <dev/ofw/openfirm.h>
272e0a1eb9Skettenis #include <dev/ofw/ofw_clock.h>
282e0a1eb9Skettenis #include <dev/ofw/ofw_pinctrl.h>
292e0a1eb9Skettenis #include <dev/ofw/fdt.h>
302e0a1eb9Skettenis #include <dev/spi/spivar.h>
312e0a1eb9Skettenis
322e0a1eb9Skettenis /* registers */
332e0a1eb9Skettenis #define SPI_CTRLR0 0x0000
342e0a1eb9Skettenis #define SPI_CTRLR0_DFS_4BIT (0x0 << 0)
352e0a1eb9Skettenis #define SPI_CTRLR0_DFS_8BIT (0x1 << 0)
362e0a1eb9Skettenis #define SPI_CTRLR0_DFS_16BIT (0x2 << 0)
372e0a1eb9Skettenis #define SPI_CTRLR0_SCPH (0x1 << 6)
382e0a1eb9Skettenis #define SPI_CTRLR0_SCPOL (0x1 << 7)
392e0a1eb9Skettenis #define SPI_CTRLR0_CSM_KEEP (0x0 << 8)
402e0a1eb9Skettenis #define SPI_CTRLR0_CSM_HALF (0x1 << 8)
412e0a1eb9Skettenis #define SPI_CTRLR0_CSM_ONE (0x2 << 8)
422e0a1eb9Skettenis #define SPI_CTRLR0_SSD_HALF (0x0 << 10)
432e0a1eb9Skettenis #define SPI_CTRLR0_SSD_ONE (0x1 << 10)
442e0a1eb9Skettenis #define SPI_CTRLR0_EM_LITTLE (0x0 << 11)
452e0a1eb9Skettenis #define SPI_CTRLR0_EM_BIG (0x1 << 11)
462e0a1eb9Skettenis #define SPI_CTRLR0_FBM_MSB (0x0 << 12)
472e0a1eb9Skettenis #define SPI_CTRLR0_FBM_LSB (0x1 << 12)
482e0a1eb9Skettenis #define SPI_CTRLR0_BHT_16BIT (0x0 << 13)
492e0a1eb9Skettenis #define SPI_CTRLR0_BHT_8BIT (0x1 << 13)
502e0a1eb9Skettenis #define SPI_CTRLR0_RSD(x) ((x) << 14)
512e0a1eb9Skettenis #define SPI_CTRLR0_FRF_SPI (0x0 << 16)
522e0a1eb9Skettenis #define SPI_CTRLR0_FRF_SSP (0x1 << 16)
532e0a1eb9Skettenis #define SPI_CTRLR0_FRF_MICROWIRE (0x2 << 16)
542e0a1eb9Skettenis #define SPI_CTRLR0_XFM_TR (0x0 << 18)
552e0a1eb9Skettenis #define SPI_CTRLR0_XFM_TO (0x1 << 18)
562e0a1eb9Skettenis #define SPI_CTRLR0_XFM_RO (0x2 << 18)
572e0a1eb9Skettenis #define SPI_CTRLR0_SOI(x) ((1 << (x)) << 23)
582e0a1eb9Skettenis #define SPI_CTRLR1 0x0004
592e0a1eb9Skettenis #define SPI_ENR 0x0008
602e0a1eb9Skettenis #define SPI_SER 0x000c
612e0a1eb9Skettenis #define SPI_SER_CS(x) ((1 << (x)) << 0)
622e0a1eb9Skettenis #define SPI_BAUDR 0x0010
632e0a1eb9Skettenis #define SPI_TXFTLR 0x0014
642e0a1eb9Skettenis #define SPI_RXFTLR 0x0018
652e0a1eb9Skettenis #define SPI_TXFLR 0x001c
662e0a1eb9Skettenis #define SPI_RXFLR 0x0020
672e0a1eb9Skettenis #define SPI_SR 0x0024
682e0a1eb9Skettenis #define SPI_SR_BSF (1 << 0)
692e0a1eb9Skettenis #define SPI_SR_TFF (1 << 1)
702e0a1eb9Skettenis #define SPI_SR_TFE (1 << 2)
712e0a1eb9Skettenis #define SPI_SR_RFE (1 << 3)
722e0a1eb9Skettenis #define SPI_SR_RFF (1 << 4)
732e0a1eb9Skettenis #define SPI_IPR 0x0028
742e0a1eb9Skettenis #define SPI_IMR 0x002c
752e0a1eb9Skettenis #define SPI_ISR 0x0030
762e0a1eb9Skettenis #define SPI_RISR 0x0034
772e0a1eb9Skettenis #define SPI_ICR 0x0038
782e0a1eb9Skettenis #define SPI_ICR_MASK (0x7f << 0)
792e0a1eb9Skettenis #define SPI_DMACR 0x003c
802e0a1eb9Skettenis #define SPI_DMATDLR 0x0040
812e0a1eb9Skettenis #define SPI_DMARDLR 0x0044
822e0a1eb9Skettenis #define SPI_VERSION 0x0048
832e0a1eb9Skettenis #define SPI_TXDR 0x0400
842e0a1eb9Skettenis #define SPI_RXDR 0x0800
852e0a1eb9Skettenis
862e0a1eb9Skettenis #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
872e0a1eb9Skettenis
882e0a1eb9Skettenis struct rkspi_softc {
892e0a1eb9Skettenis struct device sc_dev;
902e0a1eb9Skettenis bus_space_tag_t sc_iot;
912e0a1eb9Skettenis bus_space_handle_t sc_ioh;
922e0a1eb9Skettenis bus_size_t sc_ios;
932e0a1eb9Skettenis int sc_node;
942e0a1eb9Skettenis
952e0a1eb9Skettenis struct rwlock sc_buslock;
962e0a1eb9Skettenis struct spi_controller sc_tag;
972e0a1eb9Skettenis
982e0a1eb9Skettenis int sc_ridx;
992e0a1eb9Skettenis int sc_widx;
1002e0a1eb9Skettenis int sc_cs;
1012e0a1eb9Skettenis u_int sc_cs_delay;
1022e0a1eb9Skettenis u_int sc_spi_freq;
1032e0a1eb9Skettenis };
1042e0a1eb9Skettenis
1052e0a1eb9Skettenis int rkspi_match(struct device *, void *, void *);
1062e0a1eb9Skettenis void rkspi_attach(struct device *, struct device *, void *);
1072e0a1eb9Skettenis int rkspi_detach(struct device *, int);
1082e0a1eb9Skettenis
1092e0a1eb9Skettenis void rkspi_config(void *, struct spi_config *);
1102e0a1eb9Skettenis int rkspi_transfer(void *, char *, char *, int, int);
1112e0a1eb9Skettenis int rkspi_acquire_bus(void *, int);
1122e0a1eb9Skettenis void rkspi_release_bus(void *, int);
1132e0a1eb9Skettenis
1142e0a1eb9Skettenis int rkspi_wait_state(struct rkspi_softc *, uint32_t, uint32_t);
1152e0a1eb9Skettenis
1162e0a1eb9Skettenis void rkspi_scan(struct rkspi_softc *);
1172e0a1eb9Skettenis
1182e0a1eb9Skettenis #define HREAD4(sc, reg) \
1192e0a1eb9Skettenis (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
1202e0a1eb9Skettenis #define HWRITE4(sc, reg, val) \
1212e0a1eb9Skettenis bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
1222e0a1eb9Skettenis #define HSET4(sc, reg, bits) \
1232e0a1eb9Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
1242e0a1eb9Skettenis #define HCLR4(sc, reg, bits) \
1252e0a1eb9Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
1262e0a1eb9Skettenis
1272e0a1eb9Skettenis const struct cfattach rkspi_ca = {
1282e0a1eb9Skettenis sizeof(struct rkspi_softc), rkspi_match, rkspi_attach,
1292e0a1eb9Skettenis rkspi_detach
1302e0a1eb9Skettenis };
1312e0a1eb9Skettenis
1322e0a1eb9Skettenis struct cfdriver rkspi_cd = {
1332e0a1eb9Skettenis NULL, "rkspi", DV_DULL
1342e0a1eb9Skettenis };
1352e0a1eb9Skettenis
1362e0a1eb9Skettenis int
rkspi_match(struct device * parent,void * match,void * aux)1372e0a1eb9Skettenis rkspi_match(struct device *parent, void *match, void *aux)
1382e0a1eb9Skettenis {
1392e0a1eb9Skettenis struct fdt_attach_args *faa = aux;
1402e0a1eb9Skettenis
1412e0a1eb9Skettenis return OF_is_compatible(faa->fa_node, "rockchip,rk3066-spi");
1422e0a1eb9Skettenis }
1432e0a1eb9Skettenis
1442e0a1eb9Skettenis void
rkspi_attach(struct device * parent,struct device * self,void * aux)1452e0a1eb9Skettenis rkspi_attach(struct device *parent, struct device *self, void *aux)
1462e0a1eb9Skettenis {
1472e0a1eb9Skettenis struct rkspi_softc *sc = (struct rkspi_softc *)self;
1482e0a1eb9Skettenis struct fdt_attach_args *faa = aux;
1492e0a1eb9Skettenis
1502e0a1eb9Skettenis if (faa->fa_nreg < 1)
1512e0a1eb9Skettenis return;
1522e0a1eb9Skettenis
1532e0a1eb9Skettenis sc->sc_iot = faa->fa_iot;
1542e0a1eb9Skettenis sc->sc_ios = faa->fa_reg[0].size;
1552e0a1eb9Skettenis sc->sc_node = faa->fa_node;
1562e0a1eb9Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1572e0a1eb9Skettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1582e0a1eb9Skettenis printf(": can't map registers\n");
1592e0a1eb9Skettenis return;
1602e0a1eb9Skettenis }
1612e0a1eb9Skettenis
1622e0a1eb9Skettenis pinctrl_byname(sc->sc_node, "default");
1632e0a1eb9Skettenis clock_set_assigned(sc->sc_node);
1642e0a1eb9Skettenis clock_enable(sc->sc_node, "apb_pclk");
1652e0a1eb9Skettenis clock_enable(sc->sc_node, "spiclk");
1662e0a1eb9Skettenis
1672e0a1eb9Skettenis sc->sc_spi_freq = clock_get_frequency(sc->sc_node, "spiclk");
1682e0a1eb9Skettenis
1692e0a1eb9Skettenis printf("\n");
1702e0a1eb9Skettenis
1712e0a1eb9Skettenis HWRITE4(sc, SPI_ENR, 0);
1722e0a1eb9Skettenis HWRITE4(sc, SPI_DMACR, 0);
1732e0a1eb9Skettenis HWRITE4(sc, SPI_DMATDLR, 0);
1742e0a1eb9Skettenis HWRITE4(sc, SPI_DMARDLR, 0);
1752e0a1eb9Skettenis HWRITE4(sc, SPI_IPR, 0);
1762e0a1eb9Skettenis HWRITE4(sc, SPI_IMR, 0);
1772e0a1eb9Skettenis HWRITE4(sc, SPI_ICR, SPI_ICR_MASK);
1782e0a1eb9Skettenis
1792e0a1eb9Skettenis rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname);
1802e0a1eb9Skettenis
1812e0a1eb9Skettenis sc->sc_tag.sc_cookie = sc;
1822e0a1eb9Skettenis sc->sc_tag.sc_config = rkspi_config;
1832e0a1eb9Skettenis sc->sc_tag.sc_transfer = rkspi_transfer;
1842e0a1eb9Skettenis sc->sc_tag.sc_acquire_bus = rkspi_acquire_bus;
1852e0a1eb9Skettenis sc->sc_tag.sc_release_bus = rkspi_release_bus;
1862e0a1eb9Skettenis
1872e0a1eb9Skettenis rkspi_scan(sc);
1882e0a1eb9Skettenis }
1892e0a1eb9Skettenis
1902e0a1eb9Skettenis int
rkspi_detach(struct device * self,int flags)1912e0a1eb9Skettenis rkspi_detach(struct device *self, int flags)
1922e0a1eb9Skettenis {
1932e0a1eb9Skettenis struct rkspi_softc *sc = (struct rkspi_softc *)self;
1942e0a1eb9Skettenis
1952e0a1eb9Skettenis HWRITE4(sc, SPI_ENR, 0);
1962e0a1eb9Skettenis HWRITE4(sc, SPI_IMR, 0);
1972e0a1eb9Skettenis bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
1982e0a1eb9Skettenis return 0;
1992e0a1eb9Skettenis }
2002e0a1eb9Skettenis
2012e0a1eb9Skettenis void
rkspi_config(void * cookie,struct spi_config * conf)2022e0a1eb9Skettenis rkspi_config(void *cookie, struct spi_config *conf)
2032e0a1eb9Skettenis {
2042e0a1eb9Skettenis struct rkspi_softc *sc = cookie;
2052e0a1eb9Skettenis uint32_t ctrlr0;
2062e0a1eb9Skettenis uint16_t div;
2072e0a1eb9Skettenis int cs;
2082e0a1eb9Skettenis
2092e0a1eb9Skettenis div = 2;
2102e0a1eb9Skettenis while ((sc->sc_spi_freq / div) > conf->sc_freq)
2112e0a1eb9Skettenis div++;
2122e0a1eb9Skettenis /* Clock divider needs to be even. */
2132e0a1eb9Skettenis if (div & 1)
2142e0a1eb9Skettenis div++;
2152e0a1eb9Skettenis
2162e0a1eb9Skettenis cs = conf->sc_cs;
2172e0a1eb9Skettenis if (cs >= 2) {
2182e0a1eb9Skettenis printf("%s: invalid chip-select (%d)\n", DEVNAME(sc), cs);
2192e0a1eb9Skettenis return;
2202e0a1eb9Skettenis }
2212e0a1eb9Skettenis sc->sc_cs = cs;
2222e0a1eb9Skettenis sc->sc_cs_delay = conf->sc_cs_delay;
2232e0a1eb9Skettenis
2242e0a1eb9Skettenis ctrlr0 = SPI_CTRLR0_BHT_8BIT | SPI_CTRLR0_SSD_ONE | SPI_CTRLR0_EM_BIG;
2252e0a1eb9Skettenis if (conf->sc_flags & SPI_CONFIG_CPHA)
2262e0a1eb9Skettenis ctrlr0 |= SPI_CTRLR0_SCPH;
2272e0a1eb9Skettenis if (conf->sc_flags & SPI_CONFIG_CPOL)
2282e0a1eb9Skettenis ctrlr0 |= SPI_CTRLR0_SCPOL;
2292e0a1eb9Skettenis switch (conf->sc_bpw) {
2302e0a1eb9Skettenis case 4:
2312e0a1eb9Skettenis ctrlr0 |= SPI_CTRLR0_DFS_4BIT;
2322e0a1eb9Skettenis break;
2332e0a1eb9Skettenis case 8:
2342e0a1eb9Skettenis ctrlr0 |= SPI_CTRLR0_DFS_8BIT;
2352e0a1eb9Skettenis break;
2362e0a1eb9Skettenis case 16:
2372e0a1eb9Skettenis ctrlr0 |= SPI_CTRLR0_DFS_16BIT;
2382e0a1eb9Skettenis break;
2392e0a1eb9Skettenis default:
2402e0a1eb9Skettenis printf("%s: invalid bits-per-word (%d)\n", DEVNAME(sc),
2412e0a1eb9Skettenis conf->sc_bpw);
2422e0a1eb9Skettenis return;
2432e0a1eb9Skettenis }
2442e0a1eb9Skettenis
2452e0a1eb9Skettenis HWRITE4(sc, SPI_ENR, 0);
2462e0a1eb9Skettenis HWRITE4(sc, SPI_SER, 0);
2472e0a1eb9Skettenis HWRITE4(sc, SPI_CTRLR0, ctrlr0);
2482e0a1eb9Skettenis HWRITE4(sc, SPI_BAUDR, div);
2492e0a1eb9Skettenis }
2502e0a1eb9Skettenis
2512e0a1eb9Skettenis int
rkspi_wait_state(struct rkspi_softc * sc,uint32_t mask,uint32_t value)2522e0a1eb9Skettenis rkspi_wait_state(struct rkspi_softc *sc, uint32_t mask, uint32_t value)
2532e0a1eb9Skettenis {
2542e0a1eb9Skettenis int timeout;
2552e0a1eb9Skettenis
2562e0a1eb9Skettenis for (timeout = 1000; timeout > 0; timeout--) {
2572e0a1eb9Skettenis if ((HREAD4(sc, SPI_SR) & mask) == value)
2582e0a1eb9Skettenis return 0;
2592e0a1eb9Skettenis delay(10);
2602e0a1eb9Skettenis }
2612e0a1eb9Skettenis
2622e0a1eb9Skettenis return ETIMEDOUT;
2632e0a1eb9Skettenis }
2642e0a1eb9Skettenis
2652e0a1eb9Skettenis int
rkspi_transfer(void * cookie,char * out,char * in,int len,int flags)2662e0a1eb9Skettenis rkspi_transfer(void *cookie, char *out, char *in, int len, int flags)
2672e0a1eb9Skettenis {
2682e0a1eb9Skettenis struct rkspi_softc *sc = cookie;
2692e0a1eb9Skettenis int i;
2702e0a1eb9Skettenis
2712e0a1eb9Skettenis sc->sc_ridx = sc->sc_widx = 0;
2722e0a1eb9Skettenis
2732e0a1eb9Skettenis /* drain input buffer */
2742e0a1eb9Skettenis while (!(HREAD4(sc, SPI_SR) & SPI_SR_RFE))
2752e0a1eb9Skettenis HREAD4(sc, SPI_RXDR);
2762e0a1eb9Skettenis
2772e0a1eb9Skettenis if (out)
2782e0a1eb9Skettenis HCLR4(sc, SPI_CTRLR0, SPI_CTRLR0_XFM_RO);
2792e0a1eb9Skettenis else
2802e0a1eb9Skettenis HSET4(sc, SPI_CTRLR0, SPI_CTRLR0_XFM_RO);
2812e0a1eb9Skettenis HWRITE4(sc, SPI_CTRLR1, len - 1);
2822e0a1eb9Skettenis
2832e0a1eb9Skettenis HSET4(sc, SPI_SER, SPI_SER_CS(sc->sc_cs));
2842e0a1eb9Skettenis delay(sc->sc_cs_delay);
2852e0a1eb9Skettenis
2862e0a1eb9Skettenis HWRITE4(sc, SPI_ENR, 1);
2872e0a1eb9Skettenis
2882e0a1eb9Skettenis while (sc->sc_ridx < len || sc->sc_widx < len) {
2892e0a1eb9Skettenis for (i = sc->sc_widx; i < len; i++) {
2902e0a1eb9Skettenis if (rkspi_wait_state(sc, SPI_SR_TFF, 0))
2912e0a1eb9Skettenis goto err;
2922e0a1eb9Skettenis if (out)
2932e0a1eb9Skettenis HWRITE4(sc, SPI_TXDR, out[i]);
2942e0a1eb9Skettenis sc->sc_widx++;
2952e0a1eb9Skettenis }
2962e0a1eb9Skettenis
2972e0a1eb9Skettenis for (i = sc->sc_ridx; i < sc->sc_widx; i++) {
2982e0a1eb9Skettenis if (rkspi_wait_state(sc, SPI_SR_RFE, 0))
2992e0a1eb9Skettenis goto err;
3002e0a1eb9Skettenis if (in)
3012e0a1eb9Skettenis in[i] = HREAD4(sc, SPI_RXDR);
3022e0a1eb9Skettenis else
3032e0a1eb9Skettenis HREAD4(sc, SPI_RXDR);
3042e0a1eb9Skettenis sc->sc_ridx++;
3052e0a1eb9Skettenis }
3062e0a1eb9Skettenis
3072e0a1eb9Skettenis if (rkspi_wait_state(sc, SPI_SR_BSF, 0))
3082e0a1eb9Skettenis goto err;
3092e0a1eb9Skettenis }
3102e0a1eb9Skettenis
3112e0a1eb9Skettenis HWRITE4(sc, SPI_ENR, 0);
3122e0a1eb9Skettenis
3132e0a1eb9Skettenis if (!ISSET(flags, SPI_KEEP_CS))
3142e0a1eb9Skettenis HCLR4(sc, SPI_SER, SPI_SER_CS(sc->sc_cs));
3152e0a1eb9Skettenis return 0;
3162e0a1eb9Skettenis
3172e0a1eb9Skettenis err:
3182e0a1eb9Skettenis HWRITE4(sc, SPI_ENR, 0);
3192e0a1eb9Skettenis
3202e0a1eb9Skettenis HCLR4(sc, SPI_SER, SPI_SER_CS(sc->sc_cs));
3212e0a1eb9Skettenis return ETIMEDOUT;
3222e0a1eb9Skettenis }
3232e0a1eb9Skettenis
3242e0a1eb9Skettenis int
rkspi_acquire_bus(void * cookie,int flags)3252e0a1eb9Skettenis rkspi_acquire_bus(void *cookie, int flags)
3262e0a1eb9Skettenis {
3272e0a1eb9Skettenis struct rkspi_softc *sc = cookie;
3282e0a1eb9Skettenis
3292e0a1eb9Skettenis rw_enter(&sc->sc_buslock, RW_WRITE);
3302e0a1eb9Skettenis return 0;
3312e0a1eb9Skettenis }
3322e0a1eb9Skettenis
3332e0a1eb9Skettenis void
rkspi_release_bus(void * cookie,int flags)3342e0a1eb9Skettenis rkspi_release_bus(void *cookie, int flags)
3352e0a1eb9Skettenis {
3362e0a1eb9Skettenis struct rkspi_softc *sc = cookie;
3372e0a1eb9Skettenis
3382e0a1eb9Skettenis rw_exit(&sc->sc_buslock);
3392e0a1eb9Skettenis }
3402e0a1eb9Skettenis
3412e0a1eb9Skettenis void
rkspi_scan(struct rkspi_softc * sc)3422e0a1eb9Skettenis rkspi_scan(struct rkspi_softc *sc)
3432e0a1eb9Skettenis {
3442e0a1eb9Skettenis struct spi_attach_args sa;
3452e0a1eb9Skettenis uint32_t reg[1];
3462e0a1eb9Skettenis char name[32];
3472e0a1eb9Skettenis int node;
3482e0a1eb9Skettenis
3492e0a1eb9Skettenis for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
3502e0a1eb9Skettenis memset(name, 0, sizeof(name));
3512e0a1eb9Skettenis memset(reg, 0, sizeof(reg));
3522e0a1eb9Skettenis
3532e0a1eb9Skettenis if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
3542e0a1eb9Skettenis continue;
3552e0a1eb9Skettenis if (name[0] == '\0')
3562e0a1eb9Skettenis continue;
3572e0a1eb9Skettenis
3582e0a1eb9Skettenis if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg))
3592e0a1eb9Skettenis continue;
3602e0a1eb9Skettenis
3612e0a1eb9Skettenis memset(&sa, 0, sizeof(sa));
3622e0a1eb9Skettenis sa.sa_tag = &sc->sc_tag;
3632e0a1eb9Skettenis sa.sa_name = name;
3642e0a1eb9Skettenis sa.sa_cookie = &node;
3652e0a1eb9Skettenis
3662e0a1eb9Skettenis config_found(&sc->sc_dev, &sa, NULL);
3672e0a1eb9Skettenis }
3682e0a1eb9Skettenis }
369