1/* $NetBSD: sun4i_spi.c,v 1.7 2021/01/27 03:10:20 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 2019 Tobias Nygren 5 * Copyright (c) 2018 Jonathan A. Kollasch 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__KERNEL_RCSID(0, "$NetBSD: sun4i_spi.c,v 1.7 2021/01/27 03:10:20 thorpej Exp $"); 32 33#include <sys/param.h> 34#include <sys/device.h> 35#include <sys/systm.h> 36#include <sys/bus.h> 37#include <sys/intr.h> 38#include <sys/kernel.h> 39#include <sys/bitops.h> 40#include <dev/spi/spivar.h> 41#include <arm/sunxi/sun4i_spireg.h> 42#include <dev/fdt/fdtvar.h> 43 44static const struct device_compatible_entry compat_data[] = { 45 { .compat = "allwinner,sun4i-a10-spi" }, 46 DEVICE_COMPAT_EOL 47}; 48 49struct sun4ispi_softc { 50 device_t sc_dev; 51 bus_space_tag_t sc_bst; 52 bus_space_handle_t sc_bsh; 53 void *sc_intrh; 54 struct spi_controller sc_spi; 55 SIMPLEQ_HEAD(,spi_transfer) sc_q; 56 struct spi_transfer *sc_transfer; 57 struct spi_chunk *sc_rchunk, *sc_wchunk; 58 uint32_t sc_CTL; 59 u_int sc_modclkrate; 60 volatile bool sc_running; 61}; 62 63#define SPIREG_READ(sc, reg) \ 64 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 65#define SPIREG_WRITE(sc, reg, val) \ 66 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 67 68static struct spi_controller * sun4i_spi_get_controller(device_t); 69static int sun4ispi_match(device_t, cfdata_t, void *); 70static void sun4ispi_attach(device_t, device_t, void *); 71 72static int sun4ispi_configure(void *, int, int, int); 73static int sun4ispi_transfer(void *, struct spi_transfer *); 74 75static void sun4ispi_txfifo_fill(struct sun4ispi_softc * const, size_t); 76static void sun4ispi_rxfifo_drain(struct sun4ispi_softc * const, size_t); 77static void sun4ispi_rxtx(struct sun4ispi_softc * const); 78static void sun4ispi_set_interrupt_mask(struct sun4ispi_softc * const); 79static void sun4ispi_start(struct sun4ispi_softc * const); 80static int sun4ispi_intr(void *); 81 82CFATTACH_DECL_NEW(sun4i_spi, sizeof(struct sun4ispi_softc), 83 sun4ispi_match, sun4ispi_attach, NULL, NULL); 84 85static const struct fdtbus_spi_controller_func sun4i_spi_funcs = { 86 .get_controller = sun4i_spi_get_controller 87}; 88 89static struct spi_controller * 90sun4i_spi_get_controller(device_t dev) 91{ 92 struct sun4ispi_softc * const sc = device_private(dev); 93 94 return &sc->sc_spi; 95} 96 97static int 98sun4ispi_match(device_t parent, cfdata_t cf, void *aux) 99{ 100 struct fdt_attach_args * const faa = aux; 101 102 return of_compatible_match(faa->faa_phandle, compat_data); 103} 104 105static void 106sun4ispi_attach(device_t parent, device_t self, void *aux) 107{ 108 struct sun4ispi_softc * const sc = device_private(self); 109 struct fdt_attach_args * const faa = aux; 110 const int phandle = faa->faa_phandle; 111 bus_addr_t addr; 112 bus_size_t size; 113 struct clk *clk, *modclk; 114 char intrstr[128]; 115 116 sc->sc_dev = self; 117 sc->sc_bst = faa->faa_bst; 118 SIMPLEQ_INIT(&sc->sc_q); 119 120 if ((clk = fdtbus_clock_get_index(phandle, 0)) == NULL 121 || clk_enable(clk) != 0) { 122 aprint_error(": couldn't enable clock\n"); 123 return; 124 } 125 126 if ((modclk = fdtbus_clock_get(phandle, "mod")) == NULL 127 || clk_set_rate(modclk, clk_get_rate(clk)) != 0 128 || clk_enable(modclk) != 0) { 129 aprint_error(": couldn't enable module clock\n"); 130 return; 131 } 132 sc->sc_modclkrate = clk_get_rate(modclk); 133 134 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0 135 || bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 136 aprint_error(": couldn't map registers\n"); 137 return; 138 } 139 140 SPIREG_WRITE(sc, SPI_CTL, SPI_CTL_SSPOL | SPI_CTL_RF_RST 141 | SPI_CTL_TF_RST | SPI_CTL_MODE); 142 SPIREG_WRITE(sc, SPI_DMACTL, 0); 143 SPIREG_WRITE(sc, SPI_WAIT, 0); 144 SPIREG_WRITE(sc, SPI_INTCTL, 0); 145 SPIREG_WRITE(sc, SPI_INT_STA, ~0); 146 147 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 148 aprint_error(": failed to decode interrupt\n"); 149 return; 150 } 151 152 sc->sc_intrh = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, 0, 153 sun4ispi_intr, sc, device_xname(self)); 154 if (sc->sc_intrh == NULL) { 155 aprint_error(": unable to establish interrupt\n"); 156 return; 157 } 158 159 aprint_naive("\n"); 160 aprint_normal(": SPI\n"); 161 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 162 163 sc->sc_spi.sct_cookie = sc; 164 sc->sc_spi.sct_configure = sun4ispi_configure; 165 sc->sc_spi.sct_transfer = sun4ispi_transfer; 166 (void) of_getprop_uint32(phandle, "num-cs", &sc->sc_spi.sct_nslaves); 167 fdtbus_register_spi_controller(self, phandle, &sun4i_spi_funcs); 168 (void) fdtbus_attach_spibus(self, phandle, spibus_print); 169} 170 171static int 172sun4ispi_configure(void *cookie, int slave, int mode, int speed) 173{ 174 struct sun4ispi_softc * const sc = cookie; 175 uint32_t ctl, cctl; 176 uint32_t minfreq, maxfreq; 177 178 minfreq = sc->sc_modclkrate >> 16; 179 maxfreq = sc->sc_modclkrate >> 1; 180 181 if (speed <= 0 || speed < minfreq || speed > maxfreq) 182 return EINVAL; 183 184 if (slave >= sc->sc_spi.sct_nslaves) 185 return EINVAL; 186 187 ctl = SPI_CTL_SDM | SPI_CTL_TP_EN | SPI_CTL_SSPOL | SPI_CTL_MODE | SPI_CTL_EN; 188 189 switch (mode) { 190 case SPI_MODE_0: 191 ctl |= 0; 192 break; 193 case SPI_MODE_1: 194 ctl |= SPI_CTL_PHA; 195 break; 196 case SPI_MODE_2: 197 ctl |= SPI_CTL_POL; 198 break; 199 case SPI_MODE_3: 200 ctl |= SPI_CTL_PHA | SPI_CTL_POL; 201 break; 202 default: 203 return EINVAL; 204 } 205 206 if (speed < sc->sc_modclkrate / 512) { 207 for (cctl = 0; cctl <= __SHIFTOUT_MASK(SPI_CCTL_CDR1); cctl++) { 208 if ((sc->sc_modclkrate / (1 << cctl)) <= speed) 209 goto cdr1_found; 210 } 211 return EINVAL; 212cdr1_found: 213 cctl = __SHIFTIN(cctl, SPI_CCTL_CDR1); 214 } else { 215 cctl = howmany(sc->sc_modclkrate, 2 * speed) - 1; 216 cctl = SPI_CCTL_DRS|__SHIFTIN(cctl, SPI_CCTL_CDR2); 217 } 218 219 device_printf(sc->sc_dev, "ctl 0x%x, cctl 0x%x, CLK %uHz, SCLK %uHz\n", 220 ctl, cctl, sc->sc_modclkrate, 221 (cctl & SPI_CCTL_DRS) 222 ? (sc->sc_modclkrate / (u_int)(2 * (__SHIFTOUT(cctl, SPI_CCTL_CDR2) + 1))) 223 : (sc->sc_modclkrate >> (__SHIFTOUT(cctl, SPI_CCTL_CDR1) + 1)) 224 ); 225 226 sc->sc_CTL = ctl; 227 SPIREG_WRITE(sc, SPI_CTL, (ctl | SPI_CTL_RF_RST | SPI_CTL_TF_RST) & ~SPI_CTL_EN); 228 SPIREG_WRITE(sc, SPI_CCTL, cctl); 229 SPIREG_WRITE(sc, SPI_CTL, ctl); 230 231 return 0; 232} 233 234static int 235sun4ispi_transfer(void *cookie, struct spi_transfer *st) 236{ 237 struct sun4ispi_softc * const sc = cookie; 238 int s; 239 240 s = splbio(); 241 spi_transq_enqueue(&sc->sc_q, st); 242 if (sc->sc_running == false) { 243 sun4ispi_start(sc); 244 } 245 splx(s); 246 247 return 0; 248} 249 250static void 251sun4ispi_txfifo_fill(struct sun4ispi_softc * const sc, size_t maxlen) 252{ 253 struct spi_chunk *chunk = sc->sc_wchunk; 254 size_t len; 255 uint8_t b; 256 257 if (chunk == NULL) 258 return; 259 260 len = MIN(maxlen, chunk->chunk_wresid); 261 chunk->chunk_wresid -= len; 262 while (len--) { 263 if (chunk->chunk_wptr) { 264 b = *chunk->chunk_wptr++; 265 } else { 266 b = 0; 267 } 268 bus_space_write_1(sc->sc_bst, sc->sc_bsh, SPI_TXDATA, b); 269 } 270 if (sc->sc_wchunk->chunk_wresid == 0) { 271 sc->sc_wchunk = sc->sc_wchunk->chunk_next; 272 } 273} 274 275static void 276sun4ispi_rxfifo_drain(struct sun4ispi_softc * const sc, size_t maxlen) 277{ 278 struct spi_chunk *chunk = sc->sc_rchunk; 279 size_t len; 280 uint8_t b; 281 282 if (chunk == NULL) 283 return; 284 285 len = MIN(maxlen, chunk->chunk_rresid); 286 chunk->chunk_rresid -= len; 287 288 while (len--) { 289 b = bus_space_read_1(sc->sc_bst, sc->sc_bsh, SPI_RXDATA); 290 if (chunk->chunk_rptr) { 291 *chunk->chunk_rptr++ = b; 292 } 293 } 294 if (sc->sc_rchunk->chunk_rresid == 0) { 295 sc->sc_rchunk = sc->sc_rchunk->chunk_next; 296 } 297} 298 299static void 300sun4ispi_rxtx(struct sun4ispi_softc * const sc) 301{ 302 bool again; 303 size_t rxavail, txavail; 304 uint32_t fsr; 305 306 /* service both FIFOs until no more progress can be made */ 307 again = true; 308 while (again) { 309 again = false; 310 fsr = SPIREG_READ(sc, SPI_FIFO_STA); 311 rxavail = __SHIFTOUT(fsr, SPI_FIFO_STA_RF_CNT); 312 txavail = 64 - __SHIFTOUT(fsr, SPI_FIFO_STA_TF_CNT); 313 if (rxavail > 0) { 314 KASSERT(sc->sc_rchunk != NULL); 315 sun4ispi_rxfifo_drain(sc, rxavail); 316 again = true; 317 } 318 if (txavail > 0 && sc->sc_wchunk != NULL) { 319 sun4ispi_txfifo_fill(sc, txavail); 320 again = true; 321 } 322 } 323} 324 325static void 326sun4ispi_set_interrupt_mask(struct sun4ispi_softc * const sc) 327{ 328 uint32_t intctl; 329 330 intctl = SPI_INTCTL_TX_INT_EN; 331 intctl |= SPI_INTCTL_RF_OF_INT_EN; 332 intctl |= SPI_INTCTL_TF_UR_INT_EN; 333 334 if (sc->sc_rchunk) { 335 if (sc->sc_rchunk->chunk_rresid >= 32) { 336 intctl |= SPI_INTCTL_RF_HALF_FU_INT_EN; 337 } else { 338 intctl |= SPI_INTCTL_RF_RDY_INT_EN; 339 } 340 } 341 if (sc->sc_wchunk) { 342 intctl |= SPI_INTCTL_TF_HALF_EMP_INT_EN; 343 } 344 SPIREG_WRITE(sc, SPI_INTCTL, intctl); 345} 346 347static void 348sun4ispi_start(struct sun4ispi_softc * const sc) 349{ 350 struct spi_transfer *st; 351 uint32_t ctl; 352 struct spi_chunk *chunk; 353 size_t burstcount; 354 355 while ((st = spi_transq_first(&sc->sc_q)) != NULL) { 356 357 spi_transq_dequeue(&sc->sc_q); 358 359 KASSERT(sc->sc_transfer == NULL); 360 sc->sc_transfer = st; 361 sc->sc_rchunk = sc->sc_wchunk = st->st_chunks; 362 sc->sc_running = true; 363 364 burstcount = 0; 365 for (chunk = st->st_chunks; chunk; chunk = chunk->chunk_next) { 366 burstcount += chunk->chunk_count; 367 } 368 KASSERT(burstcount <= SPI_BC_BC); 369 SPIREG_WRITE(sc, SPI_BC, __SHIFTIN(burstcount, SPI_BC_BC)); 370 SPIREG_WRITE(sc, SPI_TC, __SHIFTIN(burstcount, SPI_TC_WTC)); 371 372 sun4ispi_rxtx(sc); 373 sun4ispi_set_interrupt_mask(sc); 374 375 KASSERT(st->st_slave < sc->sc_spi.sct_nslaves); 376 ctl = sc->sc_CTL | __SHIFTIN(st->st_slave, SPI_CTL_SS) | SPI_CTL_XCH; 377 SPIREG_WRITE(sc, SPI_CTL, ctl); 378 379 if (!cold) 380 return; 381 382 for (;;) { 383 (void) sun4ispi_intr(sc); 384 if (ISSET(st->st_flags, SPI_F_DONE)) 385 break; 386 } 387 } 388 sc->sc_running = false; 389} 390 391static int 392sun4ispi_intr(void *cookie) 393{ 394 struct sun4ispi_softc * const sc = cookie; 395 struct spi_transfer *st; 396 uint32_t isr; 397 398 isr = SPIREG_READ(sc, SPI_INT_STA); 399 if (!isr) 400 return 0; 401 402 if (ISSET(isr, SPI_INT_STA_RO)) { 403 device_printf(sc->sc_dev, "RXFIFO overflow\n"); 404 } 405 if (ISSET(isr, SPI_INT_STA_TU)) { 406 device_printf(sc->sc_dev, "TXFIFO underrun\n"); 407 } 408 409 sun4ispi_rxtx(sc); 410 411 if (ISSET(isr, SPI_INT_STA_TC)) { 412 SPIREG_WRITE(sc, SPI_INTCTL, 0); 413 KASSERT(sc->sc_rchunk == NULL); 414 KASSERT(sc->sc_wchunk == NULL); 415 st = sc->sc_transfer; 416 sc->sc_transfer = NULL; 417 KASSERT(st != NULL); 418 spi_done(st, 0); 419 sc->sc_running = false; 420 } else { 421 sun4ispi_set_interrupt_mask(sc); 422 } 423 SPIREG_WRITE(sc, SPI_INT_STA, isr); 424 425 return 1; 426} 427