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