Sfoglia il codice sorgente

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>
Guennadi Liakhovetski 17 anni fa
parent
commit
38254f45b0
5 ha cambiato i file con 193 aggiunte e 2 eliminazioni
  1. 5 0
      README
  2. 1 0
      drivers/spi/Makefile
  3. 166 0
      drivers/spi/mxc_spi.c
  4. 6 1
      include/asm-arm/arch-mx31/mx31-regs.h
  5. 15 1
      include/spi.h

+ 5 - 0
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.

+ 1 - 0
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)

+ 166 - 0
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;
+}

+ 6 - 1
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

+ 15 - 1
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_ */