1 /* $NetBSD: sun6i_spi.c,v 1.9 2021/04/24 23:36:28 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: sun6i_spi.c,v 1.9 2021/04/24 23:36:28 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 40 #include <sys/bitops.h> 41 #include <dev/spi/spivar.h> 42 43 #include <arm/sunxi/sun6i_spireg.h> 44 45 #include <dev/fdt/fdtvar.h> 46 47 #include <arm/fdt/arm_fdtvar.h> 48 49 #define SPI_IER_DEFAULT (SPI_IER_TC_INT_EN | SPI_IER_TF_UDR_INT_EN | \ 50 SPI_IER_TF_OVF_INT_EN | SPI_IER_RF_UDR_INT_EN | SPI_IER_RF_OVF_INT_EN) 51 52 struct sun6ispi_softc { 53 device_t sc_dev; 54 bus_space_tag_t sc_iot; 55 bus_space_handle_t sc_ioh; 56 void *sc_intrh; 57 struct spi_controller sc_spi; 58 SIMPLEQ_HEAD(,spi_transfer) sc_q; 59 struct spi_transfer *sc_transfer; 60 struct spi_chunk *sc_wchunk; 61 struct spi_chunk *sc_rchunk; 62 uint32_t sc_TCR; 63 u_int sc_modclkrate; 64 volatile bool sc_running; 65 }; 66 67 #define SPIREG_READ(sc, reg) \ 68 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) 69 #define SPIREG_WRITE(sc, reg, val) \ 70 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 71 72 static int sun6ispi_match(device_t, cfdata_t, void *); 73 static void sun6ispi_attach(device_t, device_t, void *); 74 75 static int sun6ispi_configure(void *, int, int, int); 76 static int sun6ispi_transfer(void *, struct spi_transfer *); 77 78 static void sun6ispi_start(struct sun6ispi_softc * const); 79 static int sun6ispi_intr(void *); 80 81 static void sun6ispi_send(struct sun6ispi_softc * const); 82 static void sun6ispi_recv(struct sun6ispi_softc * const); 83 84 CFATTACH_DECL_NEW(sun6i_spi, sizeof(struct sun6ispi_softc), 85 sun6ispi_match, sun6ispi_attach, NULL, NULL); 86 87 static const struct device_compatible_entry compat_data[] = { 88 { .compat = "allwinner,sun8i-h3-spi" }, 89 DEVICE_COMPAT_EOL 90 }; 91 92 static int 93 sun6ispi_match(device_t parent, cfdata_t cf, void *aux) 94 { 95 struct fdt_attach_args * const faa = aux; 96 97 return of_compatible_match(faa->faa_phandle, compat_data); 98 } 99 100 static void 101 sun6ispi_attach(device_t parent, device_t self, void *aux) 102 { 103 struct sun6ispi_softc * const sc = device_private(self); 104 struct fdt_attach_args * const faa = aux; 105 struct spibus_attach_args sba; 106 const int phandle = faa->faa_phandle; 107 bus_addr_t addr; 108 bus_size_t size; 109 struct fdtbus_reset *rst; 110 struct clk *clk, *modclk; 111 uint32_t gcr, isr; 112 char intrstr[128]; 113 114 sc->sc_dev = self; 115 sc->sc_iot = faa->faa_bst; 116 SIMPLEQ_INIT(&sc->sc_q); 117 118 if ((clk = fdtbus_clock_get_index(phandle, 0)) == NULL 119 || clk_enable(clk) != 0) { 120 aprint_error(": couldn't enable clock\n"); 121 return; 122 } 123 124 /* 200MHz max on H3,H5 */ 125 if ((modclk = fdtbus_clock_get(phandle, "mod")) == NULL 126 || clk_set_rate(modclk, 200000000) != 0 127 || clk_enable(modclk) != 0) { 128 aprint_error(": couldn't enable module clock\n"); 129 return; 130 } 131 sc->sc_modclkrate = clk_get_rate(modclk); 132 133 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0 134 || bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) { 135 aprint_error(": couldn't map registers\n"); 136 return; 137 } 138 139 if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL) 140 if (fdtbus_reset_deassert(rst) != 0) { 141 aprint_error(": couldn't de-assert reset\n"); 142 return; 143 } 144 145 isr = SPIREG_READ(sc, SPI_INT_STA); 146 SPIREG_WRITE(sc, SPI_INT_STA, isr); 147 148 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 149 aprint_error(": failed to decode interrupt\n"); 150 return; 151 } 152 153 sc->sc_intrh = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, 0, 154 sun6ispi_intr, sc, device_xname(self)); 155 if (sc->sc_intrh == NULL) { 156 aprint_error(": unable to establish interrupt\n"); 157 return; 158 } 159 160 aprint_naive("\n"); 161 aprint_normal(": SPI\n"); 162 163 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 164 165 gcr = SPI_GCR_SRST; 166 SPIREG_WRITE(sc, SPI_GCR, gcr); 167 for (u_int i = 0; ; i++) { 168 if (i >= 1000000) { 169 aprint_error_dev(self, "reset timeout\n"); 170 return; 171 } 172 gcr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SPI_GCR); 173 if ((gcr & SPI_GCR_SRST) == 0) 174 break; 175 else 176 DELAY(1); 177 } 178 gcr = SPI_GCR_TP_EN | SPI_GCR_MODE | SPI_GCR_EN; 179 SPIREG_WRITE(sc, SPI_GCR, gcr); 180 181 SPIREG_WRITE(sc, SPI_IER, SPI_IER_DEFAULT); 182 183 sc->sc_spi.sct_cookie = sc; 184 sc->sc_spi.sct_configure = sun6ispi_configure; 185 sc->sc_spi.sct_transfer = sun6ispi_transfer; 186 sc->sc_spi.sct_nslaves = 4; 187 188 memset(&sba, 0, sizeof(sba)); 189 sba.sba_controller = &sc->sc_spi; 190 191 (void) config_found(self, &sba, spibus_print, CFARG_EOL); 192 } 193 194 static int 195 sun6ispi_configure(void *cookie, int slave, int mode, int speed) 196 { 197 struct sun6ispi_softc * const sc = cookie; 198 uint32_t tcr, cctl; 199 uint32_t minfreq, maxfreq; 200 201 minfreq = sc->sc_modclkrate >> 16; 202 maxfreq = sc->sc_modclkrate >> 1; 203 204 if (speed <= 0 || speed < minfreq || speed > maxfreq) 205 return EINVAL; 206 207 if (slave >= sc->sc_spi.sct_nslaves) 208 return EINVAL; 209 210 tcr = SPI_TCR_SS_LEVEL | SPI_TCR_SPOL; 211 212 switch (mode) { 213 case SPI_MODE_0: 214 tcr |= 0; 215 break; 216 case SPI_MODE_1: 217 tcr |= SPI_TCR_CPHA; 218 break; 219 case SPI_MODE_2: 220 tcr |= SPI_TCR_CPOL; 221 break; 222 case SPI_MODE_3: 223 tcr |= SPI_TCR_CPHA|SPI_TCR_CPOL; 224 break; 225 default: 226 return EINVAL; 227 } 228 229 sc->sc_TCR = tcr; 230 231 if (speed < sc->sc_modclkrate / 512) { 232 for (cctl = 0; cctl <= __SHIFTOUT_MASK(SPI_CCTL_CDR1); cctl++) { 233 if ((sc->sc_modclkrate / (1<<cctl)) <= speed) 234 goto cdr1_found; 235 } 236 return EINVAL; 237 cdr1_found: 238 cctl = __SHIFTIN(cctl, SPI_CCTL_CDR1); 239 } else { 240 cctl = howmany(sc->sc_modclkrate, 2 * speed) - 1; 241 cctl = SPI_CCTL_DRS|__SHIFTIN(cctl, SPI_CCTL_CDR2); 242 } 243 244 device_printf(sc->sc_dev, "tcr 0x%x, cctl 0x%x, CLK %uHz, SCLK %uHz\n", 245 tcr, cctl, sc->sc_modclkrate, 246 (cctl & SPI_CCTL_DRS) 247 ? (sc->sc_modclkrate / (u_int)(2 * (__SHIFTOUT(cctl, SPI_CCTL_CDR2) + 1))) 248 : (sc->sc_modclkrate >> (__SHIFTOUT(cctl, SPI_CCTL_CDR1) + 1)) 249 ); 250 251 SPIREG_WRITE(sc, SPI_CCTL, cctl); 252 253 return 0; 254 } 255 256 static int 257 sun6ispi_transfer(void *cookie, struct spi_transfer *st) 258 { 259 struct sun6ispi_softc * const sc = cookie; 260 int s; 261 262 s = splbio(); 263 spi_transq_enqueue(&sc->sc_q, st); 264 if (sc->sc_running == false) { 265 sun6ispi_start(sc); 266 } 267 splx(s); 268 return 0; 269 } 270 271 static void 272 sun6ispi_start(struct sun6ispi_softc * const sc) 273 { 274 struct spi_transfer *st; 275 uint32_t isr, tcr; 276 struct spi_chunk *chunk; 277 size_t burstcount; 278 279 while ((st = spi_transq_first(&sc->sc_q)) != NULL) { 280 281 spi_transq_dequeue(&sc->sc_q); 282 283 KASSERT(sc->sc_transfer == NULL); 284 sc->sc_transfer = st; 285 sc->sc_rchunk = sc->sc_wchunk = st->st_chunks; 286 sc->sc_running = true; 287 288 isr = SPIREG_READ(sc, SPI_INT_STA); 289 SPIREG_WRITE(sc, SPI_INT_STA, isr); 290 291 burstcount = 0; 292 for (chunk = st->st_chunks; chunk; chunk = chunk->chunk_next) { 293 burstcount += chunk->chunk_count; 294 } 295 KASSERT(burstcount <= SPI_BC_MBC); 296 SPIREG_WRITE(sc, SPI_BC, __SHIFTIN(burstcount, SPI_BC_MBC)); 297 SPIREG_WRITE(sc, SPI_TC, __SHIFTIN(burstcount, SPI_TC_MWTC)); 298 SPIREG_WRITE(sc, SPI_BCC, __SHIFTIN(burstcount, SPI_BCC_STC)); 299 300 KASSERT(st->st_slave <= 3); 301 tcr = sc->sc_TCR | __SHIFTIN(st->st_slave, SPI_TCR_SS_SEL); 302 303 sun6ispi_send(sc); 304 305 const uint32_t ier = SPI_IER_DEFAULT | SPI_IER_RF_RDY_INT_EN | SPI_IER_TX_ERQ_INT_EN; 306 SPIREG_WRITE(sc, SPI_IER, ier); 307 308 SPIREG_WRITE(sc, SPI_TCR, tcr|SPI_TCR_XCH); 309 310 if (!cold) 311 return; 312 313 for (;;) { 314 sun6ispi_intr(sc); 315 if (ISSET(st->st_flags, SPI_F_DONE)) 316 break; 317 } 318 } 319 320 sc->sc_running = false; 321 } 322 323 static void 324 sun6ispi_send(struct sun6ispi_softc * const sc) 325 { 326 uint8_t fd; 327 uint32_t fsr; 328 struct spi_chunk *chunk; 329 330 while ((chunk = sc->sc_wchunk) != NULL) { 331 while (chunk->chunk_wresid) { 332 fsr = SPIREG_READ(sc, SPI_FSR); 333 if (__SHIFTOUT(fsr, SPI_FSR_TF_CNT) >= 64) { 334 return; 335 } 336 if (chunk->chunk_wptr) { 337 fd = *chunk->chunk_wptr++; 338 } else { 339 fd = '\0'; 340 } 341 bus_space_write_1(sc->sc_iot, sc->sc_ioh, SPI_TXD, fd); 342 chunk->chunk_wresid--; 343 } 344 sc->sc_wchunk = sc->sc_wchunk->chunk_next; 345 } 346 } 347 348 static void 349 sun6ispi_recv(struct sun6ispi_softc * const sc) 350 { 351 uint8_t fd; 352 uint32_t fsr; 353 struct spi_chunk *chunk; 354 355 while ((chunk = sc->sc_rchunk) != NULL) { 356 while (chunk->chunk_rresid) { 357 fsr = SPIREG_READ(sc, SPI_FSR); 358 if (__SHIFTOUT(fsr, SPI_FSR_RF_CNT) == 0) { 359 return; 360 } 361 fd = bus_space_read_1(sc->sc_iot, sc->sc_ioh, SPI_RXD); 362 if (chunk->chunk_rptr) { 363 *chunk->chunk_rptr++ = fd; 364 } 365 chunk->chunk_rresid--; 366 } 367 sc->sc_rchunk = sc->sc_rchunk->chunk_next; 368 } 369 } 370 371 static int 372 sun6ispi_intr(void *cookie) 373 { 374 struct sun6ispi_softc * const sc = cookie; 375 struct spi_transfer *st; 376 uint32_t isr; 377 378 isr = SPIREG_READ(sc, SPI_INT_STA); 379 SPIREG_WRITE(sc, SPI_INT_STA, isr); 380 381 if (ISSET(isr, SPI_ISR_RX_RDY)) { 382 sun6ispi_recv(sc); 383 sun6ispi_send(sc); 384 } 385 386 if (ISSET(isr, SPI_ISR_TC)) { 387 SPIREG_WRITE(sc, SPI_IER, SPI_IER_DEFAULT); 388 389 sc->sc_rchunk = sc->sc_wchunk = NULL; 390 st = sc->sc_transfer; 391 sc->sc_transfer = NULL; 392 KASSERT(st != NULL); 393 spi_done(st, 0); 394 sc->sc_running = false; 395 } 396 397 return isr; 398 } 399