1 /* $NetBSD: arspi.c,v 1.16 2025/09/10 01:55:07 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Urbana-Champaign Independent Media Center. 5 * Copyright (c) 2006 Garrett D'Amore. 6 * All rights reserved. 7 * 8 * Portions of this code were written by Garrett D'Amore for the 9 * Champaign-Urbana Community Wireless Network Project. 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above 17 * copyright notice, this list of conditions and the following 18 * disclaimer in the documentation and/or other materials provided 19 * with the distribution. 20 * 3. All advertising materials mentioning features or use of this 21 * software must display the following acknowledgements: 22 * This product includes software developed by the Urbana-Champaign 23 * Independent Media Center. 24 * This product includes software developed by Garrett D'Amore. 25 * 4. Urbana-Champaign Independent Media Center's name and Garrett 26 * D'Amore's name may not be used to endorse or promote products 27 * derived from this software without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT 30 * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR 31 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT 34 * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT, 35 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 44 #include <sys/cdefs.h> 45 __KERNEL_RCSID(0, "$NetBSD: arspi.c,v 1.16 2025/09/10 01:55:07 thorpej Exp $"); 46 47 #include "locators.h" 48 49 #include <sys/param.h> 50 #include <sys/bus.h> 51 #include <sys/cpu.h> 52 #include <sys/device.h> 53 #include <sys/errno.h> 54 #include <sys/kernel.h> 55 #include <sys/kmem.h> 56 #include <sys/proc.h> 57 #include <sys/systm.h> 58 59 #include <mips/atheros/include/ar5315reg.h> 60 #include <mips/atheros/include/arbusvar.h> 61 62 #include <mips/atheros/dev/arspireg.h> 63 64 #include <dev/spi/spiflash.h> 65 #include <dev/spi/spivar.h> 66 67 /* 68 * This device is intended only to operate with specific SPI flash 69 * parts, and is not a general purpose SPI host. (Or at least if it 70 * is, the Linux and eCos sources do not show how to use it as such.) 71 * And lack of documentation on the Atheros SoCs is less than helpful. 72 * 73 * So for now we just "emulate" enough of the host bus framework to 74 * make the SPI flash drivers happy. 75 */ 76 77 struct arspi_job { 78 uint8_t job_opcode; 79 struct spi_chunk *job_chunk; 80 uint32_t job_flags; 81 uint32_t job_addr; 82 uint32_t job_data; 83 int job_rxcnt; 84 int job_txcnt; 85 int job_addrcnt; 86 int job_rresid; 87 int job_wresid; 88 }; 89 90 #define JOB_READ 0x1 91 #define JOB_WRITE 0x2 92 #define JOB_LAST 0x4 93 #define JOB_WAIT 0x8 /* job must wait for WIP bits */ 94 #define JOB_WREN 0x10 /* WREN needed */ 95 96 struct arspi_softc { 97 struct spi_controller sc_spi; 98 void *sc_ih; 99 bool sc_interrupts; 100 101 struct spi_transfer *sc_transfer; 102 struct spi_chunk *sc_wchunk; /* for partial writes */ 103 struct spi_transq sc_transq; 104 bus_space_tag_t sc_st; 105 bus_space_handle_t sc_sh; 106 bus_size_t sc_size; 107 }; 108 109 #define STATIC 110 111 STATIC int arspi_match(device_t, cfdata_t, void *); 112 STATIC void arspi_attach(device_t, device_t, void *); 113 STATIC void arspi_interrupts(device_t); 114 STATIC int arspi_intr(void *); 115 /* SPI service routines */ 116 STATIC int arspi_configure(void *, int, int, int); 117 STATIC int arspi_transfer(void *, struct spi_transfer *); 118 /* internal support */ 119 STATIC void arspi_poll(struct arspi_softc *); 120 STATIC void arspi_done(struct arspi_softc *, int); 121 STATIC void arspi_sched(struct arspi_softc *); 122 STATIC int arspi_get_byte(struct spi_chunk **, uint8_t *); 123 STATIC int arspi_put_byte(struct spi_chunk **, uint8_t); 124 STATIC int arspi_make_job(struct spi_transfer *); 125 STATIC void arspi_update_job(struct spi_transfer *); 126 STATIC void arspi_finish_job(struct spi_transfer *); 127 128 129 CFATTACH_DECL_NEW(arspi, sizeof(struct arspi_softc), 130 arspi_match, arspi_attach, NULL, NULL); 131 132 #define GETREG(sc, o) bus_space_read_4(sc->sc_st, sc->sc_sh, o) 133 #define PUTREG(sc, o, v) bus_space_write_4(sc->sc_st, sc->sc_sh, o, v) 134 135 int 136 arspi_match(device_t parent, cfdata_t cf, void *aux) 137 { 138 struct arbus_attach_args *aa = aux; 139 140 if (strcmp(aa->aa_name, cf->cf_name) != 0) 141 return 0; 142 return 1; 143 } 144 145 void 146 arspi_attach(device_t parent, device_t self, void *aux) 147 { 148 struct arspi_softc *sc = device_private(self); 149 struct arbus_attach_args *aa = aux; 150 151 /* 152 * Map registers. 153 */ 154 sc->sc_st = aa->aa_bst; 155 sc->sc_size = aa->aa_size; 156 if (bus_space_map(sc->sc_st, aa->aa_addr, sc->sc_size, 0, 157 &sc->sc_sh) != 0) { 158 printf(": unable to map registers!\n"); 159 return; 160 } 161 162 aprint_normal(": Atheros SPI controller\n"); 163 164 /* 165 * Initialize SPI controller. 166 */ 167 sc->sc_spi.sct_cookie = sc; 168 sc->sc_spi.sct_configure = arspi_configure; 169 sc->sc_spi.sct_transfer = arspi_transfer; 170 sc->sc_spi.sct_nslaves = 1; 171 172 173 /* 174 * Initialize the queue. 175 */ 176 spi_transq_init(&sc->sc_transq); 177 178 /* 179 * Enable device interrupts. 180 */ 181 sc->sc_ih = arbus_intr_establish(aa->aa_cirq, aa->aa_mirq, 182 arspi_intr, sc); 183 if (sc->sc_ih == NULL) { 184 aprint_error("%s: couldn't establish interrupt\n", 185 device_xname(self)); 186 /* just leave it in polled mode */ 187 } else 188 config_interrupts(self, arspi_interrupts); 189 190 /* 191 * Initialize and attach bus attach. 192 */ 193 spibus_attach(self, &sc->sc_spi); 194 } 195 196 void 197 arspi_interrupts(device_t self) 198 { 199 /* 200 * we never leave polling mode, because, apparently, we 201 * are missing some data about how to drive the SPI in interrupt 202 * mode. 203 */ 204 #if 0 205 struct arspi_softc *sc = device_private(self); 206 int s; 207 208 s = splbio(); 209 sc->sc_interrupts = true; 210 splx(s); 211 #endif 212 } 213 214 int 215 arspi_intr(void *arg) 216 { 217 struct arspi_softc *sc = arg; 218 219 while (GETREG(sc, ARSPI_REG_CTL) & ARSPI_CTL_BUSY); 220 221 arspi_done(sc, 0); 222 223 return 1; 224 } 225 226 void 227 arspi_poll(struct arspi_softc *sc) 228 { 229 230 while (sc->sc_transfer) { 231 arspi_intr(sc); 232 } 233 } 234 235 int 236 arspi_configure(void *cookie, int slave, int mode, int speed) 237 { 238 239 /* 240 * We don't support the full SPI protocol, and hopefully the 241 * firmware has programmed a reasonable mode already. So 242 * just a couple of quick sanity checks, then bail. 243 */ 244 if ((mode != 0) || (slave != 0)) 245 return EINVAL; 246 247 return 0; 248 } 249 250 int 251 arspi_transfer(void *cookie, struct spi_transfer *st) 252 { 253 struct arspi_softc *sc = cookie; 254 int rv; 255 int s; 256 257 st->st_busprivate = NULL; 258 if ((rv = arspi_make_job(st)) != 0) { 259 if (st->st_busprivate) { 260 struct arspi_job *job = st->st_busprivate; 261 st->st_busprivate = NULL; 262 kmem_free(job, sizeof(*job)); 263 } 264 spi_done(st, rv); 265 return rv; 266 } 267 268 s = splbio(); 269 spi_transq_enqueue(&sc->sc_transq, st); 270 if (sc->sc_transfer == NULL) { 271 arspi_sched(sc); 272 if (!sc->sc_interrupts) 273 arspi_poll(sc); 274 } 275 splx(s); 276 return 0; 277 } 278 279 void 280 arspi_sched(struct arspi_softc *sc) 281 { 282 struct spi_transfer *st; 283 struct arspi_job *job; 284 uint32_t ctl, cnt; 285 286 for (;;) { 287 if ((st = sc->sc_transfer) == NULL) { 288 if ((st = spi_transq_first(&sc->sc_transq)) == NULL) { 289 /* no work left to do */ 290 break; 291 } 292 spi_transq_dequeue(&sc->sc_transq); 293 sc->sc_transfer = st; 294 } 295 296 arspi_update_job(st); 297 job = st->st_busprivate; 298 299 /* there shouldn't be anything running, but ensure it */ 300 do { 301 ctl = GETREG(sc, ARSPI_REG_CTL); 302 } while (ctl & ARSPI_CTL_BUSY); 303 /* clear all of the tx and rx bits */ 304 ctl &= ~(ARSPI_CTL_TXCNT_MASK | ARSPI_CTL_RXCNT_MASK); 305 306 if (job->job_flags & JOB_WAIT) { 307 PUTREG(sc, ARSPI_REG_OPCODE, SPIFLASH_CMD_RDSR); 308 /* only the opcode for tx */ 309 ctl |= (1 << ARSPI_CTL_TXCNT_SHIFT); 310 /* and one rx byte */ 311 ctl |= (1 << ARSPI_CTL_RXCNT_SHIFT); 312 } else if (job->job_flags & JOB_WREN) { 313 PUTREG(sc, ARSPI_REG_OPCODE, SPIFLASH_CMD_WREN); 314 /* just the opcode */ 315 ctl |= (1 << ARSPI_CTL_TXCNT_SHIFT); 316 /* no rx bytes */ 317 } else { 318 /* set the data */ 319 PUTREG(sc, ARSPI_REG_DATA, job->job_data); 320 321 /* set the opcode and the address */ 322 PUTREG(sc, ARSPI_REG_OPCODE, job->job_opcode | 323 (job->job_addr << 8)); 324 325 /* now set txcnt */ 326 cnt = 1; /* opcode */ 327 cnt += job->job_addrcnt + job->job_txcnt; 328 ctl |= (cnt << ARSPI_CTL_TXCNT_SHIFT); 329 330 /* now set rxcnt */ 331 cnt = job->job_rxcnt; 332 ctl |= (cnt << ARSPI_CTL_RXCNT_SHIFT); 333 } 334 335 /* set the start bit */ 336 ctl |= ARSPI_CTL_START; 337 338 PUTREG(sc, ARSPI_REG_CTL, ctl); 339 break; 340 } 341 } 342 343 void 344 arspi_done(struct arspi_softc *sc, int err) 345 { 346 struct spi_transfer *st; 347 struct arspi_job *job; 348 349 if ((st = sc->sc_transfer) != NULL) { 350 job = st->st_busprivate; 351 352 if (job->job_flags & JOB_WAIT) { 353 if (err == 0) { 354 if ((GETREG(sc, ARSPI_REG_DATA) & 355 SPIFLASH_SR_BUSY) == 0) { 356 /* intermediate wait done */ 357 job->job_flags &= ~JOB_WAIT; 358 goto done; 359 } 360 } 361 } else if (job->job_flags & JOB_WREN) { 362 if (err == 0) { 363 job->job_flags &= ~JOB_WREN; 364 goto done; 365 } 366 } else if (err == 0) { 367 /* 368 * When breaking up write jobs, we have to wait until 369 * the WIP bit is clear, and we have to separately 370 * send WREN for each chunk. These flags facilitate 371 * that. 372 */ 373 if (job->job_flags & JOB_WRITE) 374 job->job_flags |= (JOB_WAIT | JOB_WREN); 375 job->job_data = GETREG(sc, ARSPI_REG_DATA); 376 arspi_finish_job(st); 377 } 378 379 if (err || (job->job_flags & JOB_LAST)) { 380 sc->sc_transfer = NULL; 381 st->st_busprivate = NULL; 382 spi_done(st, err); 383 kmem_free(job, sizeof(*job)); 384 } 385 } 386 done: 387 arspi_sched(sc); 388 } 389 390 int 391 arspi_get_byte(struct spi_chunk **chunkp, uint8_t *bytep) 392 { 393 struct spi_chunk *chunk; 394 395 chunk = *chunkp; 396 397 /* skip leading empty (or already consumed) chunks */ 398 while (chunk && chunk->chunk_wresid == 0) 399 chunk = chunk->chunk_next; 400 401 if (chunk == NULL) { 402 return ENODATA; 403 } 404 405 /* 406 * chunk must be write only. SPI flash doesn't support 407 * any full duplex operations. 408 */ 409 if ((chunk->chunk_rptr) || !(chunk->chunk_wptr)) { 410 return EINVAL; 411 } 412 413 *bytep = *chunk->chunk_wptr; 414 chunk->chunk_wptr++; 415 chunk->chunk_wresid--; 416 chunk->chunk_rresid--; 417 /* clearing wptr and rptr makes sanity checks later easier */ 418 if (chunk->chunk_wresid == 0) 419 chunk->chunk_wptr = NULL; 420 if (chunk->chunk_rresid == 0) 421 chunk->chunk_rptr = NULL; 422 while (chunk && chunk->chunk_wresid == 0) 423 chunk = chunk->chunk_next; 424 425 *chunkp = chunk; 426 return 0; 427 } 428 429 int 430 arspi_put_byte(struct spi_chunk **chunkp, uint8_t byte) 431 { 432 struct spi_chunk *chunk; 433 434 chunk = *chunkp; 435 436 /* skip leading empty (or already consumed) chunks */ 437 while (chunk && chunk->chunk_rresid == 0) 438 chunk = chunk->chunk_next; 439 440 if (chunk == NULL) { 441 return EOVERFLOW; 442 } 443 444 /* 445 * chunk must be read only. SPI flash doesn't support 446 * any full duplex operations. 447 */ 448 if ((chunk->chunk_wptr) || !(chunk->chunk_rptr)) { 449 return EINVAL; 450 } 451 452 *chunk->chunk_rptr = byte; 453 chunk->chunk_rptr++; 454 chunk->chunk_wresid--; /* technically this was done at send time */ 455 chunk->chunk_rresid--; 456 while (chunk && chunk->chunk_rresid == 0) 457 chunk = chunk->chunk_next; 458 459 *chunkp = chunk; 460 return 0; 461 } 462 463 int 464 arspi_make_job(struct spi_transfer *st) 465 { 466 struct arspi_job *job; 467 struct spi_chunk *chunk; 468 uint8_t byte; 469 int i, rv; 470 471 job = kmem_zalloc(sizeof (struct arspi_job), KM_SLEEP); 472 473 st->st_busprivate = job; 474 475 /* skip any leading empty chunks (should not be any!) */ 476 chunk = st->st_chunks; 477 478 /* get transfer opcode */ 479 if ((rv = arspi_get_byte(&chunk, &byte)) != 0) 480 return rv; 481 482 job->job_opcode = byte; 483 switch (job->job_opcode) { 484 case SPIFLASH_CMD_WREN: 485 case SPIFLASH_CMD_WRDI: 486 case SPIFLASH_CMD_CHIPERASE: 487 break; 488 case SPIFLASH_CMD_RDJI: 489 job->job_rxcnt = 3; 490 break; 491 case SPIFLASH_CMD_RDSR: 492 job->job_rxcnt = 1; 493 break; 494 case SPIFLASH_CMD_WRSR: 495 /* 496 * is this in data, or in address? stick it in data 497 * for now. 498 */ 499 job->job_txcnt = 1; 500 break; 501 case SPIFLASH_CMD_RDID: 502 job->job_addrcnt = 3; /* 3 dummy bytes */ 503 job->job_rxcnt = 1; 504 break; 505 case SPIFLASH_CMD_ERASE: 506 job->job_addrcnt = 3; 507 break; 508 case SPIFLASH_CMD_READ: 509 job->job_addrcnt = 3; 510 job->job_flags |= JOB_READ; 511 break; 512 case SPIFLASH_CMD_PROGRAM: 513 job->job_addrcnt = 3; 514 job->job_flags |= JOB_WRITE; 515 break; 516 case SPIFLASH_CMD_READFAST: 517 /* 518 * This is a pain in the arse to support, so we will 519 * rewrite as an ordinary read. But later, after we 520 * obtain the address. 521 */ 522 job->job_addrcnt = 3; /* 3 address */ 523 job->job_flags |= JOB_READ; 524 break; 525 default: 526 return EINVAL; 527 } 528 529 for (i = 0; i < job->job_addrcnt; i++) { 530 if ((rv = arspi_get_byte(&chunk, &byte)) != 0) 531 return rv; 532 job->job_addr <<= 8; 533 job->job_addr |= byte; 534 } 535 536 537 if (job->job_opcode == SPIFLASH_CMD_READFAST) { 538 /* eat the dummy timing byte */ 539 if ((rv = arspi_get_byte(&chunk, &byte)) != 0) 540 return rv; 541 /* rewrite this as a read */ 542 job->job_opcode = SPIFLASH_CMD_READ; 543 } 544 545 job->job_chunk = chunk; 546 547 /* 548 * Now quickly check a few other things. Namely, we are not 549 * allowed to have both READ and WRITE. 550 */ 551 for (chunk = job->job_chunk; chunk; chunk = chunk->chunk_next) { 552 if (chunk->chunk_wptr) { 553 job->job_wresid += chunk->chunk_wresid; 554 } 555 if (chunk->chunk_rptr) { 556 job->job_rresid += chunk->chunk_rresid; 557 } 558 } 559 560 if (job->job_rresid && job->job_wresid) { 561 return EINVAL; 562 } 563 564 return 0; 565 } 566 567 /* 568 * NB: The Atheros SPI controller runs in little endian mode. So all 569 * data accesses must be swapped appropriately. 570 * 571 * The controller auto-swaps read accesses done through the mapped memory 572 * region, but when using SPI directly, we have to do the right thing to 573 * swap to or from little endian. 574 */ 575 576 void 577 arspi_update_job(struct spi_transfer *st) 578 { 579 struct arspi_job *job = st->st_busprivate; 580 uint8_t byte; 581 int i; 582 583 if (job->job_flags & (JOB_WAIT|JOB_WREN)) 584 return; 585 586 job->job_rxcnt = 0; 587 job->job_txcnt = 0; 588 job->job_data = 0; 589 590 job->job_txcnt = uimin(job->job_wresid, 4); 591 job->job_rxcnt = uimin(job->job_rresid, 4); 592 593 job->job_wresid -= job->job_txcnt; 594 job->job_rresid -= job->job_rxcnt; 595 596 for (i = 0; i < job->job_txcnt; i++) { 597 arspi_get_byte(&job->job_chunk, &byte); 598 job->job_data |= (byte << (i * 8)); 599 } 600 601 if ((!job->job_wresid) && (!job->job_rresid)) { 602 job->job_flags |= JOB_LAST; 603 } 604 } 605 606 void 607 arspi_finish_job(struct spi_transfer *st) 608 { 609 struct arspi_job *job = st->st_busprivate; 610 uint8_t byte; 611 int i; 612 613 job->job_addr += job->job_rxcnt; 614 job->job_addr += job->job_txcnt; 615 for (i = 0; i < job->job_rxcnt; i++) { 616 byte = job->job_data & 0xff; 617 job->job_data >>= 8; 618 arspi_put_byte(&job->job_chunk, byte); 619 } 620 } 621 622