1 /* $NetBSD: auspi.c,v 1.12 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: auspi.c,v 1.12 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/proc.h> 56 #include <sys/systm.h> 57 58 #include <mips/alchemy/include/aubusvar.h> 59 #include <mips/alchemy/include/auvar.h> 60 61 #include <mips/alchemy/dev/aupscreg.h> 62 #include <mips/alchemy/dev/aupscvar.h> 63 #include <mips/alchemy/dev/auspireg.h> 64 #include <mips/alchemy/dev/auspivar.h> 65 66 #include <dev/spi/spivar.h> 67 68 struct auspi_softc { 69 device_t sc_dev; 70 struct aupsc_controller sc_psc; /* parent controller ops */ 71 struct spi_controller sc_spi; /* SPI implementation ops */ 72 struct auspi_machdep sc_md; /* board-specific support */ 73 struct auspi_job *sc_job; /* current job */ 74 struct spi_chunk *sc_wchunk; 75 struct spi_chunk *sc_rchunk; 76 void *sc_ih; /* interrupt handler */ 77 78 struct spi_transfer *sc_transfer; 79 bool sc_running; /* is it processing stuff? */ 80 81 SIMPLEQ_HEAD(,spi_transfer) sc_q; 82 }; 83 84 #define auspi_select(sc, slave) \ 85 (sc)->sc_md.am_select((sc)->sc_md.am_cookie, (slave)) 86 87 #define STATIC 88 89 STATIC int auspi_match(device_t, struct cfdata *, void *); 90 STATIC void auspi_attach(device_t, device_t, void *); 91 STATIC int auspi_intr(void *); 92 93 CFATTACH_DECL_NEW(auspi, sizeof(struct auspi_softc), 94 auspi_match, auspi_attach, NULL, NULL); 95 96 /* SPI service routines */ 97 STATIC int auspi_configure(void *, int, int, int); 98 STATIC int auspi_transfer(void *, struct spi_transfer *); 99 100 /* internal stuff */ 101 STATIC void auspi_done(struct auspi_softc *, int); 102 STATIC void auspi_send(struct auspi_softc *); 103 STATIC void auspi_recv(struct auspi_softc *); 104 STATIC void auspi_sched(struct auspi_softc *); 105 106 #define GETREG(sc, x) \ 107 bus_space_read_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x) 108 #define PUTREG(sc, x, v) \ 109 bus_space_write_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x, v) 110 111 int 112 auspi_match(device_t parent, struct cfdata *cf, void *aux) 113 { 114 struct aupsc_attach_args *aa = aux; 115 116 if (strcmp(aa->aupsc_name, cf->cf_name) != 0) 117 return 0; 118 119 return 1; 120 } 121 122 void 123 auspi_attach(device_t parent, device_t self, void *aux) 124 { 125 struct auspi_softc *sc = device_private(self); 126 struct aupsc_attach_args *aa = aux; 127 const struct auspi_machdep *md; 128 129 sc->sc_dev = self; 130 131 if ((md = auspi_machdep(aa->aupsc_addr)) != NULL) { 132 sc->sc_md = *md; 133 } 134 135 aprint_normal(": Alchemy PSC SPI protocol\n"); 136 137 sc->sc_psc = aa->aupsc_ctrl; 138 139 /* 140 * Initialize SPI controller 141 */ 142 sc->sc_spi.sct_cookie = sc; 143 sc->sc_spi.sct_configure = auspi_configure; 144 sc->sc_spi.sct_transfer = auspi_transfer; 145 146 /* fix this! */ 147 sc->sc_spi.sct_nslaves = sc->sc_md.am_nslaves; 148 149 /* enable SPI mode */ 150 sc->sc_psc.psc_enable(sc, AUPSC_SEL_SPI); 151 152 /* initialize the queue */ 153 SIMPLEQ_INIT(&sc->sc_q); 154 155 /* make sure interrupts disabled at the SPI */ 156 PUTREG(sc, AUPSC_SPIMSK, SPIMSK_ALL); 157 158 /* enable device interrupts */ 159 sc->sc_ih = au_intr_establish(aa->aupsc_irq, 0, IPL_BIO, IST_LEVEL, 160 auspi_intr, sc); 161 162 spibus_attach(self, &sc->sc_spi); 163 } 164 165 int 166 auspi_configure(void *arg, int slave, int mode, int speed) 167 { 168 struct auspi_softc *sc = arg; 169 int brg, i; 170 uint32_t reg; 171 172 /* setup interrupt registers */ 173 PUTREG(sc, AUPSC_SPIMSK, SPIMSK_NORM); 174 175 reg = GETREG(sc, AUPSC_SPICFG); 176 177 reg &= ~(SPICFG_BRG_MASK); /* clear BRG */ 178 reg &= ~(SPICFG_DIV_MASK); /* use pscn_mainclock/2 */ 179 reg &= ~(SPICFG_PSE); /* disable port swap */ 180 reg &= ~(SPICFG_BI); /* clear bit clock invert */ 181 reg &= ~(SPICFG_CDE); /* clear clock phase delay */ 182 reg &= ~(SPICFG_CGE); /* clear clock gate enable */ 183 //reg |= SPICFG_MO; /* master-only mode */ 184 reg |= SPICFG_DE; /* device enable */ 185 reg |= SPICFG_DD; /* disable DMA */ 186 reg |= SPICFG_RT_1; /* 1 byte rx fifo threshold */ 187 reg |= SPICFG_TT_1; /* 1 byte tx fifo threshold */ 188 reg |= ((8-1) << SPICFG_LEN_SHIFT);/* always work in 8-bit chunks */ 189 190 /* 191 * We assume a base clock of 48MHz has been established by the 192 * platform code. The clock divider reduces this to 24MHz. 193 * Next we have to figure out the BRG 194 */ 195 #define BASECLK 24000000 196 for (brg = 0; brg < 64; brg++) { 197 if (speed >= (BASECLK / ((brg + 1) * 2))) { 198 break; 199 } 200 } 201 202 /* 203 * Does the device want to go even slower? Our minimum speed without 204 * changing other assumptions, and complicating the code even further, 205 * is 24MHz/128, or 187.5kHz. That should be slow enough for any 206 * device we're likely to encounter. 207 */ 208 if (speed < (BASECLK / ((brg + 1) * 2))) { 209 return EINVAL; 210 } 211 reg &= ~SPICFG_BRG_MASK; 212 reg |= (brg << SPICFG_BRG_SHIFT); 213 214 /* 215 * I'm not entirely confident that these values are correct. 216 * But at least mode 0 appears to work properly with the 217 * devices I have tested. The documentation seems to suggest 218 * that I have the meaning of the clock delay bit inverted. 219 */ 220 switch (mode) { 221 case SPI_MODE_0: 222 reg |= 0; /* CPHA = 0, CPOL = 0 */ 223 break; 224 case SPI_MODE_1: 225 reg |= SPICFG_CDE; /* CPHA = 1, CPOL = 0 */ 226 break; 227 case SPI_MODE_2: 228 reg |= SPICFG_BI; /* CPHA = 0, CPOL = 1 */ 229 break; 230 case SPI_MODE_3: 231 reg |= SPICFG_CDE | SPICFG_BI; /* CPHA = 1, CPOL = 1 */ 232 break; 233 default: 234 return EINVAL; 235 } 236 237 PUTREG(sc, AUPSC_SPICFG, reg); 238 239 for (i = 1000000; i; i -= 10) { 240 if (GETREG(sc, AUPSC_SPISTAT) & SPISTAT_DR) { 241 return 0; 242 } 243 } 244 245 return ETIMEDOUT; 246 } 247 248 void 249 auspi_send(struct auspi_softc *sc) 250 { 251 uint32_t data; 252 struct spi_chunk *chunk; 253 254 /* fill the fifo */ 255 while ((chunk = sc->sc_wchunk) != NULL) { 256 257 while (chunk->chunk_wresid) { 258 259 /* transmit fifo full? */ 260 if (GETREG(sc, AUPSC_SPISTAT) & SPISTAT_TF) { 261 return; 262 } 263 264 if (chunk->chunk_wptr) { 265 data = *chunk->chunk_wptr++; 266 } else { 267 data = 0; 268 } 269 chunk->chunk_wresid--; 270 271 /* if the last outbound character, mark it */ 272 if ((chunk->chunk_wresid == 0) && 273 (chunk->chunk_next == NULL)) { 274 data |= SPITXRX_LC; 275 } 276 PUTREG(sc, AUPSC_SPITXRX, data); 277 } 278 279 /* advance to next transfer */ 280 sc->sc_wchunk = sc->sc_wchunk->chunk_next; 281 } 282 } 283 284 void 285 auspi_recv(struct auspi_softc *sc) 286 { 287 uint32_t data; 288 struct spi_chunk *chunk; 289 290 while ((chunk = sc->sc_rchunk) != NULL) { 291 while (chunk->chunk_rresid) { 292 293 /* rx fifo empty? */ 294 if ((GETREG(sc, AUPSC_SPISTAT) & SPISTAT_RE) != 0) { 295 return; 296 } 297 298 /* collect rx data */ 299 data = GETREG(sc, AUPSC_SPITXRX); 300 if (chunk->chunk_rptr) { 301 *chunk->chunk_rptr++ = data & 0xff; 302 } 303 304 chunk->chunk_rresid--; 305 } 306 307 /* advance next to next transfer */ 308 sc->sc_rchunk = sc->sc_rchunk->chunk_next; 309 } 310 } 311 312 void 313 auspi_sched(struct auspi_softc *sc) 314 { 315 struct spi_transfer *st; 316 int err; 317 318 while ((st = spi_transq_first(&sc->sc_q)) != NULL) { 319 320 /* remove the item */ 321 spi_transq_dequeue(&sc->sc_q); 322 323 /* note that we are working on it */ 324 sc->sc_transfer = st; 325 326 if ((err = auspi_select(sc, st->st_slave)) != 0) { 327 spi_done(st, err); 328 continue; 329 } 330 331 /* clear the fifos */ 332 PUTREG(sc, AUPSC_SPIPCR, SPIPCR_RC | SPIPCR_TC); 333 /* setup chunks */ 334 sc->sc_rchunk = sc->sc_wchunk = st->st_chunks; 335 auspi_send(sc); 336 /* now kick the master start to get the chip running */ 337 PUTREG(sc, AUPSC_SPIPCR, SPIPCR_MS); 338 sc->sc_running = true; 339 return; 340 } 341 auspi_select(sc, -1); 342 sc->sc_running = false; 343 } 344 345 void 346 auspi_done(struct auspi_softc *sc, int err) 347 { 348 struct spi_transfer *st; 349 350 /* called from interrupt handler */ 351 if ((st = sc->sc_transfer) != NULL) { 352 sc->sc_transfer = NULL; 353 spi_done(st, err); 354 } 355 /* make sure we clear these bits out */ 356 sc->sc_wchunk = sc->sc_rchunk = NULL; 357 auspi_sched(sc); 358 } 359 360 int 361 auspi_intr(void *arg) 362 { 363 struct auspi_softc *sc = arg; 364 uint32_t ev; 365 int err = 0; 366 367 368 if ((GETREG(sc, AUPSC_SPISTAT) & SPISTAT_DI) == 0) { 369 return 0; 370 } 371 372 ev = GETREG(sc, AUPSC_SPIEVNT); 373 374 if (ev & SPIMSK_MM) { 375 printf("%s: multiple masters detected!\n", 376 device_xname(sc->sc_dev)); 377 err = EIO; 378 } 379 if (ev & SPIMSK_RO) { 380 printf("%s: receive overflow\n", device_xname(sc->sc_dev)); 381 err = EIO; 382 } 383 if (ev & SPIMSK_TU) { 384 printf("%s: transmit underflow\n", device_xname(sc->sc_dev)); 385 err = EIO; 386 } 387 if (err) { 388 /* clear errors */ 389 PUTREG(sc, AUPSC_SPIEVNT, 390 ev & (SPIMSK_MM | SPIMSK_RO | SPIMSK_TU)); 391 /* clear the fifos */ 392 PUTREG(sc, AUPSC_SPIPCR, SPIPCR_RC | SPIPCR_TC); 393 auspi_done(sc, err); 394 395 } else { 396 397 /* do all data exchanges */ 398 auspi_send(sc); 399 auspi_recv(sc); 400 401 /* 402 * if the master done bit is set, make sure we do the 403 * right processing. 404 */ 405 if (ev & SPIMSK_MD) { 406 if ((sc->sc_wchunk != NULL) || 407 (sc->sc_rchunk != NULL)) { 408 printf("%s: partial transfer?\n", 409 device_xname(sc->sc_dev)); 410 err = EIO; 411 } 412 auspi_done(sc, err); 413 } 414 /* clear interrupts */ 415 PUTREG(sc, AUPSC_SPIEVNT, 416 ev & (SPIMSK_TR | SPIMSK_RR | SPIMSK_MD)); 417 } 418 419 return 1; 420 } 421 422 int 423 auspi_transfer(void *arg, struct spi_transfer *st) 424 { 425 struct auspi_softc *sc = arg; 426 int s; 427 428 /* make sure we select the right chip */ 429 s = splbio(); 430 spi_transq_enqueue(&sc->sc_q, st); 431 if (sc->sc_running == 0) { 432 auspi_sched(sc); 433 } 434 splx(s); 435 return 0; 436 } 437 438