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