From: Hans-Christian Egtvedt <[EMAIL PROTECTED]>

This adds a driver for the SPI controller found on most AT91 and AVR32
chips.

Currently, AT91RM9200 seems to have a SPI framework on its own. It
probably makes sense to merge the two drivers and make AT91RM9200 use
the framework everyone else uses at some point...

Signed-off-by: Haavard Skinnemoen <[EMAIL PROTECTED]>
---
 cpu/at32ap/at32ap700x/gpio.c                      |   34 ++++
 drivers/spi/Makefile                              |    1 +
 drivers/spi/atmel_spi.c                           |  194 +++++++++++++++++++++
 drivers/spi/atmel_spi.h                           |   90 ++++++++++
 include/asm-avr32/arch-at32ap700x/chip-features.h |    1 +
 include/asm-avr32/arch-at32ap700x/clk.h           |    6 +
 include/asm-avr32/arch-at32ap700x/gpio.h          |    4 +
 7 files changed, 330 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/atmel_spi.c
 create mode 100644 drivers/spi/atmel_spi.h

diff --git a/cpu/at32ap/at32ap700x/gpio.c b/cpu/at32ap/at32ap700x/gpio.c
index 859124a..68527cb 100644
--- a/cpu/at32ap/at32ap700x/gpio.c
+++ b/cpu/at32ap/at32ap700x/gpio.c
@@ -142,3 +142,37 @@ 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)
+{
+       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))
+               gpio_select_periph_A(GPIO_PIN_PA3,  0); /* NPCS0 */
+       if (cs_mask & (1 << 1))
+               gpio_select_periph_A(GPIO_PIN_PA4,  0); /* NPCS1 */
+       if (cs_mask & (1 << 2))
+               gpio_select_periph_A(GPIO_PIN_PA5,  0); /* NPCS2 */
+       if (cs_mask & (1 << 3))
+               gpio_select_periph_B(GPIO_PIN_PA20, 0); /* NPCS3 */
+}
+
+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
\ No newline at end of file
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index bc8a104..e66e0ee 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 0000000..6700d6b
--- /dev/null
+++ b/drivers/spi/atmel_spi.c
@@ -0,0 +1,194 @@
+/*
+ * 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 spi_slave        *slave;
+       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;
+
+       slave = malloc(sizeof(struct spi_slave));
+       if (!slave)
+               return NULL;
+
+       slave->regs = regs;
+       slave->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
+                       | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
+       spi_writel(slave, CSR(cs), csrx);
+
+       return slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+       free(slave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+       /* Enable the SPI hardware */
+       spi_writel(slave, CR, ATMEL_SPI_CR_SPIEN);
+
+       /*
+        * Select the slave. This should set SCK to the correct
+        * initial state, etc.
+        */
+       spi_writel(slave, MR, slave->mr);
+
+       return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+       /* Disable the SPI hardware */
+       spi_writel(slave, CR, ATMEL_SPI_CR_SPIDIS);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+               const void *dout, void *din, unsigned long flags)
+{
+       unsigned int    len_tx;
+       unsigned int    len_rx;
+       unsigned int    len;
+       u32             status;
+       const u8        *txp = dout;
+       u8              *rxp = din;
+       u8              value;
+
+       if (bitlen == 0)
+               return 0;
+
+       /*
+        * 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)
+               return -1;
+
+       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->cs);
+
+       for (len_tx = 0, len_rx = 0; len_rx < len; ) {
+               status = spi_readl(slave, 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(slave, TDR, value);
+                       len_tx++;
+               }
+               if (status & ATMEL_SPI_SR_RDRF) {
+                       value = spi_readl(slave, RDR);
+                       if (rxp)
+                               *rxp++ = value;
+                       len_rx++;
+               }
+       }
+
+       if (flags & SPI_XFER_END) {
+               /*
+                * Wait until the transfer is completely done before
+                * we deactivate CS.
+                */
+               do {
+                       status = spi_readl(slave, SR);
+               } while (!(status & ATMEL_SPI_SR_TXEMPTY));
+
+               spi_cs_deactivate(slave->cs);
+       }
+
+       return 0;
+}
diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h
new file mode 100644
index 0000000..af1853a
--- /dev/null
+++ b/drivers/spi/atmel_spi.h
@@ -0,0 +1,90 @@
+/*
+ * 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 spi_slave {
+       void            *regs;
+       unsigned int    cs;
+       u32             mr;
+};
+
+/* Register access macros */
+#define spi_readl(slave, reg)                                  \
+       readl(slave->regs + ATMEL_SPI_##reg)
+#define spi_writel(slave, reg, value)                          \
+       writel(value, slave->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 29b1fd6..c47107e 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 385319a..bd6f2e5 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
 
 /* Board code may need the SDRAM base clock as a compile-time constant */
 #define SDRAMC_BUS_HZ  (MAIN_CLK_RATE >> CFG_CLKDIV_HSB)
diff --git a/include/asm-avr32/arch-at32ap700x/gpio.h 
b/include/asm-avr32/arch-at32ap700x/gpio.h
index b10a3e4..ef20cea 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__ */
-- 
1.5.5.1


-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
U-Boot-Users mailing list
U-Boot-Users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/u-boot-users

Reply via email to