/* $Id: sa11x0_ssp.c,v 1.1 2008/03/04 16:05:17 nbrk Exp $ */ /* * StrongARM 11[01]0 Synchronous Serial Port. * Actually it can operate using different serial protocols, * but we are interested in Motorola SPI for now. */ #include #include #include #include #include #include #include #include #include #include #include #include #define INVERTBITS(x) \ do { \ (x) = ((((x) & 0xf0) >> 4) | (((x) & 0x0f) << 4)); \ (x) = ((((x) & 0xcc) >> 2) | (((x) & 0x33) << 2)); \ (x) = ((((x) & 0xaa) >> 1) | (((x) & 0x55) << 1)); \ } while (0) struct sassp_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; /* XXX ioh for gpioc */ bus_space_handle_t sc_gpioch; struct spi_bus sc_bus; int sc_active; }; /* * Prototypes. */ int sassp_match(struct device *parent, void *cf, void *aux); void sassp_attach(struct device *parent, struct device *self, void *aux); int sassp_shift_1(void *arg, uint8_t data); #if 0 uint16_t sassp_shift_2(void *arg, uint16_t data); uint32_t sassp_shift_4(void *arg, uint32_t data); #endif int sassp_acquire(void *arg); void sassp_release(void *arg); /* * Autoconf glue. */ struct cfattach sassp_ca = { sizeof(struct sassp_softc), sassp_match, sassp_attach, NULL, NULL }; struct cfdriver sassp_cd = { NULL, "sassp", DV_DULL }; int sassp_match(struct device *parent, void *cf, void *aux) { struct saip_attach_args *saa = aux; if (saa->sai_addr == SASSP_BASE) return (1); return (0); } void sassp_attach(struct device *parent, struct device *self, void *aux) { struct sassp_softc *sc = (struct sassp_softc *)self; struct saip_attach_args *saa = aux; struct spibus_attach_args sba; sc->sc_iot = saa->sai_iot; if (bus_space_map(sc->sc_iot, saa->sai_addr, SASSP_NPORTS, 0, &sc->sc_ioh)) { printf(": can't map i/o space\n"); return; } if (bus_space_map(sc->sc_iot, SAGPIO_BASE, SAGPIO_NPORTS, 0, &sc->sc_gpioch)) { printf(": can't map gpio i/o space\n"); return; } printf(": SA-11x0 SSP/MCP txfifo %d rxfifo %d\n", SASSP_TXFIFOLEN, SASSP_RXFIFOLEN); #if 0 /* Force into SPI @460800 */ bus_space_write_4(sc->sc_iot, sc->sc_ioh, SASSP_CR0, 0x07 /* DSS */ | CR0_SSE | 0x03 /* SCR */ ); /* Read back rate */ sc->sc_bus.bus_speed = 3686400 / (2 *( (bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_CR0) & CR0_SCR_MASK) + 1 )); /* Set polarity and phase */ bus_space_write_4(sc->sc_iot, sc->sc_ioh, SASSP_CR1, CR1_SPO | CR1_SPH); printf("%s: configured to SPI mode at speed %ld\n", sc->sc_dev.dv_xname, sc->sc_bus.bus_speed); #endif /* * Attach SPI bus. */ sc->sc_bus.bus_cookie = sc; sc->sc_bus.bus_shift_1 = sassp_shift_1; sc->sc_bus.bus_acquire = sassp_acquire; sc->sc_bus.bus_release = sassp_release; /* attach args */ sba.sba_bus = &sc->sc_bus; config_found(&sc->sc_dev, &sba, spibus_print); return; } int sassp_shift_1(void *arg, uint8_t data) { /* * Shift a byte on a SPI bus. */ struct sassp_softc *sc = arg; uint32_t timeo; /* * Caller should already bother about locks.. */ /* wait for TX fifo to drain a bit if it's full */ while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_SR) & SR_TNF) == 0) ; /* wait for low on GPIO 10 */ timeo = 400000; while (timeo--) { if ( (bus_space_read_4(sc->sc_iot, sc->sc_gpioch, SAGPIO_PLR) & (1 << 10)) == 0) break; if (timeo == 0) { printf("%s: SSP tx timeout\n", sc->sc_dev.dv_xname); return(-1); } } /* invert data before sending */ INVERTBITS(data); bus_space_write_4(sc->sc_iot, sc->sc_ioh, SASSP_DR, (uint16_t)(data << 8)); /* wait for RX fifo become not empty */ timeo = 400000; while ( ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_SR) & SR_RNE) == 0) && timeo--) ; if (timeo == 0) printf("%s: WARNING, rx fifo empty after timeout\n", sc->sc_dev.dv_xname); /* read bottom of rx fifo */ data = (uint8_t)bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_DR); /* invert received data */ INVERTBITS(data); return(data); } int sassp_acquire(void *arg) { /* * Wake up slave by setting GPIO 25 low. */ struct sassp_softc *sc = arg; #if 0 uint32_t plmask; #endif /* check if this bus is already acquired */ if (sc->sc_active) { printf("%s: WARNING, trying to acquire locked bus\n", sc->sc_dev.dv_xname); return(1); } #if 0 /* * Modify Pin Level register. */ plmask = bus_space_read_4(sc->sc_iot, sc->sc_gpioch, 0x0c /* XXX SAGPIO_PCR */); plmask |= 1 << 25; bus_space_write_4(sc->sc_iot, sc->sc_gpioch, 0x0c, plmask); #endif sc->sc_active = 1; return(0); } void sassp_release(void *arg) { /* * Release bus by bringing GPIO 25 back to high. */ struct sassp_softc *sc = arg; #if 0 uint32_t plmask; plmask = bus_space_read_4(sc->sc_iot, sc->sc_gpioch, 0x08 /* XXX SAGPIO_PSR */); plmask |= (1 << 25); bus_space_write_4(sc->sc_iot, sc->sc_gpioch, 0x08, plmask); #endif sc->sc_active = 0; }