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