summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/bfin_spi6xx.c308
-rw-r--r--drivers/spi/mxs_spi.c39
3 files changed, 328 insertions, 20 deletions
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 83abcbda28..b8264df3a9 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -31,6 +31,7 @@ COBJS-$(CONFIG_ARMADA100_SPI) += armada100_spi.o
COBJS-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o
COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o
COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o
+COBJS-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o
COBJS-$(CONFIG_CF_SPI) += cf_spi.o
COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
diff --git a/drivers/spi/bfin_spi6xx.c b/drivers/spi/bfin_spi6xx.c
new file mode 100644
index 0000000000..fde3447426
--- /dev/null
+++ b/drivers/spi/bfin_spi6xx.c
@@ -0,0 +1,308 @@
+/*
+ * Analog Devices SPI3 controller driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+
+#include <asm/blackfin.h>
+#include <asm/gpio.h>
+#include <asm/portmux.h>
+#include <asm/mach-common/bits/spi6xx.h>
+
+struct bfin_spi_slave {
+ struct spi_slave slave;
+ u32 control, clock;
+ struct bfin_spi_regs *regs;
+ int cs_pol;
+};
+
+#define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave)
+
+#define gpio_cs(cs) ((cs) - MAX_CTRL_CS)
+#ifdef CONFIG_BFIN_SPI_GPIO_CS
+# define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS)
+#else
+# define is_gpio_cs(cs) 0
+#endif
+
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ if (is_gpio_cs(cs))
+ return gpio_is_valid(gpio_cs(cs));
+ else
+ return (cs >= 1 && cs <= MAX_CTRL_CS);
+}
+
+void spi_cs_activate(struct spi_slave *slave)
+{
+ struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
+
+ if (is_gpio_cs(slave->cs)) {
+ unsigned int cs = gpio_cs(slave->cs);
+ gpio_set_value(cs, bss->cs_pol);
+ } else {
+ u32 ssel;
+ ssel = bfin_read32(&bss->regs->ssel);
+ ssel |= 1 << slave->cs;
+ if (bss->cs_pol)
+ ssel |= (1 << 8) << slave->cs;
+ else
+ ssel &= ~((1 << 8) << slave->cs);
+ bfin_write32(&bss->regs->ssel, ssel);
+ }
+
+ SSYNC();
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
+
+ if (is_gpio_cs(slave->cs)) {
+ unsigned int cs = gpio_cs(slave->cs);
+ gpio_set_value(cs, !bss->cs_pol);
+ } else {
+ u32 ssel;
+ ssel = bfin_read32(&bss->regs->ssel);
+ if (bss->cs_pol)
+ ssel &= ~((1 << 8) << slave->cs);
+ else
+ ssel |= (1 << 8) << slave->cs;
+ /* deassert cs */
+ bfin_write32(&bss->regs->ssel, ssel);
+ SSYNC();
+ /* disable cs */
+ ssel &= ~(1 << slave->cs);
+ bfin_write32(&bss->regs->ssel, ssel);
+ }
+
+ SSYNC();
+}
+
+void spi_init()
+{
+}
+
+#define SPI_PINS(n) \
+ { 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 }
+static unsigned short pins[][5] = {
+#ifdef SPI0_REGBASE
+ [0] = SPI_PINS(0),
+#endif
+#ifdef SPI1_REGBASE
+ [1] = SPI_PINS(1),
+#endif
+#ifdef SPI2_REGBASE
+ [2] = SPI_PINS(2),
+#endif
+};
+
+#define SPI_CS_PINS(n) \
+ { \
+ P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \
+ P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \
+ P_SPI##n##_SSEL7, \
+ }
+static const unsigned short cs_pins[][7] = {
+#ifdef SPI0_REGBASE
+ [0] = SPI_CS_PINS(0),
+#endif
+#ifdef SPI1_REGBASE
+ [1] = SPI_CS_PINS(1),
+#endif
+#ifdef SPI2_REGBASE
+ [2] = SPI_CS_PINS(2),
+#endif
+};
+
+void spi_set_speed(struct spi_slave *slave, uint hz)
+{
+ struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
+ ulong sclk;
+ u32 clock;
+
+ sclk = get_sclk1();
+ clock = sclk / hz;
+ if (clock)
+ clock--;
+ bss->clock = clock;
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct bfin_spi_slave *bss;
+ u32 reg_base;
+
+ if (!spi_cs_is_valid(bus, cs))
+ return NULL;
+
+ if (bus >= ARRAY_SIZE(pins) || pins[bus] == NULL) {
+ debug("%s: invalid bus %u\n", __func__, bus);
+ return NULL;
+ }
+ switch (bus) {
+#ifdef SPI0_REGBASE
+ case 0:
+ reg_base = SPI0_REGBASE;
+ break;
+#endif
+#ifdef SPI1_REGBASE
+ case 1:
+ reg_base = SPI1_REGBASE;
+ break;
+#endif
+#ifdef SPI2_REGBASE
+ case 2:
+ reg_base = SPI2_REGBASE;
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ bss = malloc(sizeof(*bss));
+ if (!bss)
+ return NULL;
+
+ bss->slave.bus = bus;
+ bss->slave.cs = cs;
+ bss->regs = (struct bfin_spi_regs *)reg_base;
+ bss->control = SPI_CTL_EN | SPI_CTL_MSTR;
+ if (mode & SPI_CPHA)
+ bss->control |= SPI_CTL_CPHA;
+ if (mode & SPI_CPOL)
+ bss->control |= SPI_CTL_CPOL;
+ if (mode & SPI_LSB_FIRST)
+ bss->control |= SPI_CTL_LSBF;
+ bss->control &= ~SPI_CTL_ASSEL;
+ bss->cs_pol = mode & SPI_CS_HIGH ? 1 : 0;
+ spi_set_speed(&bss->slave, max_hz);
+
+ return &bss->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
+ free(bss);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
+
+ debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
+
+ if (is_gpio_cs(slave->cs)) {
+ unsigned int cs = gpio_cs(slave->cs);
+ gpio_request(cs, "bfin-spi");
+ gpio_direction_output(cs, !bss->cs_pol);
+ pins[slave->bus][0] = P_DONTCARE;
+ } else
+ pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1];
+ peripheral_request_list(pins[slave->bus], "bfin-spi");
+
+ bfin_write32(&bss->regs->control, bss->control);
+ bfin_write32(&bss->regs->clock, bss->clock);
+ bfin_write32(&bss->regs->delay, 0x0);
+ bfin_write32(&bss->regs->rx_control, SPI_RXCTL_REN);
+ bfin_write32(&bss->regs->tx_control, SPI_TXCTL_TEN | SPI_TXCTL_TTI);
+ SSYNC();
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
+
+ debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
+
+ peripheral_free_list(pins[slave->bus]);
+ if (is_gpio_cs(slave->cs))
+ gpio_free(gpio_cs(slave->cs));
+
+ bfin_write32(&bss->regs->rx_control, 0x0);
+ bfin_write32(&bss->regs->tx_control, 0x0);
+ bfin_write32(&bss->regs->control, 0x0);
+ SSYNC();
+}
+
+#ifndef CONFIG_BFIN_SPI_IDLE_VAL
+# define CONFIG_BFIN_SPI_IDLE_VAL 0xff
+#endif
+
+static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx,
+ uint bytes)
+{
+ /* discard invalid rx data and empty rfifo */
+ while (!(bfin_read32(&bss->regs->status) & SPI_STAT_RFE))
+ bfin_read32(&bss->regs->rfifo);
+
+ while (bytes--) {
+ u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL);
+ debug("%s: tx:%x ", __func__, value);
+ bfin_write32(&bss->regs->tfifo, value);
+ SSYNC();
+ while (bfin_read32(&bss->regs->status) & SPI_STAT_RFE)
+ if (ctrlc())
+ return -1;
+ value = bfin_read32(&bss->regs->rfifo);
+ if (rx)
+ *rx++ = value;
+ debug("rx:%x\n", value);
+ }
+
+ return 0;
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
+{
+ struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
+ const u8 *tx = dout;
+ u8 *rx = din;
+ uint bytes = bitlen / 8;
+ int ret = 0;
+
+ debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
+ slave->bus, slave->cs, bitlen, bytes, flags);
+
+ if (bitlen == 0)
+ goto done;
+
+ /* we can only do 8 bit transfers */
+ if (bitlen % 8) {
+ flags |= SPI_XFER_END;
+ goto done;
+ }
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ ret = spi_pio_xfer(bss, tx, rx, bytes);
+
+ done:
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
+
+ return ret;
+}
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index bb865b7f4c..ffa3c1d693 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -40,17 +40,6 @@
#define MXSSSP_SMALL_TRANSFER 512
-/*
- * CONFIG_MXS_SPI_DMA_ENABLE: Experimental mixed PIO/DMA support for MXS SPI
- * host. Use with utmost caution!
- *
- * Enabling this is not yet recommended since this
- * still doesn't support transfers to/from unaligned
- * addresses. Therefore this driver will not work
- * for example with saving environment. This is
- * caused by DMA alignment constraints on MXS.
- */
-
struct mxs_spi_slave {
struct spi_slave slave;
uint32_t max_khz;
@@ -70,7 +59,7 @@ void spi_init(void)
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
/* MXS SPI: 4 ports and 3 chip selects maximum */
- if (bus > 3 || cs > 2)
+ if (!mxs_ssp_bus_id_valid(bus) || cs > 2)
return 0;
else
return 1;
@@ -92,7 +81,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!mxs_slave)
return NULL;
- if (mxs_dma_init_channel(bus))
+ if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
goto err_init;
mxs_slave->slave.bus = bus;
@@ -168,7 +157,12 @@ static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
while (length--) {
/* We transfer 1 byte */
+#if defined(CONFIG_MX23)
+ writel(SSP_CTRL0_XFER_COUNT_MASK, &ssp_regs->hw_ssp_ctrl0_clr);
+ writel(1, &ssp_regs->hw_ssp_ctrl0_set);
+#elif defined(CONFIG_MX28)
writel(1, &ssp_regs->hw_ssp_xfer_size);
+#endif
if ((flags & SPI_XFER_END) && !length)
mxs_spi_end_xfer(ssp_regs);
@@ -226,6 +220,12 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
int tl;
int ret = 0;
+#if defined(CONFIG_MX23)
+ const int mxs_spi_pio_words = 1;
+#elif defined(CONFIG_MX28)
+ const int mxs_spi_pio_words = 4;
+#endif
+
ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count);
memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count);
@@ -281,7 +281,7 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
dp->cmd.data |=
((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) |
- (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
+ (mxs_spi_pio_words << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
MXS_DMA_DESC_HALT_ON_TERMINATE |
MXS_DMA_DESC_TERMINATE_FLUSH;
@@ -298,15 +298,19 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
}
/*
- * Write CTRL0, CMD0, CMD1, XFER_SIZE registers. It is
+ * Write CTRL0, CMD0, CMD1 and XFER_SIZE registers in
+ * case of MX28, write only CTRL0 in case of MX23 due
+ * to the difference in register layout. It is utterly
* essential that the XFER_SIZE register is written on
* a per-descriptor basis with the same size as is the
* descriptor!
*/
dp->cmd.pio_words[0] = ctrl0;
+#ifdef CONFIG_MX28
dp->cmd.pio_words[1] = 0;
dp->cmd.pio_words[2] = 0;
dp->cmd.pio_words[3] = tl;
+#endif
mxs_dma_desc_append(dmach, dp);
@@ -332,12 +336,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
char dummy;
int write = 0;
char *data = NULL;
-
-#ifdef CONFIG_MXS_SPI_DMA_ENABLE
int dma = 1;
-#else
- int dma = 0;
-#endif
if (bitlen == 0) {
if (flags & SPI_XFER_END) {