Commit 38254f45 authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Wolfgang Denk

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: default avatarGuennadi Liakhovetski <lg@denx.de>
parent 248b7d98
......@@ -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.
......
......@@ -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)
......
/*
* 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;
}
......@@ -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
......
......@@ -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_ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment