1 /* $NetBSD: bcm2835_spi.c,v 1.6 2019/08/13 17:03:10 tnn 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.6 2019/08/13 17:03:10 tnn 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 SIMPLEQ_HEAD(,spi_transfer) sc_q; 57 struct spi_transfer *sc_transfer; 58 struct spi_chunk *sc_wchunk; 59 struct spi_chunk *sc_rchunk; 60 uint32_t sc_CS; 61 volatile bool sc_running; 62 }; 63 64 static int bcmspi_match(device_t, cfdata_t, void *); 65 static void bcmspi_attach(device_t, device_t, void *); 66 67 static int bcmspi_configure(void *, int, int, int); 68 static int bcmspi_transfer(void *, struct spi_transfer *); 69 70 static void bcmspi_start(struct bcmspi_softc * const); 71 static int bcmspi_intr(void *); 72 73 static void bcmspi_send(struct bcmspi_softc * const); 74 static void bcmspi_recv(struct bcmspi_softc * const); 75 76 CFATTACH_DECL_NEW(bcmspi, sizeof(struct bcmspi_softc), 77 bcmspi_match, bcmspi_attach, NULL, NULL); 78 79 static int 80 bcmspi_match(device_t parent, cfdata_t cf, void *aux) 81 { 82 const char * const compatible[] = { 83 "brcm,bcm2835-spi", 84 NULL 85 }; 86 struct fdt_attach_args * const faa = aux; 87 88 return of_match_compatible(faa->faa_phandle, compatible); 89 } 90 91 static void 92 bcmspi_attach(device_t parent, device_t self, void *aux) 93 { 94 struct bcmspi_softc * const sc = device_private(self); 95 struct fdt_attach_args * const faa = aux; 96 struct spibus_attach_args sba; 97 98 aprint_naive("\n"); 99 aprint_normal(": SPI\n"); 100 101 sc->sc_dev = self; 102 sc->sc_iot = faa->faa_bst; 103 SIMPLEQ_INIT(&sc->sc_q); 104 105 const int phandle = faa->faa_phandle; 106 bus_addr_t addr; 107 bus_size_t size; 108 109 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 110 aprint_error(": missing 'reg' property\n"); 111 return; 112 } 113 114 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) { 115 aprint_error_dev(sc->sc_dev, "unable to map device\n"); 116 return; 117 } 118 119 char intrstr[128]; 120 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 121 aprint_error(": failed to decode interrupt\n"); 122 return; 123 } 124 125 sc->sc_intrh = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, 126 bcmspi_intr, sc); 127 if (sc->sc_intrh == NULL) { 128 aprint_error_dev(sc->sc_dev, "unable to establish interrupt\n"); 129 return; 130 } 131 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 132 133 sc->sc_spi.sct_cookie = sc; 134 sc->sc_spi.sct_configure = bcmspi_configure; 135 sc->sc_spi.sct_transfer = bcmspi_transfer; 136 sc->sc_spi.sct_nslaves = 3; 137 138 memset(&sba, 0, sizeof(sba)); 139 sba.sba_controller = &sc->sc_spi; 140 141 (void) config_found_ia(self, "spibus", &sba, spibus_print); 142 } 143 144 static int 145 bcmspi_configure(void *cookie, int slave, int mode, int speed) 146 { 147 struct bcmspi_softc * const sc = cookie; 148 uint32_t cs, clk; 149 150 cs = SPI_CS_INTR | SPI_CS_INTD; 151 152 if (slave > 2) 153 return EINVAL; 154 155 if (speed <= 0) 156 return EINVAL; 157 158 switch (mode) { 159 case SPI_MODE_0: 160 cs |= 0; 161 break; 162 case SPI_MODE_1: 163 cs |= SPI_CS_CPHA; 164 break; 165 case SPI_MODE_2: 166 cs |= SPI_CS_CPOL; 167 break; 168 case SPI_MODE_3: 169 cs |= SPI_CS_CPHA|SPI_CS_CPOL; 170 break; 171 default: 172 return EINVAL; 173 } 174 175 sc->sc_CS = cs; 176 177 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CS, cs); 178 179 clk = 2 * 250000000 / speed; /* XXX 250MHz */ 180 clk = (clk / 2) + (clk & 1); 181 clk = roundup(clk, 2); 182 clk = __SHIFTIN(clk, SPI_CLK_CDIV); 183 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CLK, clk); 184 185 return 0; 186 } 187 188 static int 189 bcmspi_transfer(void *cookie, struct spi_transfer *st) 190 { 191 struct bcmspi_softc * const sc = cookie; 192 int s; 193 194 s = splbio(); 195 spi_transq_enqueue(&sc->sc_q, st); 196 if (sc->sc_running == false) { 197 bcmspi_start(sc); 198 } 199 splx(s); 200 return 0; 201 } 202 203 static void 204 bcmspi_start(struct bcmspi_softc * const sc) 205 { 206 struct spi_transfer *st; 207 uint32_t cs; 208 209 while ((st = spi_transq_first(&sc->sc_q)) != NULL) { 210 211 spi_transq_dequeue(&sc->sc_q); 212 213 KASSERT(sc->sc_transfer == NULL); 214 sc->sc_transfer = st; 215 sc->sc_rchunk = sc->sc_wchunk = st->st_chunks; 216 217 cs = sc->sc_CS; 218 cs |= SPI_CS_TA; 219 cs |= SPI_CS_CLEAR_TX; 220 cs |= SPI_CS_CLEAR_RX; 221 KASSERT(st->st_slave <= 2); 222 cs |= __SHIFTIN(st->st_slave, SPI_CS_CS); 223 sc->sc_running = true; 224 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CS, cs); 225 226 if (!cold) 227 return; 228 229 int s = splbio(); 230 for (;;) { 231 bcmspi_intr(sc); 232 if (ISSET(st->st_flags, SPI_F_DONE)) 233 break; 234 } 235 splx(s); 236 } 237 238 sc->sc_running = false; 239 } 240 241 static void 242 bcmspi_send(struct bcmspi_softc * const sc) 243 { 244 uint32_t fd; 245 uint32_t cs; 246 struct spi_chunk *chunk; 247 248 while ((chunk = sc->sc_wchunk) != NULL) { 249 while (chunk->chunk_wresid) { 250 cs = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_CS); 251 if ((cs & SPI_CS_TXD) == 0) 252 return; 253 if (chunk->chunk_wptr) { 254 fd = *chunk->chunk_wptr++; 255 } else { 256 fd = '\0'; 257 } 258 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_FIFO, fd); 259 chunk->chunk_wresid--; 260 } 261 sc->sc_wchunk = sc->sc_wchunk->chunk_next; 262 } 263 } 264 265 static void 266 bcmspi_recv(struct bcmspi_softc * const sc) 267 { 268 uint32_t fd; 269 uint32_t cs; 270 struct spi_chunk *chunk; 271 272 while ((chunk = sc->sc_rchunk) != NULL) { 273 while (chunk->chunk_rresid) { 274 cs = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_CS); 275 if ((cs & SPI_CS_RXD) == 0) 276 return; 277 fd = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_FIFO); 278 if (chunk->chunk_rptr) { 279 *chunk->chunk_rptr++ = fd & 0xff; 280 } 281 chunk->chunk_rresid--; 282 } 283 sc->sc_rchunk = sc->sc_rchunk->chunk_next; 284 } 285 } 286 287 static int 288 bcmspi_intr(void *cookie) 289 { 290 struct bcmspi_softc * const sc = cookie; 291 struct spi_transfer *st; 292 uint32_t cs; 293 294 cs = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_CS); 295 if (ISSET(cs, SPI_CS_DONE)) { 296 if (sc->sc_wchunk != NULL) { 297 bcmspi_send(sc); 298 goto end; 299 } else { 300 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SPI_CS, 301 sc->sc_CS); 302 bcmspi_recv(sc); 303 sc->sc_rchunk = sc->sc_wchunk = NULL; 304 st = sc->sc_transfer; 305 sc->sc_transfer = NULL; 306 KASSERT(st != NULL); 307 spi_done(st, 0); 308 sc->sc_running = false; 309 goto end; 310 } 311 } else if (ISSET(cs, SPI_CS_RXR)) { 312 bcmspi_recv(sc); 313 bcmspi_send(sc); 314 } 315 316 end: 317 return ISSET(cs, SPI_CS_DONE|SPI_CS_RXR); 318 } 319