1 /* $NetBSD: bcm2835_spi.c,v 1.10 2021/04/24 23:36:26 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Jonathan A. Kollasch 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: bcm2835_spi.c,v 1.10 2021/04/24 23:36:26 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/systm.h> 35 #include <sys/mutex.h> 36 #include <sys/bus.h> 37 #include <sys/intr.h> 38 #include <sys/kernel.h> 39 40 #include <sys/bitops.h> 41 #include <dev/spi/spivar.h> 42 43 #include <arm/broadcom/bcm2835reg.h> 44 #include <arm/broadcom/bcm2835_spireg.h> 45 46 #include <dev/fdt/fdtvar.h> 47 48 #include <arm/fdt/arm_fdtvar.h> 49 50 struct bcmspi_softc { 51 device_t sc_dev; 52 bus_space_tag_t sc_iot; 53 bus_space_handle_t sc_ioh; 54 void *sc_intrh; 55 struct spi_controller sc_spi; 56 kmutex_t sc_mutex; 57 SIMPLEQ_HEAD(,spi_transfer) sc_q; 58 struct spi_transfer *sc_transfer; 59 struct spi_chunk *sc_wchunk; 60 struct spi_chunk *sc_rchunk; 61 uint32_t sc_CS; 62 volatile bool sc_running; 63 }; 64 65 static int bcmspi_match(device_t, cfdata_t, void *); 66 static void bcmspi_attach(device_t, device_t, void *); 67 68 static int bcmspi_configure(void *, int, int, int); 69 static int bcmspi_transfer(void *, struct spi_transfer *); 70 71 static void bcmspi_start(struct bcmspi_softc * const); 72 static int bcmspi_intr(void *); 73 74 static void bcmspi_send(struct bcmspi_softc * const); 75 static void bcmspi_recv(struct bcmspi_softc * const); 76 77 CFATTACH_DECL_NEW(bcmspi, sizeof(struct bcmspi_softc), 78 bcmspi_match, bcmspi_attach, NULL, NULL); 79 80 static const struct device_compatible_entry compat_data[] = { 81 { .compat = "brcm,bcm2835-spi" }, 82 DEVICE_COMPAT_EOL 83 }; 84 85 static int 86 bcmspi_match(device_t parent, cfdata_t cf, void *aux) 87 { 88 struct fdt_attach_args * const faa = aux; 89 90 return of_compatible_match(faa->faa_phandle, compat_data); 91 } 92 93 static void 94 bcmspi_attach(device_t parent, device_t self, void *aux) 95 { 96 struct bcmspi_softc * const sc = device_private(self); 97 struct fdt_attach_args * const faa = aux; 98 struct spibus_attach_args sba; 99 100 aprint_naive("\n"); 101 aprint_normal(": SPI\n"); 102 103 sc->sc_dev = self; 104 sc->sc_iot = faa->faa_bst; 105 SIMPLEQ_INIT(&sc->sc_q); 106 107 const int phandle = faa->faa_phandle; 108 bus_addr_t addr; 109 bus_size_t size; 110 111 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 112 aprint_error(": missing 'reg' property\n"); 113 return; 114 } 115 116 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) { 117 aprint_error_dev(sc->sc_dev, "unable to map device\n"); 118 return; 119 } 120 121 char intrstr[128]; 122 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 123 aprint_error(": failed to decode interrupt\n"); 124 return; 125 } 126 127 sc->sc_intrh = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, 0, 128 bcmspi_intr, sc, device_xname(self)); 129 if (sc->sc_intrh == NULL) { 130 aprint_error_dev(sc->sc_dev, "unable to establish interrupt\n"); 131 return; 132 } 133 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 134 135 sc->sc_spi.sct_cookie = sc; 136 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); 137 sc->sc_spi.sct_configure = bcmspi_configure; 138 sc->sc_spi.sct_transfer = bcmspi_transfer; 139 sc->sc_spi.sct_nslaves = 3; 140 141 memset(&sba, 0, sizeof(sba)); 142 sba.sba_controller = &sc->sc_spi; 143 144 config_found(self, &sba, spibus_print, CFARG_EOL); 145 } 146 147 static int 148 bcmspi_configure(void *cookie, int slave, int mode, int speed) 149 { 150 struct bcmspi_softc * const sc = cookie; 151 uint32_t cs, clk; 152 153 cs = SPI_CS_INTR | SPI_CS_INTD; 154 155 if (slave > 2) 156 return EINVAL; 157 158 if (speed <= 0) 159 return EINVAL; 160 161 switch (mode) { 162 case SPI_MODE_0: 163 cs |= 0; 164 break; 165 case SPI_MODE_1: 166 cs |= SPI_CS_CPHA; 167 break; 168 case SPI_MODE_2: 169 cs |= SPI_CS_CPOL; 170 break; 171 case SPI_MODE_3: 172 cs |= SPI_CS_CPHA|SPI_CS_CPOL; 173 break; 174 default: 175 return EINVAL; 176 } 177 178 sc->sc_CS = cs; 179 180 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CS, cs); 181 182 clk = 2 * 250000000 / speed; /* XXX 250MHz */ 183 clk = (clk / 2) + (clk & 1); 184 clk = roundup(clk, 2); 185 clk = __SHIFTIN(clk, SPI_CLK_CDIV); 186 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CLK, clk); 187 188 return 0; 189 } 190 191 static int 192 bcmspi_transfer(void *cookie, struct spi_transfer *st) 193 { 194 struct bcmspi_softc * const sc = cookie; 195 196 mutex_enter(&sc->sc_mutex); 197 spi_transq_enqueue(&sc->sc_q, st); 198 if (sc->sc_running == false) { 199 bcmspi_start(sc); 200 } 201 mutex_exit(&sc->sc_mutex); 202 return 0; 203 } 204 205 static void 206 bcmspi_start(struct bcmspi_softc * const sc) 207 { 208 struct spi_transfer *st; 209 uint32_t cs; 210 211 while ((st = spi_transq_first(&sc->sc_q)) != NULL) { 212 213 spi_transq_dequeue(&sc->sc_q); 214 215 KASSERT(sc->sc_transfer == NULL); 216 sc->sc_transfer = st; 217 sc->sc_rchunk = sc->sc_wchunk = st->st_chunks; 218 219 cs = sc->sc_CS; 220 cs |= SPI_CS_TA; 221 cs |= SPI_CS_CLEAR_TX; 222 cs |= SPI_CS_CLEAR_RX; 223 KASSERT(st->st_slave <= 2); 224 cs |= __SHIFTIN(st->st_slave, SPI_CS_CS); 225 sc->sc_running = true; 226 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CS, cs); 227 228 if (!cold) 229 return; 230 231 for (;;) { 232 mutex_exit(&sc->sc_mutex); 233 bcmspi_intr(sc); 234 mutex_enter(&sc->sc_mutex); 235 if (ISSET(st->st_flags, SPI_F_DONE)) 236 break; 237 } 238 } 239 240 sc->sc_running = false; 241 } 242 243 static void 244 bcmspi_send(struct bcmspi_softc * const sc) 245 { 246 uint32_t fd; 247 uint32_t cs; 248 struct spi_chunk *chunk; 249 250 while ((chunk = sc->sc_wchunk) != NULL) { 251 while (chunk->chunk_wresid) { 252 cs = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_CS); 253 if ((cs & SPI_CS_TXD) == 0) 254 return; 255 if (chunk->chunk_wptr) { 256 fd = *chunk->chunk_wptr++; 257 } else { 258 fd = '\0'; 259 } 260 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_FIFO, fd); 261 chunk->chunk_wresid--; 262 } 263 sc->sc_wchunk = sc->sc_wchunk->chunk_next; 264 } 265 } 266 267 static void 268 bcmspi_recv(struct bcmspi_softc * const sc) 269 { 270 uint32_t fd; 271 uint32_t cs; 272 struct spi_chunk *chunk; 273 274 while ((chunk = sc->sc_rchunk) != NULL) { 275 while (chunk->chunk_rresid) { 276 cs = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_CS); 277 if ((cs & SPI_CS_RXD) == 0) 278 return; 279 fd = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_FIFO); 280 if (chunk->chunk_rptr) { 281 *chunk->chunk_rptr++ = fd & 0xff; 282 } 283 chunk->chunk_rresid--; 284 } 285 sc->sc_rchunk = sc->sc_rchunk->chunk_next; 286 } 287 } 288 289 static int 290 bcmspi_intr(void *cookie) 291 { 292 struct bcmspi_softc * const sc = cookie; 293 struct spi_transfer *st; 294 uint32_t cs; 295 296 mutex_enter(&sc->sc_mutex); 297 cs = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_CS); 298 if (ISSET(cs, SPI_CS_DONE)) { 299 if (sc->sc_wchunk != NULL) { 300 bcmspi_send(sc); 301 goto end; 302 } else { 303 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CS, 304 sc->sc_CS); 305 bcmspi_recv(sc); 306 sc->sc_rchunk = sc->sc_wchunk = NULL; 307 st = sc->sc_transfer; 308 sc->sc_transfer = NULL; 309 KASSERT(st != NULL); 310 spi_done(st, 0); 311 sc->sc_running = false; 312 goto end; 313 } 314 } else if (ISSET(cs, SPI_CS_RXR)) { 315 bcmspi_recv(sc); 316 bcmspi_send(sc); 317 } 318 319 end: 320 mutex_exit(&sc->sc_mutex); 321 return ISSET(cs, SPI_CS_DONE|SPI_CS_RXR); 322 } 323