diff options
| author | Hans-Christian Egtvedt <hcegtvedt@atmel.com> | 2008-05-16 11:10:32 +0200 | 
|---|---|---|
| committer | Wolfgang Denk <wd@denx.de> | 2008-06-03 20:30:05 +0200 | 
| commit | 60445cb5c3eb77ed1a07f2d908eef09174483698 (patch) | |
| tree | bea7feee25572ecae29288549f0344a8f3a3da26 /drivers/spi/atmel_spi.c | |
| parent | d255bb0e78d1cac5b7c8c98cb77a095f5f16de0d (diff) | |
| download | olio-uboot-2014.01-60445cb5c3eb77ed1a07f2d908eef09174483698.tar.xz olio-uboot-2014.01-60445cb5c3eb77ed1a07f2d908eef09174483698.zip | |
atmel_spi: Driver for the Atmel SPI controller
This adds a driver for the SPI controller found on most AT91 and AVR32
chips, implementing the new SPI API.
Changed in v4:
  - Update to new API
  - Handle zero-length transfers appropriately. The user may send a
    zero-length SPI transfer with SPI_XFER_END set in order to
    deactivate the chip select after a series of transfers with chip
    select active. This is useful e.g. when polling the status
    register of DataFlash.
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'drivers/spi/atmel_spi.c')
| -rw-r--r-- | drivers/spi/atmel_spi.c | 210 | 
1 files changed, 210 insertions, 0 deletions
| diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c new file mode 100644 index 000000000..317c0b41b --- /dev/null +++ b/drivers/spi/atmel_spi.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <spi.h> +#include <malloc.h> + +#include <asm/io.h> + +#include <asm/arch/clk.h> +#include <asm/arch/memory-map.h> + +#include "atmel_spi.h" + +void spi_init() +{ + +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, +			unsigned int max_hz, unsigned int mode) +{ +	struct atmel_spi_slave	*as; +	unsigned int		scbr; +	u32			csrx; +	void			*regs; + +	if (cs > 3 || !spi_cs_is_valid(bus, cs)) +		return NULL; + +	switch (bus) { +	case 0: +		regs = (void *)SPI0_BASE; +		break; +#ifdef SPI1_BASE +	case 1: +		regs = (void *)SPI1_BASE; +		break; +#endif +#ifdef SPI2_BASE +	case 2: +		regs = (void *)SPI2_BASE; +		break; +#endif +#ifdef SPI3_BASE +	case 3: +		regs = (void *)SPI3_BASE; +		break; +#endif +	default: +		return NULL; +	} + + +	scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz; +	if (scbr > ATMEL_SPI_CSRx_SCBR_MAX) +		/* Too low max SCK rate */ +		return NULL; +	if (scbr < 1) +		scbr = 1; + +	csrx = ATMEL_SPI_CSRx_SCBR(scbr); +	csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8); +	if (!(mode & SPI_CPHA)) +		csrx |= ATMEL_SPI_CSRx_NCPHA; +	if (mode & SPI_CPOL) +		csrx |= ATMEL_SPI_CSRx_CPOL; + +	as = malloc(sizeof(struct atmel_spi_slave)); +	if (!as) +		return NULL; + +	as->slave.bus = bus; +	as->slave.cs = cs; +	as->regs = regs; +	as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS +			| ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); +	spi_writel(as, CSR(cs), csrx); + +	return &as->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ +	struct atmel_spi_slave *as = to_atmel_spi(slave); + +	free(as); +} + +int spi_claim_bus(struct spi_slave *slave) +{ +	struct atmel_spi_slave *as = to_atmel_spi(slave); + +	/* Enable the SPI hardware */ +	spi_writel(as, CR, ATMEL_SPI_CR_SPIEN); + +	/* +	 * Select the slave. This should set SCK to the correct +	 * initial state, etc. +	 */ +	spi_writel(as, MR, as->mr); + +	return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +	struct atmel_spi_slave *as = to_atmel_spi(slave); + +	/* Disable the SPI hardware */ +	spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, +		const void *dout, void *din, unsigned long flags) +{ +	struct atmel_spi_slave *as = to_atmel_spi(slave); +	unsigned int	len_tx; +	unsigned int	len_rx; +	unsigned int	len; +	int		ret; +	u32		status; +	const u8	*txp = dout; +	u8		*rxp = din; +	u8		value; + +	ret = 0; +	if (bitlen == 0) +		/* Finish any previously submitted transfers */ +		goto out; + +	/* +	 * TODO: The controller can do non-multiple-of-8 bit +	 * transfers, but this driver currently doesn't support it. +	 * +	 * It's also not clear how such transfers are supposed to be +	 * represented as a stream of bytes...this is a limitation of +	 * the current SPI interface. +	 */ +	if (bitlen % 8) { +		/* Errors always terminate an ongoing transfer */ +		flags |= SPI_XFER_END; +		goto out; +	} + +	len = bitlen / 8; + +	/* +	 * The controller can do automatic CS control, but it is +	 * somewhat quirky, and it doesn't really buy us much anyway +	 * in the context of U-Boot. +	 */ +	if (flags & SPI_XFER_BEGIN) +		spi_cs_activate(slave); + +	for (len_tx = 0, len_rx = 0; len_rx < len; ) { +		status = spi_readl(as, SR); + +		if (status & ATMEL_SPI_SR_OVRES) +			return -1; + +		if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) { +			if (txp) +				value = *txp++; +			else +				value = 0; +			spi_writel(as, TDR, value); +			len_tx++; +		} +		if (status & ATMEL_SPI_SR_RDRF) { +			value = spi_readl(as, RDR); +			if (rxp) +				*rxp++ = value; +			len_rx++; +		} +	} + +out: +	if (flags & SPI_XFER_END) { +		/* +		 * Wait until the transfer is completely done before +		 * we deactivate CS. +		 */ +		do { +			status = spi_readl(as, SR); +		} while (!(status & ATMEL_SPI_SR_TXEMPTY)); + +		spi_cs_deactivate(slave); +	} + +	return 0; +} |