diff options
| -rw-r--r-- | cpu/at32ap/at32ap700x/gpio.c | 43 | ||||
| -rw-r--r-- | drivers/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/spi/atmel_spi.c | 210 | ||||
| -rw-r--r-- | drivers/spi/atmel_spi.h | 95 | ||||
| -rw-r--r-- | include/asm-avr32/arch-at32ap700x/chip-features.h | 1 | ||||
| -rw-r--r-- | include/asm-avr32/arch-at32ap700x/clk.h | 6 | ||||
| -rw-r--r-- | include/asm-avr32/arch-at32ap700x/gpio.h | 4 | 
7 files changed, 360 insertions, 0 deletions
| diff --git a/cpu/at32ap/at32ap700x/gpio.c b/cpu/at32ap/at32ap700x/gpio.c index 859124a91..3da35d4fe 100644 --- a/cpu/at32ap/at32ap700x/gpio.c +++ b/cpu/at32ap/at32ap700x/gpio.c @@ -21,8 +21,11 @@   */  #include <common.h> +#include <asm/io.h> +  #include <asm/arch/chip-features.h>  #include <asm/arch/gpio.h> +#include <asm/arch/memory-map.h>  /*   * Lots of small functions here. We depend on --gc-sections getting @@ -142,3 +145,43 @@ void gpio_enable_mmci(void)  	gpio_select_periph_A(GPIO_PIN_PA15, 0);	/* DATA3 */  }  #endif + +#ifdef AT32AP700x_CHIP_HAS_SPI +void gpio_enable_spi0(unsigned long cs_mask) +{ +	u32 pa_mask = 0; + +	gpio_select_periph_A(GPIO_PIN_PA0,  0);	/* MISO	*/ +	gpio_select_periph_A(GPIO_PIN_PA1,  0);	/* MOSI	*/ +	gpio_select_periph_A(GPIO_PIN_PA2,  0);	/* SCK	*/ + +	if (cs_mask & (1 << 0)) +		pa_mask |= 1 << 3;	/* NPCS0 */ +	if (cs_mask & (1 << 1)) +		pa_mask |= 1 << 4;	/* NPCS1 */ +	if (cs_mask & (1 << 2)) +		pa_mask |= 1 << 5;	/* NPCS2 */ +	if (cs_mask & (1 << 3)) +		pa_mask |= 1 << 20;	/* NPCS3 */ + +	__raw_writel(pa_mask, PIOA_BASE + 0x00); +	__raw_writel(pa_mask, PIOA_BASE + 0x30); +	__raw_writel(pa_mask, PIOA_BASE + 0x10); +} + +void gpio_enable_spi1(unsigned long cs_mask) +{ +	gpio_select_periph_B(GPIO_PIN_PA0,  0);	/* MISO	*/ +	gpio_select_periph_B(GPIO_PIN_PB1,  0);	/* MOSI	*/ +	gpio_select_periph_B(GPIO_PIN_PB5,  0);	/* SCK	*/ + +	if (cs_mask & (1 << 0)) +		gpio_select_periph_B(GPIO_PIN_PB2,  0);	/* NPCS0 */ +	if (cs_mask & (1 << 1)) +		gpio_select_periph_B(GPIO_PIN_PB3,  0);	/* NPCS1 */ +	if (cs_mask & (1 << 2)) +		gpio_select_periph_B(GPIO_PIN_PB4,  0);	/* NPCS2 */ +	if (cs_mask & (1 << 3)) +		gpio_select_periph_A(GPIO_PIN_PA27, 0);	/* NPCS3 */ +} +#endif diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bc8a10412..e66e0ee09 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libspi.a  COBJS-y += mpc8xxx_spi.o +COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o  COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o  COBJS	:= $(COBJS-y) 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; +} diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h new file mode 100644 index 000000000..8b69a6d21 --- /dev/null +++ b/drivers/spi/atmel_spi.h @@ -0,0 +1,95 @@ +/* + * Register definitions for the Atmel AT32/AT91 SPI Controller + */ + +/* Register offsets */ +#define ATMEL_SPI_CR			0x0000 +#define ATMEL_SPI_MR			0x0004 +#define ATMEL_SPI_RDR			0x0008 +#define ATMEL_SPI_TDR			0x000c +#define ATMEL_SPI_SR			0x0010 +#define ATMEL_SPI_IER			0x0014 +#define ATMEL_SPI_IDR			0x0018 +#define ATMEL_SPI_IMR			0x001c +#define ATMEL_SPI_CSR(x)		(0x0030 + 4 * (x)) +#define ATMEL_SPI_VERSION		0x00fc + +/* Bits in CR */ +#define ATMEL_SPI_CR_SPIEN		(1 << 0) +#define ATMEL_SPI_CR_SPIDIS		(1 << 1) +#define ATMEL_SPI_CR_SWRST		(1 << 7) +#define ATMEL_SPI_CR_LASTXFER		(1 << 24) + +/* Bits in MR */ +#define ATMEL_SPI_MR_MSTR		(1 << 0) +#define ATMEL_SPI_MR_PS			(1 << 1) +#define ATMEL_SPI_MR_PCSDEC		(1 << 2) +#define ATMEL_SPI_MR_FDIV		(1 << 3) +#define ATMEL_SPI_MR_MODFDIS		(1 << 4) +#define ATMEL_SPI_MR_LLB		(1 << 7) +#define ATMEL_SPI_MR_PCS(x)		(((x) & 15) << 16) +#define ATMEL_SPI_MR_DLYBCS(x)		((x) << 24) + +/* Bits in RDR */ +#define ATMEL_SPI_RDR_RD(x)		(x) +#define ATMEL_SPI_RDR_PCS(x)		((x) << 16) + +/* Bits in TDR */ +#define ATMEL_SPI_TDR_TD(x)		(x) +#define ATMEL_SPI_TDR_PCS(x)		((x) << 16) +#define ATMEL_SPI_TDR_LASTXFER		(1 << 24) + +/* Bits in SR/IER/IDR/IMR */ +#define ATMEL_SPI_SR_RDRF		(1 << 0) +#define ATMEL_SPI_SR_TDRE		(1 << 1) +#define ATMEL_SPI_SR_MODF		(1 << 2) +#define ATMEL_SPI_SR_OVRES		(1 << 3) +#define ATMEL_SPI_SR_ENDRX		(1 << 4) +#define ATMEL_SPI_SR_ENDTX		(1 << 5) +#define ATMEL_SPI_SR_RXBUFF		(1 << 6) +#define ATMEL_SPI_SR_TXBUFE		(1 << 7) +#define ATMEL_SPI_SR_NSSR		(1 << 8) +#define ATMEL_SPI_SR_TXEMPTY		(1 << 9) +#define ATMEL_SPI_SR_SPIENS		(1 << 16) + +/* Bits in CSRx */ +#define ATMEL_SPI_CSRx_CPOL		(1 << 0) +#define ATMEL_SPI_CSRx_NCPHA		(1 << 1) +#define ATMEL_SPI_CSRx_CSAAT		(1 << 3) +#define ATMEL_SPI_CSRx_BITS(x)		((x) << 4) +#define ATMEL_SPI_CSRx_SCBR(x)		((x) << 8) +#define ATMEL_SPI_CSRx_SCBR_MAX		0xff +#define ATMEL_SPI_CSRx_DLYBS(x)		((x) << 16) +#define ATMEL_SPI_CSRx_DLYBCT(x)	((x) << 24) + +/* Bits in VERSION */ +#define ATMEL_SPI_VERSION_REV(x)	((x) << 0) +#define ATMEL_SPI_VERSION_MFN(x)	((x) << 16) + +/* Constants for CSRx:BITS */ +#define ATMEL_SPI_BITS_8		0 +#define ATMEL_SPI_BITS_9		1 +#define ATMEL_SPI_BITS_10		2 +#define ATMEL_SPI_BITS_11		3 +#define ATMEL_SPI_BITS_12		4 +#define ATMEL_SPI_BITS_13		5 +#define ATMEL_SPI_BITS_14		6 +#define ATMEL_SPI_BITS_15		7 +#define ATMEL_SPI_BITS_16		8 + +struct atmel_spi_slave { +	struct spi_slave slave; +	void		*regs; +	u32		mr; +}; + +static inline struct atmel_spi_slave *to_atmel_spi(struct spi_slave *slave) +{ +	return container_of(slave, struct atmel_spi_slave, slave); +} + +/* Register access macros */ +#define spi_readl(as, reg)					\ +	readl(as->regs + ATMEL_SPI_##reg) +#define spi_writel(as, reg, value)				\ +	writel(value, as->regs + ATMEL_SPI_##reg) diff --git a/include/asm-avr32/arch-at32ap700x/chip-features.h b/include/asm-avr32/arch-at32ap700x/chip-features.h index 29b1fd663..c47107e2a 100644 --- a/include/asm-avr32/arch-at32ap700x/chip-features.h +++ b/include/asm-avr32/arch-at32ap700x/chip-features.h @@ -25,6 +25,7 @@  /* Currently, all the AP700x chips have these */  #define AT32AP700x_CHIP_HAS_USART  #define AT32AP700x_CHIP_HAS_MMCI +#define AT32AP700x_CHIP_HAS_SPI  /* Only AP7000 has ethernet interface */  #ifdef CONFIG_AT32AP7000 diff --git a/include/asm-avr32/arch-at32ap700x/clk.h b/include/asm-avr32/arch-at32ap700x/clk.h index 4a1dd33b5..a9d8431a6 100644 --- a/include/asm-avr32/arch-at32ap700x/clk.h +++ b/include/asm-avr32/arch-at32ap700x/clk.h @@ -74,6 +74,12 @@ static inline unsigned long get_mci_clk_rate(void)  	return get_pbb_clk_rate();  }  #endif +#ifdef AT32AP700x_CHIP_HAS_SPI +static inline unsigned long get_spi_clk_rate(unsigned int dev_id) +{ +	return get_pba_clk_rate(); +} +#endif  extern void clk_init(void); diff --git a/include/asm-avr32/arch-at32ap700x/gpio.h b/include/asm-avr32/arch-at32ap700x/gpio.h index b10a3e4f8..ef20ceaab 100644 --- a/include/asm-avr32/arch-at32ap700x/gpio.h +++ b/include/asm-avr32/arch-at32ap700x/gpio.h @@ -216,5 +216,9 @@ void gpio_enable_macb1(void);  #ifdef AT32AP700x_CHIP_HAS_MMCI  void gpio_enable_mmci(void);  #endif +#ifdef AT32AP700x_CHIP_HAS_SPI +void gpio_enable_spi0(unsigned long cs_mask); +void gpio_enable_spi1(unsigned long cs_mask); +#endif  #endif /* __ASM_AVR32_ARCH_GPIO_H__ */ |