[BACK]Return to sa11x0_ssp.c CVS log [TXT][DIR] Up to [local] / sys / arch / arm / sa11x0

File: [local] / sys / arch / arm / sa11x0 / sa11x0_ssp.c (download)

Revision 1.1, Tue Mar 4 16:05:17 2008 UTC (16 years, 2 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*	$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 <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/endian.h>

#include <dev/spivar.h>

#include <machine/bus.h>

#include <arm/sa11x0/sa11x0_reg.h>
#include <arm/sa11x0/sa11x0_var.h>
#include <arm/sa11x0/sa11x0_gpioreg.h>
#include <arm/sa11x0/sa11x0_sspreg.h>

#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;
}