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