diff options
Diffstat (limited to 'drivers/spi/mxc_spi.c')
| -rw-r--r-- | drivers/spi/mxc_spi.c | 166 | 
1 files changed, 166 insertions, 0 deletions
| diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c new file mode 100644 index 000000000..b2e3ab9b6 --- /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; +} |