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/atmel_spi.c3
-rw-r--r--drivers/spi/atmel_spi.h1
-rw-r--r--drivers/spi/cf_qspi.c373
-rw-r--r--drivers/spi/kirkwood_spi.c5
-rw-r--r--drivers/spi/mpc8xxx_spi.c4
-rw-r--r--drivers/spi/mxc_spi.c6
-rw-r--r--drivers/spi/mxs_spi.c252
-rw-r--r--drivers/spi/omap3_spi.c16
-rw-r--r--drivers/spi/omap3_spi.h5
-rw-r--r--drivers/spi/tegra_spi.c10
-rw-r--r--drivers/spi/xilinx_spi.c1
12 files changed, 628 insertions, 49 deletions
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 80b981f10f..f0b82c67f5 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -32,6 +32,7 @@ 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_CF_SPI) += cf_spi.o
+COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 83ef8e8b19..c7a51f7f39 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -92,6 +92,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
as->slave.cs = cs;
as->regs = regs;
as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
+#if defined(CONFIG_AT91SAM9X5)
+ | ATMEL_SPI_MR_WDRBT
+#endif
| ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
spi_writel(as, CSR(cs), csrx);
diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h
index 8b69a6d215..057de9adee 100644
--- a/drivers/spi/atmel_spi.h
+++ b/drivers/spi/atmel_spi.h
@@ -26,6 +26,7 @@
#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_WDRBT (1 << 5)
#define ATMEL_SPI_MR_LLB (1 << 7)
#define ATMEL_SPI_MR_PCS(x) (((x) & 15) << 16)
#define ATMEL_SPI_MR_DLYBCS(x) ((x) << 24)
diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c
new file mode 100644
index 0000000000..72dd1a520d
--- /dev/null
+++ b/drivers/spi/cf_qspi.c
@@ -0,0 +1,373 @@
+/*
+ * Freescale Coldfire Queued SPI driver
+ *
+ * NOTE:
+ * This driver is written to transfer 8 bit at-a-time and uses the dedicated
+ * SPI slave select pins as bit-banged GPIO to work with spi_flash subsystem.
+ *
+ *
+ * Copyright (C) 2011 Ruggedcom, Inc.
+ * Richard Retanubun (richardretanubun@freescale.com)
+ *
+ * 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 <malloc.h>
+#include <spi.h>
+#include <asm/immap.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define clamp(x, low, high) (min(max(low, x), high))
+#define to_cf_qspi_slave(s) container_of(s, struct cf_qspi_slave, s)
+
+struct cf_qspi_slave {
+ struct spi_slave slave; /* Specific bus:cs ID for each device */
+ qspi_t *regs; /* Pointer to SPI controller registers */
+ u16 qmr; /* QMR: Queued Mode Register */
+ u16 qwr; /* QWR: Queued Wrap Register */
+ u16 qcr; /* QCR: Queued Command Ram */
+};
+
+/* Register write wrapper functions */
+static void write_qmr(volatile qspi_t *qspi, u16 val) { qspi->mr = val; }
+static void write_qdlyr(volatile qspi_t *qspi, u16 val) { qspi->dlyr = val; }
+static void write_qwr(volatile qspi_t *qspi, u16 val) { qspi->wr = val; }
+static void write_qir(volatile qspi_t *qspi, u16 val) { qspi->ir = val; }
+static void write_qar(volatile qspi_t *qspi, u16 val) { qspi->ar = val; }
+static void write_qdr(volatile qspi_t *qspi, u16 val) { qspi->dr = val; }
+/* Register read wrapper functions */
+static u16 read_qdlyr(volatile qspi_t *qspi) { return qspi->dlyr; }
+static u16 read_qwr(volatile qspi_t *qspi) { return qspi->wr; }
+static u16 read_qir(volatile qspi_t *qspi) { return qspi->ir; }
+static u16 read_qdr(volatile qspi_t *qspi) { return qspi->dr; }
+
+/* These call points may be different for each ColdFire CPU */
+extern void cfspi_port_conf(void);
+static void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high);
+static void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high);
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ return 0;
+}
+void spi_release_bus(struct spi_slave *slave)
+{
+}
+
+__attribute__((weak))
+void spi_init(void)
+{
+ cfspi_port_conf();
+}
+
+__attribute__((weak))
+void spi_cs_activate(struct spi_slave *slave)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+
+ cfspi_cs_activate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV));
+}
+
+__attribute__((weak))
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+
+ cfspi_cs_deactivate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV));
+}
+
+__attribute__((weak))
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ /* Only 1 bus and 4 chipselect per controller */
+ if (bus == 0 && (cs >= 0 && cs < 4))
+ return 1;
+ else
+ return 0;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+
+ free(dev);
+}
+
+/* Translate information given by spi_setup_slave to members of cf_qspi_slave */
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct cf_qspi_slave *dev = NULL;
+
+ if (!spi_cs_is_valid(bus, cs))
+ return NULL;
+
+ dev = malloc(sizeof(struct cf_qspi_slave));
+ if (!dev)
+ return NULL;
+
+ /* Initialize to known value */
+ dev->slave.bus = bus;
+ dev->slave.cs = cs;
+ dev->regs = (qspi_t *)MMAP_QSPI;
+ dev->qmr = 0;
+ dev->qwr = 0;
+ dev->qcr = 0;
+
+
+ /* Map max_hz to QMR[BAUD] */
+ if (max_hz == 0) /* Go as fast as possible */
+ dev->qmr = 2u;
+ else /* Get the closest baud rate */
+ dev->qmr = clamp(((gd->bus_clk >> 2) + max_hz - 1)/max_hz,
+ 2u, 255u);
+
+ /* Map mode to QMR[CPOL] and QMR[CPHA] */
+ if (mode & SPI_CPOL)
+ dev->qmr |= QSPI_QMR_CPOL;
+
+ if (mode & SPI_CPHA)
+ dev->qmr |= QSPI_QMR_CPHA;
+
+ /* Hardcode bit length to 8 bit per transter */
+ dev->qmr |= QSPI_QMR_BITS_8;
+
+ /* Set QMR[MSTR] to enable QSPI as master */
+ dev->qmr |= QSPI_QMR_MSTR;
+
+ /*
+ * Set QCR and QWR to default values for spi flash operation.
+ * If more custom QCR and QRW are needed, overload mode variable
+ */
+ dev->qcr = (QSPI_QDR_CONT | QSPI_QDR_BITSE);
+
+ if (!(mode & SPI_CS_HIGH))
+ dev->qwr |= QSPI_QWR_CSIV;
+
+ return &dev->slave;
+}
+
+/* Transfer 8 bit at a time */
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+ volatile qspi_t *qspi = dev->regs;
+ u8 *txbuf = (u8 *)dout;
+ u8 *rxbuf = (u8 *)din;
+ u32 count = ((bitlen / 8) + (bitlen % 8 ? 1 : 0));
+ u32 n, i = 0;
+
+ /* Sanitize arguments */
+ if (slave == NULL) {
+ printf("%s: NULL slave ptr\n", __func__);
+ return -1;
+ }
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ /* There is something to send, lets process it. spi_xfer is also called
+ * just to toggle chip select, so bitlen of 0 is valid */
+ if (count > 0) {
+ /*
+ * NOTE: Since chip select is driven as a bit-bang-ed GPIO
+ * using spi_cs_activate() and spi_cs_deactivate(),
+ * the chip select settings inside the controller
+ * (i.e. QCR[CONT] and QWR[CSIV]) are moot. The bits are set to
+ * keep the controller settings consistent with the actual
+ * operation of the bus.
+ */
+
+ /* Write the slave device's settings for the controller.*/
+ write_qmr(qspi, dev->qmr);
+ write_qwr(qspi, dev->qwr);
+
+ /* Limit transfer to 16 at a time */
+ n = min(count, 16u);
+ do {
+ /* Setup queue end point */
+ write_qwr(qspi, ((read_qwr(qspi) & QSPI_QWR_ENDQP_MASK)
+ | QSPI_QWR_ENDQP((n-1))));
+
+ /* Write Command RAM */
+ write_qar(qspi, QSPI_QAR_CMD);
+ for (i = 0; i < n; ++i)
+ write_qdr(qspi, dev->qcr);
+
+ /* Write TxBuf, if none given, fill with ZEROes */
+ write_qar(qspi, QSPI_QAR_TRANS);
+ if (txbuf) {
+ for (i = 0; i < n; ++i)
+ write_qdr(qspi, *txbuf++);
+ } else {
+ for (i = 0; i < n; ++i)
+ write_qdr(qspi, 0);
+ }
+
+ /* Clear QIR[SPIF] by writing a 1 to it */
+ write_qir(qspi, read_qir(qspi) | QSPI_QIR_SPIF);
+ /* Set QDLYR[SPE] to start sending */
+ write_qdlyr(qspi, read_qdlyr(qspi) | QSPI_QDLYR_SPE);
+
+ /* Poll QIR[SPIF] for transfer completion */
+ while ((read_qir(qspi) & QSPI_QIR_SPIF) != 1)
+ udelay(1);
+
+ /* If given read RxBuf, load data to it */
+ if (rxbuf) {
+ write_qar(qspi, QSPI_QAR_RECV);
+ for (i = 0; i < n; ++i)
+ *rxbuf++ = read_qdr(qspi);
+ }
+
+ /* Decrement count */
+ count -= n;
+ } while (count);
+ }
+
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
+
+/* Each MCF CPU may have different pin assignments for chip selects. */
+#if defined(CONFIG_M5271)
+/* Assert chip select, val = [1|0] , dir = out, mode = GPIO */
+void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high)
+{
+ debug("%s: bus %d cs %d cs_active_high %d\n",
+ __func__, bus, cs, cs_active_high);
+
+ switch (cs) {
+ case 0: /* QSPI_CS[0] = PQSPI[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x08);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF);
+ break;
+ case 1: /* QSPI_CS[1] = PQSPI[4] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x10);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F);
+ break;
+ case 2: /* QSPI_CS[2] = PTIMER[7] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x80);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF);
+ break;
+ case 3: /* QSPI_CS[3] = PTIMER[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x08);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F);
+ break;
+ }
+}
+
+/* Deassert chip select, val = [1|0], dir = in, mode = GPIO
+ * direction set as IN to undrive the pin, external pullup/pulldown will bring
+ * bus to deassert state.
+ */
+void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high)
+{
+ debug("%s: bus %d cs %d cs_active_high %d\n",
+ __func__, bus, cs, cs_active_high);
+
+ switch (cs) {
+ case 0: /* QSPI_CS[0] = PQSPI[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xF7);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF);
+ break;
+ case 1: /* QSPI_CS[1] = PQSPI[4] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xEF);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F);
+ break;
+ case 2: /* QSPI_CS[2] = PTIMER[7] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0x7F);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF);
+ break;
+ case 3: /* QSPI_CS[3] = PTIMER[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0xF7);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F);
+ break;
+ }
+}
+#endif /* CONFIG_M5271 */
diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index f4523a3929..a7cda751bd 100644
--- a/drivers/spi/kirkwood_spi.c
+++ b/drivers/spi/kirkwood_spi.c
@@ -56,8 +56,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
writel(~KWSPI_CSN_ACT | KWSPI_SMEMRDY, &spireg->ctrl);
/* calculate spi clock prescaller using max_hz */
- data = ((CONFIG_SYS_TCLK / 2) / max_hz) & KWSPI_CLKPRESCL_MASK;
- data |= 0x10;
+ data = ((CONFIG_SYS_TCLK / 2) / max_hz) + 0x10;
+ data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data;
+ data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data;
/* program spi clock prescaller using max_hz */
writel(KWSPI_ADRLEN_3BYTE | data, &spireg->cfg);
diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c
index 44ab39dd3f..4e46041dff 100644
--- a/drivers/spi/mpc8xxx_spi.c
+++ b/drivers/spi/mpc8xxx_spi.c
@@ -124,6 +124,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
* len > 16 0
*/
+ spi->mode &= ~SPI_MODE_EN;
+
if (bitlen <= 16) {
if (bitlen <= 4)
spi->mode = (spi->mode & 0xff0fffff) |
@@ -138,6 +140,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
dout += 4;
}
+ spi->mode |= SPI_MODE_EN;
+
spi->tx = tmpdout; /* Write the data out */
debug("*** spi_xfer: ... %08x written\n", tmpdout);
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index 2e1531858d..13bebe8ac1 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -96,7 +96,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,
clk_src = mxc_get_clock(MXC_CSPI_CLK);
- div = clk_src / max_hz;
+ div = DIV_ROUND_UP(clk_src, max_hz);
div = get_cspi_div(div);
debug("clk %d Hz, div %d, real clk %d Hz\n",
@@ -147,7 +147,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,
* The following computation is taken directly from Freescale's code.
*/
if (clk_src > max_hz) {
- pre_div = clk_src / max_hz;
+ pre_div = DIV_ROUND_UP(clk_src, max_hz);
if (pre_div > 16) {
post_div = pre_div / 16;
pre_div = 15;
@@ -408,7 +408,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (bus >= ARRAY_SIZE(spi_bases))
return NULL;
- mxcs = malloc(sizeof(struct mxc_spi_slave));
+ mxcs = calloc(sizeof(struct mxc_spi_slave), 1);
if (!mxcs) {
puts("mxc_spi: SPI Slave not allocated !\n");
return NULL;
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 7859536a67..42e4c9952e 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -31,17 +31,31 @@
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
+#include <asm/arch/dma.h>
#define MXS_SPI_MAX_TIMEOUT 1000000
#define MXS_SPI_PORT_OFFSET 0x2000
#define MXS_SSP_CHIPSELECT_MASK 0x00300000
#define MXS_SSP_CHIPSELECT_SHIFT 20
+#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;
uint32_t mode;
- struct mx28_ssp_regs *regs;
+ struct mxs_ssp_regs *regs;
};
static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
@@ -67,7 +81,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
{
struct mxs_spi_slave *mxs_slave;
uint32_t addr;
- struct mx28_ssp_regs *ssp_regs;
+ struct mxs_ssp_regs *ssp_regs;
int reg;
if (!spi_cs_is_valid(bus, cs)) {
@@ -75,17 +89,20 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- mxs_slave = malloc(sizeof(struct mxs_spi_slave));
+ mxs_slave = calloc(sizeof(struct mxs_spi_slave), 1);
if (!mxs_slave)
return NULL;
+ if (mxs_dma_init_channel(bus))
+ goto err_init;
+
addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET);
mxs_slave->slave.bus = bus;
mxs_slave->slave.cs = cs;
mxs_slave->max_khz = max_hz / 1000;
mxs_slave->mode = mode;
- mxs_slave->regs = (struct mx28_ssp_regs *)addr;
+ mxs_slave->regs = (struct mxs_ssp_regs *)addr;
ssp_regs = mxs_slave->regs;
reg = readl(&ssp_regs->hw_ssp_ctrl0);
@@ -94,6 +111,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
writel(reg, &ssp_regs->hw_ssp_ctrl0);
return &mxs_slave->slave;
+
+err_init:
+ free(mxs_slave);
+ return NULL;
}
void spi_free_slave(struct spi_slave *slave)
@@ -105,10 +126,10 @@ void spi_free_slave(struct spi_slave *slave)
int spi_claim_bus(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
- struct mx28_ssp_regs *ssp_regs = mxs_slave->regs;
+ struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
uint32_t reg = 0;
- mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
+ mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0);
@@ -128,79 +149,63 @@ void spi_release_bus(struct spi_slave *slave)
{
}
-static void mxs_spi_start_xfer(struct mx28_ssp_regs *ssp_regs)
+static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)
{
writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);
writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr);
}
-static void mxs_spi_end_xfer(struct mx28_ssp_regs *ssp_regs)
+static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)
{
writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr);
writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);
}
-int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
+static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
+ char *data, int length, int write, unsigned long flags)
{
- struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
- struct mx28_ssp_regs *ssp_regs = mxs_slave->regs;
- int len = bitlen / 8;
- const char *tx = dout;
- char *rx = din;
- char dummy;
-
- if (bitlen == 0) {
- if (flags & SPI_XFER_END) {
- rx = &dummy;
- len = 1;
- } else
- return 0;
- }
-
- if (!rx && !tx)
- return 0;
+ struct mxs_ssp_regs *ssp_regs = slave->regs;
if (flags & SPI_XFER_BEGIN)
mxs_spi_start_xfer(ssp_regs);
- while (len--) {
+ while (length--) {
/* We transfer 1 byte */
writel(1, &ssp_regs->hw_ssp_xfer_size);
- if ((flags & SPI_XFER_END) && !len)
+ if ((flags & SPI_XFER_END) && !length)
mxs_spi_end_xfer(ssp_regs);
- if (tx)
+ if (write)
writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);
else
writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set);
writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set);
- if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
+ if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for start\n");
return -ETIMEDOUT;
}
- if (tx)
- writel(*tx++, &ssp_regs->hw_ssp_data);
+ if (write)
+ writel(*data++, &ssp_regs->hw_ssp_data);
writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set);
- if (rx) {
- if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
+ if (!write) {
+ if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for data\n");
return -ETIMEDOUT;
}
- *rx = readl(&ssp_regs->hw_ssp_data);
- rx++;
+ *data = readl(&ssp_regs->hw_ssp_data);
+ data++;
}
- if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
+ if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for finish\n");
return -ETIMEDOUT;
@@ -209,3 +214,174 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
return 0;
}
+
+static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
+ char *data, int length, int write, unsigned long flags)
+{
+ const int xfer_max_sz = 0xff00;
+ const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1;
+ struct mxs_ssp_regs *ssp_regs = slave->regs;
+ struct mxs_dma_desc *dp;
+ uint32_t ctrl0;
+ uint32_t cache_data_count;
+ const uint32_t dstart = (uint32_t)data;
+ int dmach;
+ int tl;
+ int ret = 0;
+
+ ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count);
+
+ memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count);
+
+ ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0);
+ ctrl0 |= SSP_CTRL0_DATA_XFER;
+
+ if (flags & SPI_XFER_BEGIN)
+ ctrl0 |= SSP_CTRL0_LOCK_CS;
+ if (!write)
+ ctrl0 |= SSP_CTRL0_READ;
+
+ if (length % ARCH_DMA_MINALIGN)
+ cache_data_count = roundup(length, ARCH_DMA_MINALIGN);
+ else
+ cache_data_count = length;
+
+ /* Flush data to DRAM so DMA can pick them up */
+ if (write)
+ flush_dcache_range(dstart, dstart + cache_data_count);
+
+ /* Invalidate the area, so no writeback into the RAM races with DMA */
+ invalidate_dcache_range(dstart, dstart + cache_data_count);
+
+ dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus;
+
+ dp = desc;
+ while (length) {
+ dp->address = (dma_addr_t)dp;
+ dp->cmd.address = (dma_addr_t)data;
+
+ /*
+ * This is correct, even though it does indeed look insane.
+ * I hereby have to, wholeheartedly, thank Freescale Inc.,
+ * for always inventing insane hardware and keeping me busy
+ * and employed ;-)
+ */
+ if (write)
+ dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
+ else
+ dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
+
+ /*
+ * The DMA controller can transfer large chunks (64kB) at
+ * time by setting the transfer length to 0. Setting tl to
+ * 0x10000 will overflow below and make .data contain 0.
+ * Otherwise, 0xff00 is the transfer maximum.
+ */
+ if (length >= 0x10000)
+ tl = 0x10000;
+ else
+ tl = min(length, xfer_max_sz);
+
+ dp->cmd.data |=
+ ((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) |
+ (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
+ MXS_DMA_DESC_HALT_ON_TERMINATE |
+ MXS_DMA_DESC_TERMINATE_FLUSH;
+
+ data += tl;
+ length -= tl;
+
+ if (!length) {
+ dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM;
+
+ if (flags & SPI_XFER_END) {
+ ctrl0 &= ~SSP_CTRL0_LOCK_CS;
+ ctrl0 |= SSP_CTRL0_IGNORE_CRC;
+ }
+ }
+
+ /*
+ * Write CTRL0, CMD0, CMD1, XFER_SIZE registers. It is
+ * 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;
+ dp->cmd.pio_words[1] = 0;
+ dp->cmd.pio_words[2] = 0;
+ dp->cmd.pio_words[3] = tl;
+
+ mxs_dma_desc_append(dmach, dp);
+
+ dp++;
+ }
+
+ if (mxs_dma_go(dmach))
+ ret = -EINVAL;
+
+ /* The data arrived into DRAM, invalidate cache over them */
+ if (!write)
+ invalidate_dcache_range(dstart, dstart + cache_data_count);
+
+ return ret;
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
+ struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
+ int len = bitlen / 8;
+ 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) {
+ din = (void *)&dummy;
+ len = 1;
+ } else
+ return 0;
+ }
+
+ /* Half-duplex only */
+ if (din && dout)
+ return -EINVAL;
+ /* No data */
+ if (!din && !dout)
+ return 0;
+
+ if (dout) {
+ data = (char *)dout;
+ write = 1;
+ } else if (din) {
+ data = (char *)din;
+ write = 0;
+ }
+
+ /*
+ * Check for alignment, if the buffer is aligned, do DMA transfer,
+ * PIO otherwise. This is a temporary workaround until proper bounce
+ * buffer is in place.
+ */
+ if (dma) {
+ if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1))
+ dma = 0;
+ if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1))
+ dma = 0;
+ }
+
+ if (!dma || (len < MXSSSP_SMALL_TRANSFER)) {
+ writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
+ return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags);
+ } else {
+ writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
+ return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags);
+ }
+}
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 9346c0b5b4..e40a632caa 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -86,15 +86,21 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
case 0:
ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
break;
+#ifdef OMAP3_MCSPI2_BASE
case 1:
ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
break;
+#endif
+#ifdef OMAP3_MCSPI3_BASE
case 2:
ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
break;
+#endif
+#ifdef OMAP3_MCSPI4_BASE
case 3:
ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
break;
+#endif
default:
printf("SPI error: unsupported bus %i. \
Supported busses 0 - 3\n", bus);
@@ -167,8 +173,18 @@ int spi_claim_bus(struct spi_slave *slave)
/* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
* REVISIT: this controller could support SPI_3WIRE mode.
*/
+#ifdef CONFIG_AM33XX
+ /*
+ * The reference design on AM33xx has D0 and D1 wired up opposite
+ * of how it has been done on previous platforms. We assume that
+ * custom hardware will also follow this convention.
+ */
+ conf &= OMAP3_MCSPI_CHCONF_DPE0;
+ conf |= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
+#else
conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
conf |= OMAP3_MCSPI_CHCONF_DPE0;
+#endif
/* wordlength */
conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h
index 0ac801cb25..bffa43cb6c 100644
--- a/drivers/spi/omap3_spi.h
+++ b/drivers/spi/omap3_spi.h
@@ -30,10 +30,15 @@
#ifndef _OMAP3_SPI_H_
#define _OMAP3_SPI_H_
+#ifdef CONFIG_AM33XX
+#define OMAP3_MCSPI1_BASE 0x48030100
+#define OMAP3_MCSPI2_BASE 0x481A0100
+#else
#define OMAP3_MCSPI1_BASE 0x48098000
#define OMAP3_MCSPI2_BASE 0x4809A000
#define OMAP3_MCSPI3_BASE 0x480B8000
#define OMAP3_MCSPI4_BASE 0x480BA000
+#endif
#define OMAP3_MCSPI_MAX_FREQ 48000000
diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra_spi.c
index 4a3e7996f9..18b00b2cae 100644
--- a/drivers/spi/tegra_spi.c
+++ b/drivers/spi/tegra_spi.c
@@ -54,7 +54,7 @@ static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave)
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
- /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */
+ /* Tegra20 SPI-Flash - only 1 device ('bus/cs') */
if (bus != 0 || cs != 0)
return 0;
else
@@ -72,9 +72,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- if (max_hz > TEGRA2_SPI_MAX_FREQ) {
+ if (max_hz > TEGRA_SPI_MAX_FREQ) {
printf("SPI error: unsupported frequency %d Hz. Max frequency"
- " is %d Hz\n", max_hz, TEGRA2_SPI_MAX_FREQ);
+ " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ);
return NULL;
}
@@ -86,7 +86,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
spi->slave.bus = bus;
spi->slave.cs = cs;
spi->freq = max_hz;
- spi->regs = (struct spi_tegra *)TEGRA2_SPI_BASE;
+ spi->regs = (struct spi_tegra *)NV_PA_SPI_BASE;
spi->mode = mode;
return &spi->slave;
@@ -130,7 +130,7 @@ int spi_claim_bus(struct spi_slave *slave)
debug("spi_init: COMMAND = %08x\n", readl(&regs->command));
/*
- * SPI pins on Tegra2 are muxed - change pinmux later due to UART
+ * SPI pins on Tegra20 are muxed - change pinmux later due to UART
* issue.
*/
pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH);
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index e563c19051..52a4134f18 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -78,7 +78,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct xilinx_spi_slave *xilspi;
- struct xilinx_spi_reg *regs;
if (!spi_cs_is_valid(bus, cs)) {
printf("XILSPI error: %s: unsupported bus %d / cs %d\n",