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