Esempio n. 1
0
int at91_spi_init(unsigned int clock, unsigned int mode)
{
    unsigned int	scbr;
    unsigned int 	csrx;
    unsigned int	mr;
    unsigned int	cs = at91_spi_get_cs();

    scbr = MASTER_CLOCK/ clock;
    csrx = AT91C_SPI_SCBR(scbr);
    csrx |= AT91C_SPI_BITS_8;
    if (!(mode & SPI_CPHA))
        csrx |= AT91C_SPI_NCPHA;
    if (mode & SPI_CPOL)
        csrx |= AT91C_SPI_CPOL;

    mr = AT91C_SPI_MSTR
         | AT91C_SPI_MODFDIS
         | AT91C_SPI_PCS((~(1 << cs) & 0xf));

    spi_writel(SPI_CSR(cs), csrx);
    spi_writel(SPI_MR, mr);

    return 0;
}
Esempio n. 2
0
int
at91spi_configure(void *arg, int slave, int mode, int speed)
{
	struct at91spi_softc *sc = arg;
	uint		scbr;
	uint32_t	csr;

	/* setup interrupt registers */
	PUTREG(sc, SPI_IDR, -1);	/* disable interrupts for now	*/

	csr = GETREG(sc, SPI_CSR(0));	/* read register		*/
	csr &= SPI_CSR_RESERVED;	/* keep reserved bits		*/
	csr |= SPI_CSR_BITS_8;		/* assume 8 bit transfers	*/

	/*
	 * Calculate clock divider
	 */
	scbr = speed ? ((AT91_MSTCLK + speed - 1) / speed + 1) & ~1 : -1;
	if (scbr > 0xFF) {
		aprint_error("%s: speed %d not supported\n",
		    device_xname(sc->sc_dev), speed);
		return EINVAL;
	}
	csr |= scbr << SPI_CSR_SCBR_SHIFT;

	/*
	 * I'm not entirely confident that these values are correct.
	 * But at least mode 0 appears to work properly with the
	 * devices I have tested.  The documentation seems to suggest
	 * that I have the meaning of the clock delay bit inverted.
	 */
	switch (mode) {
	case SPI_MODE_0:
		csr |= SPI_CSR_NCPHA;		/* CPHA = 0, CPOL = 0 */
		break;
	case SPI_MODE_1:
		csr |= 0;			/* CPHA = 1, CPOL = 0 */
		break;
	case SPI_MODE_2:
		csr |= SPI_CSR_NCPHA		/* CPHA = 0, CPOL = 1 */
		       | SPI_CSR_CPOL;
		break;
	case SPI_MODE_3:
		csr |= SPI_CSR_CPOL;		/* CPHA = 1, CPOL = 1 */
		break;
	default:
		return EINVAL;
	}

	PUTREG(sc, SPI_CSR(0), csr);

	DPRINTFN(3, ("%s: slave %d mode %d speed %d, csr=0x%08"PRIX32"\n",
		     __FUNCTION__, slave, mode, speed, csr));

#if 0
	// wait until ready!?
	for (i = 1000000; i; i -= 10) {
		if (GETREG(sc, AUPSC_SPISTAT) & SPISTAT_DR) {
			return 0;
		}
	}

	return ETIMEDOUT;
#else
	return 0;
#endif
}
Esempio n. 3
0
void
at91spi_xfer(struct at91spi_softc *sc, int start)
{
	struct spi_chunk	*chunk;
	int			len;
	uint32_t		sr;

	DPRINTFN(3, ("%s: sc=%p start=%d\n", __FUNCTION__, sc, start));

	/* so ready to transmit more / anything received? */
	if (((sr = GETREG(sc, SPI_SR)) & (SPI_SR_ENDTX | SPI_SR_ENDRX)) != (SPI_SR_ENDTX | SPI_SR_ENDRX)) {
		/* not ready, get out */
		DPRINTFN(3, ("%s: sc=%p start=%d sr=%"PRIX32"\n", __FUNCTION__, sc, start, sr));
		return;
	}

	DPRINTFN(3, ("%s: sr=%"PRIX32"\n", __FUNCTION__, sr));

	if (!start) {
		// ok, something has been transfered, synchronize..
		int offs = sc->sc_dmaoffs ^ HALF_BUF_SIZE;
		bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, offs, HALF_BUF_SIZE,
				BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);

		if ((chunk = sc->sc_rchunk) != NULL) {
			if ((len = chunk->chunk_rresid) > HALF_BUF_SIZE)
				len = HALF_BUF_SIZE;
			if (chunk->chunk_rptr && len > 0) {
				memcpy(chunk->chunk_rptr, (const uint8_t *)sc->sc_dmapage + offs, len);
				chunk->chunk_rptr += len;
			}
			if ((chunk->chunk_rresid -= len) <= 0) {
				// done with this chunk, get next
				sc->sc_rchunk = chunk->chunk_next;
			}
		}
	}

	/* start transmitting next chunk: */
	if ((chunk = sc->sc_wchunk) != NULL) {

		/* make sure we transmit just half buffer at a time */
		len = MIN(chunk->chunk_wresid, HALF_BUF_SIZE);

		// setup outgoing data
		if (chunk->chunk_wptr && len > 0) {
			memcpy((uint8_t *)sc->sc_dmapage + sc->sc_dmaoffs, chunk->chunk_wptr, len);
			chunk->chunk_wptr += len;
		} else {
			memset((uint8_t *)sc->sc_dmapage + sc->sc_dmaoffs, 0, len);
		}

		/* advance to next transfer if it's time to */
		if ((chunk->chunk_wresid -= len) <= 0) {
			sc->sc_wchunk = sc->sc_wchunk->chunk_next;
		}

		/* determine which interrupt to get */
		if (sc->sc_wchunk) {
			/* just wait for next buffer to free */
			PUTREG(sc, SPI_IER, SPI_SR_ENDRX);
		} else {
			/* must wait until transfer has completed */
			PUTREG(sc, SPI_IDR, SPI_SR_ENDRX);
			PUTREG(sc, SPI_IER, SPI_SR_RXBUFF);
		}

		DPRINTFN(3, ("%s: dmaoffs=%d len=%d wchunk=%p (%p:%d) rchunk=%p (%p:%d) mr=%"PRIX32" sr=%"PRIX32" imr=%"PRIX32" csr0=%"PRIX32"\n",
			     __FUNCTION__, sc->sc_dmaoffs, len, sc->sc_wchunk,
			     sc->sc_wchunk ? sc->sc_wchunk->chunk_wptr : NULL,
			     sc->sc_wchunk ? sc->sc_wchunk->chunk_wresid : -1,
			     sc->sc_rchunk,
			     sc->sc_rchunk ? sc->sc_rchunk->chunk_rptr : NULL,
			     sc->sc_rchunk ? sc->sc_rchunk->chunk_rresid : -1,
			     GETREG(sc, SPI_MR), GETREG(sc, SPI_SR), 
			     GETREG(sc, SPI_IMR), GETREG(sc, SPI_CSR(0))));

		// prepare DMA
		bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmaoffs, len,
				BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);

		// and start transmitting / receiving
		PUTREG(sc, SPI_PDC_BASE + PDC_RNPR, sc->sc_dmaaddr + sc->sc_dmaoffs);
		PUTREG(sc, SPI_PDC_BASE + PDC_RNCR, len);
		PUTREG(sc, SPI_PDC_BASE + PDC_TNPR, sc->sc_dmaaddr + sc->sc_dmaoffs);
		PUTREG(sc, SPI_PDC_BASE + PDC_TNCR, len);

		// swap buffer
		sc->sc_dmaoffs ^= HALF_BUF_SIZE;

		// get out
		return;
	} else {
		DPRINTFN(3, ("%s: nothing to write anymore\n", __FUNCTION__));
		return;
	}
}
Esempio n. 4
0
void
at91spi_attach_common(device_t parent, device_t self, void *aux,
		      at91spi_machdep_tag_t md)
{
	struct at91spi_softc *sc = device_private(self);
	struct at91bus_attach_args *sa = aux;
	struct spibus_attach_args sba;
	bus_dma_segment_t segs;
	int rsegs, err;

	aprint_normal(": AT91 SPI Controller\n");

	sc->sc_dev = self;
	sc->sc_iot = sa->sa_iot;
	sc->sc_pid = sa->sa_pid;
	sc->sc_dmat = sa->sa_dmat;
	sc->sc_md = md;

	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh))
		panic("%s: Cannot map registers", device_xname(self));

	/* we want to use dma, so allocate dma memory: */
	err = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, 0, PAGE_SIZE,
			       &segs, 1, &rsegs, BUS_DMA_WAITOK);
	if (err == 0) {
		err = bus_dmamem_map(sc->sc_dmat, &segs, 1, PAGE_SIZE,
				     &sc->sc_dmapage,
				     BUS_DMA_WAITOK);
	}
	if (err == 0) {
		err = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1,
					 PAGE_SIZE, 0, BUS_DMA_WAITOK,
					 &sc->sc_dmamap);
	}
	if (err == 0) {
		err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap,
				      sc->sc_dmapage, PAGE_SIZE, NULL,
				      BUS_DMA_WAITOK);
	}
	if (err != 0) {
		panic("%s: Cannot get DMA memory", device_xname(sc->sc_dev));
	}
	sc->sc_dmaaddr = sc->sc_dmamap->dm_segs[0].ds_addr;

	/*
	 * Initialize SPI controller
	 */
	sc->sc_spi.sct_cookie = sc;
	sc->sc_spi.sct_configure = at91spi_configure;
	sc->sc_spi.sct_transfer = at91spi_transfer;

	//sc->sc_spi.sct_nslaves must have been initialized by machdep code
	if (!sc->sc_spi.sct_nslaves) {
		aprint_error("%s: no slaves!\n", device_xname(sc->sc_dev));
	}

	sba.sba_controller = &sc->sc_spi;

	/* initialize the queue */
	SIMPLEQ_INIT(&sc->sc_q);

	/* reset the SPI */
	at91_peripheral_clock(sc->sc_pid, 1);
	PUTREG(sc, SPI_CR, SPI_CR_SWRST);
	delay(100);

	/* be paranoid and make sure the PDC is dead */
	PUTREG(sc, SPI_PDC_BASE + PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
	PUTREG(sc, SPI_PDC_BASE + PDC_RNCR, 0);
	PUTREG(sc, SPI_PDC_BASE + PDC_RCR, 0);
	PUTREG(sc, SPI_PDC_BASE + PDC_TNCR, 0);
	PUTREG(sc, SPI_PDC_BASE + PDC_TCR, 0);

	// configure SPI:
	PUTREG(sc, SPI_IDR, -1);
	PUTREG(sc, SPI_CSR(0), SPI_CSR_SCBR | SPI_CSR_BITS_8);
	PUTREG(sc, SPI_CSR(1), SPI_CSR_SCBR | SPI_CSR_BITS_8);
	PUTREG(sc, SPI_CSR(2), SPI_CSR_SCBR | SPI_CSR_BITS_8);
	PUTREG(sc, SPI_CSR(3), SPI_CSR_SCBR | SPI_CSR_BITS_8);
	PUTREG(sc, SPI_MR, SPI_MR_MODFDIS/* <- machdep? */ | SPI_MR_MSTR);

	/* enable device interrupts */
	sc->sc_ih = at91_intr_establish(sc->sc_pid, IPL_BIO, INTR_HIGH_LEVEL,
					at91spi_intr, sc);

	/* enable SPI */
	PUTREG(sc, SPI_CR, SPI_CR_SPIEN);
	if (GETREG(sc, SPI_SR) & SPI_SR_RDRF)
		(void)GETREG(sc, SPI_RDR);

	PUTREG(sc, SPI_PDC_BASE + PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN);

	/* attach slave devices */
	(void) config_found_ia(sc->sc_dev, "spibus", &sba, spibus_print);
}