summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <lg@denx.de>2008-04-15 14:14:25 +0200
committerWolfgang Denk <wd@denx.de>2008-04-18 00:43:23 -0700
commit38254f45b0b412332726c90d3184ad47479fcffb (patch)
tree927798cfb0fb9f53e461071b63af16ebdc0b5912
parent248b7d984cffa3107b5acb4c3f5501b7538d877a (diff)
New i.MX31 SPI driver
This is an SPI driver for i.MX and MXC based SoCs from Freescale. So far only implemented and tested on i.MX31, can with a modified register layout and definitions be used for i.MX27, I think, MXC CPUs have similar SPI controllers too. Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
-rw-r--r--README5
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/mxc_spi.c166
-rw-r--r--include/asm-arm/arch-mx31/mx31-regs.h7
-rw-r--r--include/spi.h16
5 files changed, 193 insertions, 2 deletions
diff --git a/README b/README
index 3a10bd762b5..956c694faf2 100644
--- a/README
+++ b/README
@@ -1414,6 +1414,11 @@ The following options need to be configured:
Currently supported on some MPC8xxx processors. For an
example, see include/configs/mpc8349emds.h.
+ CONFIG_MXC_SPI
+
+ Enables the driver for the SPI controllers on i.MX and MXC
+ SoCs. Currently only i.MX31 is supported.
+
- FPGA Support: CONFIG_FPGA
Enables FPGA subsystem.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 0b7a2dfd3b2..bc8a1041210 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_MXC_SPI) += mxc_spi.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
new file mode 100644
index 00000000000..b2e3ab9b676
--- /dev/null
+++ b/drivers/spi/mxc_spi.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2008, Guennadi Liakhovetski <lg@denx.de>
+ *
+ * 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 <asm/io.h>
+
+#ifdef CONFIG_MX27
+/* i.MX27 has a completely wrong register layout and register definitions in the
+ * datasheet, the correct one is in the Freescale's Linux driver */
+
+#error "i.MX27 CSPI not supported due to drastic differences in register definisions" \
+"See linux mxc_spi driver from Freescale for details."
+
+#else
+
+#define MXC_CSPIRXDATA 0x00
+#define MXC_CSPITXDATA 0x04
+#define MXC_CSPICTRL 0x08
+#define MXC_CSPIINT 0x0C
+#define MXC_CSPIDMA 0x10
+#define MXC_CSPISTAT 0x14
+#define MXC_CSPIPERIOD 0x18
+#define MXC_CSPITEST 0x1C
+#define MXC_CSPIRESET 0x00
+
+#define MXC_CSPICTRL_EN (1 << 0)
+#define MXC_CSPICTRL_MODE (1 << 1)
+#define MXC_CSPICTRL_XCH (1 << 2)
+#define MXC_CSPICTRL_SMC (1 << 3)
+#define MXC_CSPICTRL_POL (1 << 4)
+#define MXC_CSPICTRL_PHA (1 << 5)
+#define MXC_CSPICTRL_SSCTL (1 << 6)
+#define MXC_CSPICTRL_SSPOL (1 << 7)
+#define MXC_CSPICTRL_CHIPSELECT(x) (((x) & 0x3) << 24)
+#define MXC_CSPICTRL_BITCOUNT(x) (((x) & 0x1f) << 8)
+#define MXC_CSPICTRL_DATARATE(x) (((x) & 0x7) << 16)
+
+#define MXC_CSPIPERIOD_32KHZ (1 << 15)
+
+static unsigned long spi_bases[] = {
+ 0x43fa4000,
+ 0x50010000,
+ 0x53f84000,
+};
+
+static unsigned long spi_base;
+
+#endif
+
+spi_chipsel_type spi_chipsel[] = {
+ (spi_chipsel_type)0,
+ (spi_chipsel_type)1,
+ (spi_chipsel_type)2,
+ (spi_chipsel_type)3,
+};
+int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]);
+
+static inline u32 reg_read(unsigned long addr)
+{
+ return *(volatile unsigned long*)addr;
+}
+
+static inline void reg_write(unsigned long addr, u32 val)
+{
+ *(volatile unsigned long*)addr = val;
+}
+
+static u32 spi_xchg_single(u32 data, int bitlen)
+{
+
+ unsigned int cfg_reg = reg_read(spi_base + MXC_CSPICTRL);
+
+ if (MXC_CSPICTRL_BITCOUNT(bitlen - 1) != (cfg_reg & MXC_CSPICTRL_BITCOUNT(31))) {
+ cfg_reg = (cfg_reg & ~MXC_CSPICTRL_BITCOUNT(31)) |
+ MXC_CSPICTRL_BITCOUNT(bitlen - 1);
+ reg_write(spi_base + MXC_CSPICTRL, cfg_reg);
+ }
+
+ reg_write(spi_base + MXC_CSPITXDATA, data);
+
+ cfg_reg |= MXC_CSPICTRL_XCH;
+
+ reg_write(spi_base + MXC_CSPICTRL, cfg_reg);
+
+ while (reg_read(spi_base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH)
+ ;
+
+ return reg_read(spi_base + MXC_CSPIRXDATA);
+}
+
+int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din)
+{
+ int n_blks = (bitlen + 31) / 32;
+ u32 *out_l, *in_l;
+ int i;
+
+ if ((int)dout & 3 || (int)din & 3) {
+ printf("Error: unaligned buffers in: %p, out: %p\n", din, dout);
+ return 1;
+ }
+
+ if (!spi_base)
+ spi_select(CONFIG_MXC_SPI_IFACE, (int)chipsel, SPI_MODE_2 | SPI_CS_HIGH);
+
+ for (i = 0, in_l = (u32 *)din, out_l = (u32 *)dout;
+ i < n_blks;
+ i++, in_l++, out_l++, bitlen -= 32)
+ *in_l = spi_xchg_single(*out_l, bitlen);
+
+ return 0;
+}
+
+void spi_init(void)
+{
+}
+
+int spi_select(unsigned int bus, unsigned int dev, unsigned long mode)
+{
+ unsigned int ctrl_reg;
+
+ if (bus >= sizeof(spi_bases) / sizeof(spi_bases[0]) ||
+ dev > 3)
+ return 1;
+
+ spi_base = spi_bases[bus];
+
+ ctrl_reg = MXC_CSPICTRL_CHIPSELECT(dev) |
+ MXC_CSPICTRL_BITCOUNT(31) |
+ MXC_CSPICTRL_DATARATE(7) | /* FIXME: calculate data rate */
+ MXC_CSPICTRL_EN |
+ MXC_CSPICTRL_MODE;
+
+ if (mode & SPI_CPHA)
+ ctrl_reg |= MXC_CSPICTRL_PHA;
+ if (!(mode & SPI_CPOL))
+ ctrl_reg |= MXC_CSPICTRL_POL;
+ if (mode & SPI_CS_HIGH)
+ ctrl_reg |= MXC_CSPICTRL_SSPOL;
+
+ reg_write(spi_base + MXC_CSPIRESET, 1);
+ udelay(1);
+ reg_write(spi_base + MXC_CSPICTRL, ctrl_reg);
+ reg_write(spi_base + MXC_CSPIPERIOD,
+ MXC_CSPIPERIOD_32KHZ);
+ reg_write(spi_base + MXC_CSPIINT, 0);
+
+ return 0;
+}
diff --git a/include/asm-arm/arch-mx31/mx31-regs.h b/include/asm-arm/arch-mx31/mx31-regs.h
index 380b401a421..d04072e6727 100644
--- a/include/asm-arm/arch-mx31/mx31-regs.h
+++ b/include/asm-arm/arch-mx31/mx31-regs.h
@@ -37,6 +37,9 @@
#define CCM_UPCTL (CCM_BASE + 0x10)
#define CCM_SPCTL (CCM_BASE + 0x18)
#define CCM_COSR (CCM_BASE + 0x1C)
+#define CCM_CGR0 (CCM_BASE + 0x20)
+#define CCM_CGR1 (CCM_BASE + 0x24)
+#define CCM_CGR2 (CCM_BASE + 0x28)
#define CCMR_MDS (1 << 7)
#define CCMR_SBYCS (1 << 4)
@@ -118,7 +121,9 @@
#define MUX_CTL_RXD1 0x82
#define MUX_CTL_TXD1 0x83
#define MUX_CTL_CSPI2_MISO 0x84
-/* 0x85 .. 0x8a */
+#define MUX_CTL_CSPI2_SS0 0x85
+#define MUX_CTL_CSPI2_SS1 0x86
+#define MUX_CTL_CSPI2_SS2 0x87
#define MUX_CTL_CSPI2_MOSI 0x8b
/* The modes a specific pin can be in
diff --git a/include/spi.h b/include/spi.h
index 03dc5bc0368..3a55a68c4d1 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -24,6 +24,18 @@
#ifndef _SPI_H_
#define _SPI_H_
+/* SPI mode flags */
+#define SPI_CPHA 0x01 /* clock phase */
+#define SPI_CPOL 0x02 /* clock polarity */
+#define SPI_MODE_0 (0|0) /* (original MicroWire) */
+#define SPI_MODE_1 (0|SPI_CPHA)
+#define SPI_MODE_2 (SPI_CPOL|0)
+#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
+#define SPI_CS_HIGH 0x04 /* chipselect active high? */
+#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
+#define SPI_3WIRE 0x10 /* SI/SO signals shared */
+#define SPI_LOOP 0x20 /* loopback mode */
+
/*
* The function call pointer type used to drive the chip select.
*/
@@ -68,6 +80,8 @@ void spi_init(void);
*
* Returns: 0 on success, not 0 on failure
*/
-int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din);
+int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din);
+
+int spi_select(unsigned int bus, unsigned int dev, unsigned long mode);
#endif /* _SPI_H_ */