[U-Boot] [PATCH 1/2] SPI: Add i.MX ECSPI driver

Marek Vasut marek.vasut at gmail.com
Thu Jan 12 16:37:20 CET 2012


> From: Eric Nelson <eric.nelson at boundarydevices.com>
> 
> Signed-off-by: Eric Nelson <eric.nelson at boundarydevices.com>
> CC: Jason Liu <jason.hui at linaro.org>
> CC: Stefano Babic <sbabic at denx.de>
> ---
>  drivers/spi/Makefile    |    1 +
>  drivers/spi/imx_ecspi.c |  334
> +++++++++++++++++++++++++++++++++++++++++++++++ include/imx_spi.h       | 
>  97 ++++++++++++++
>  3 files changed, 432 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/spi/imx_ecspi.c
>  create mode 100644 include/imx_spi.h
> 
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index c967d87..e27ef41 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -33,6 +33,7 @@ COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o
>  COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o
>  COBJS-$(CONFIG_CF_SPI) += cf_spi.o
>  COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
> +COBJS-$(CONFIG_IMX_ECSPI) += imx_ecspi.o
>  COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
>  COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
>  COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
> diff --git a/drivers/spi/imx_ecspi.c b/drivers/spi/imx_ecspi.c
> new file mode 100644
> index 0000000..1468208
> --- /dev/null
> +++ b/drivers/spi/imx_ecspi.c
> @@ -0,0 +1,334 @@
> +/*
> + * (C) Copyright 2008-2010 Freescale Semiconductor, Inc.
> + *
> + * 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 <config.h>
> +#include <common.h>
> +#include <spi.h>
> +#include <asm/errno.h>
> +#include <linux/types.h>
> +#include <asm/io.h>
> +#include <malloc.h>
> +#include <asm/arch/clock.h>
> +#include <imx_spi.h>
> +
> +static inline struct imx_spi_dev_t *to_imx_spi_slave(struct spi_slave
> *slave) +{
> +	return container_of(slave, struct imx_spi_dev_t, slave);
> +}
> +
> +static s32 spi_reset(struct spi_slave *slave)
> +{
> +	u32 clk_src = mxc_get_clock(MXC_CSPI_CLK);
> +	u32 pre_div = 0, post_div = 0, reg_ctrl, reg_config;
> +	struct imx_spi_dev_t *dev = to_imx_spi_slave(slave);
> +	struct spi_reg_t *reg = &(dev->reg);
> +
> +	if (dev->freq == 0) {
> +		printf("Error: desired clock is 0\n");
> +		return 1;
> +	}
> +
> +	reg_ctrl = readl(dev->base + SPI_CON_REG);
> +	reg_config = readl(dev->base + SPI_CFG_REG);
> +	/* Reset spi */
> +	writel(0, dev->base + SPI_CON_REG);
> +	writel((reg_ctrl | 0x1), dev->base + SPI_CON_REG);
> +
> +	/* Control register setup */
> +	pre_div = (clk_src + dev->freq - 1) / dev->freq;
> +	while (pre_div > 16) {
> +		pre_div = (pre_div + 1) >> 1;
> +		post_div++;
> +	}
> +	if (post_div > 0x0f) {
> +		printf("Error: no divider can meet the freq: %d\n", dev->freq);
> +		return -1;
> +	}
> +	if (pre_div)
> +		pre_div--;
> +
> +	debug("pre_div = %d, post_div=%d, clk_src=%d, spi_freq=%d\n", pre_div,
> +			post_div, clk_src, (clk_src/(pre_div + 1)) >> post_div);
> +	reg_ctrl &= ~((3 << 18) | (0xF << 12) | (0xF << 8));

Magic numbers, fix globally

> +	reg_ctrl |= (dev->ss << 18);
> +	reg_ctrl |= (pre_div << 12);
> +	reg_ctrl |= (post_div << 8);
> +	reg_ctrl |= (1 << (dev->ss + 4));	/* always set to master mode */
> +	reg_ctrl |= 1;
> +
> +	/* configuration register setup */
> +	reg_config &= ~(0x111111 << dev->ss);
> +	reg_config |= (dev->in_sctl << (dev->ss + 20));
> +	reg_config |= (dev->in_dctl << (dev->ss + 16));
> +	reg_config |= (dev->ss_pol << (dev->ss + 12));
> +	reg_config |= (dev->ssctl << (dev->ss + 8));
> +	reg_config |= (dev->sclkpol << (dev->ss + 4));
> +	reg_config |= (dev->sclkpha << (dev->ss));
> +
> +	reg_config &= 0x0f << 12 ;
> +	reg_config |= (dev->ss_pol)<<(12+dev->ss);
> +	debug("ss%x, reg_config = 0x%x\n", dev->ss, reg_config);
> +	writel(reg_config, dev->base + SPI_CFG_REG);
> +	debug("ss%x, reg_ctrl = 0x%x\n", dev->ss, reg_ctrl);
> +	writel(reg_ctrl & ~1, dev->base + SPI_CON_REG);
> +
> +	/* save config register and control register */
> +	reg->cfg_reg  = reg_config;
> +	reg->ctrl_reg = reg_ctrl;
> +
> +	/* clear interrupt reg */
> +	writel(0, dev->base + SPI_INT_REG);
> +	writel(3 << 6, dev->base + SPI_STAT_REG);
> +	return 0;
> +}
> +
> +void spi_init(void)
> +{
> +}
> +
> +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
> +		unsigned int max_hz, unsigned int mode)
> +{
> +	struct imx_spi_dev_t *imx_spi_slave = NULL;
> +
> +	if (!spi_cs_is_valid(bus, cs))
> +		return NULL;
> +
> +	imx_spi_slave = (struct imx_spi_dev_t *)
> +			calloc(sizeof(struct imx_spi_dev_t), 1);
> +	if (!imx_spi_slave)
> +		return NULL;
> +
> +	imx_spi_slave->slave.bus = bus;
> +	imx_spi_slave->slave.cs = cs;
> +
> +	spi_get_cfg(imx_spi_slave);
> +
> +	if (max_hz < imx_spi_slave->freq)
> +		imx_spi_slave->freq = max_hz ;

" ;" <-- fix globally ... run tools/checkpatch.pl before submitting

> +	spi_io_init(imx_spi_slave, 0);
> +
> +	spi_reset(&(imx_spi_slave->slave));
> +
> +	return &(imx_spi_slave->slave);
> +}
> +
> +void spi_free_slave(struct spi_slave *slave)
> +{
> +	struct imx_spi_dev_t *imx_spi_slave;
> +
> +	if (slave) {
> +		imx_spi_slave = to_imx_spi_slave(slave);
> +		free(imx_spi_slave);
> +	}
> +}
> +
> +int spi_claim_bus(struct spi_slave *slave)
> +{
> +	return 0;
> +}
> +
> +void spi_release_bus(struct spi_slave *slave)
> +{
> +
> +}
> +
> +/*
> + * SPI xchg
> + */
> +
> +int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen,
> +	const u8 *dout, u8 *din, unsigned long flags)
> +{
> +	int nbytes = (bitlen + 7) / 8;
> +	struct imx_spi_dev_t *dev = to_imx_spi_slave(slave);
> +	struct spi_reg_t *spi_reg = &(dev->reg);
> +	u32 loop_cnt ;
> +	if (!slave)
> +		return -1;
> +
> +	if (spi_reg->ctrl_reg == 0) {
> +		printf("Error: spi(base=0x%x) has not been initialized yet\n",
> +				dev->base);
> +		return -1;
> +	}
> +	spi_reg->ctrl_reg = (spi_reg->ctrl_reg & ~0xFFF00000) | \
> +			((bitlen - 1) << 20);
> +
> +	writel(spi_reg->ctrl_reg, dev->base + SPI_CON_REG);
> +	writel(spi_reg->cfg_reg, dev->base + SPI_CFG_REG);
> +
> +	/* move data to the tx fifo */
> +	debug("dout=0x%p, bitlen=%x\n", dout, bitlen);
> +
> +	/*
> +	 * The SPI controller works only with words,
> +	 * check if less than a word is sent.
> +	 * Access to the FIFO is only 32 bit
> +	 */
> +	if (bitlen % 32) {
> +		u32 data = 0;
> +		u32 cnt = (bitlen % 32) / 8;
> +		if (dout) {
> +			int i ;
> +			for (i = 0; i < cnt; i++)
> +				data = (data << 8) | (*dout++ & 0xFF);
> +		}
> +		debug("Sending SPI 0x%x\n", data);
> +
> +		writel(data, dev->base + SPI_TX_DATA);
> +		nbytes -= cnt;
> +	}
> +
> +	while (nbytes > 0) {
> +		u32 data = 0;
> +		if (dout) {
> +			/* Buffer is not 32-bit aligned */
> +			if ((unsigned long)dout & 0x03) {
> +				int i ;
> +				data = 0;
> +				for (i = 0; i < 4; i++)
> +					data = (data << 8) | (*dout++ & 0xFF);
> +			} else {
> +				data = *(u32 *)dout;
> +				data = cpu_to_be32(data);
> +			}
> +			dout += 4;
> +		}
> +		debug("Sending SPI 0x%x\n", data);
> +		writel(data, dev->base + SPI_TX_DATA);
> +		nbytes -= 4;
> +	}
> +
> +	writel(spi_reg->ctrl_reg | (1 << 2), dev->base + SPI_CON_REG);
> +
> +	loop_cnt = SPI_RETRY_TIMES ;
> +	/* poll on the TC bit (transfer complete) */
> +	while ((readl(dev->base + SPI_STAT_REG) & (1 << 7)) == 0) {
> +		loop_cnt--;
> +		if (loop_cnt <= 0) {
> +			printf("%s: Error: re-tried %d times\n",
> +				__func__, SPI_RETRY_TIMES);
> +			bitlen = 0 ;
> +			spi_cs_deactivate(slave);
> +			return -1;
> +		}
> +		udelay(100);
> +	}
> +
> +	/* clear the TC bit */
> +	writel(3 << 6, dev->base + SPI_STAT_REG);
> +
> +	nbytes = (bitlen + 7) / 8;
> +
> +	if (bitlen % 32) {
> +		u32 data = readl(dev->base + SPI_RX_DATA);
> +		u32 cnt = (bitlen % 32) / 8;
> +		data = cpu_to_be32(data) >> ((sizeof(data) - cnt) * 8);
> +		debug("SPI Rx unaligned: 0x%x\n", data);
> +		if (din) {
> +			memcpy(din, &data, cnt);
> +			din += cnt;
> +		}
> +		nbytes -= cnt;
> +	}
> +
> +	while (nbytes > 0) {
> +		u32 tmp = readl(dev->base + SPI_RX_DATA);
> +		u32 data = cpu_to_be32(tmp);
> +		u32 cnt = min(nbytes, sizeof(data));

Don't define vars in the middle of code.

> +		debug("SPI Rx: 0x%x 0x%x\n", tmp, data);
> +		if (din) {
> +			memcpy(din, &data, cnt);
> +			din += cnt;
> +		}
> +		nbytes -= cnt;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * SPI transfer:
> + *
> + * See include/spi.h and
> http://www.altera.com/literature/ds/ds_nios_spi.pdf + * for more
> informations.
> + */
> +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void
> *dout, +		void *din, unsigned long flags)
> +{
> +	int n_bytes = (bitlen + 7) / 8;
> +	int n_bits;
> +	int ret;
> +	u32 blk_size;
> +	u8 *p_outbuf = (u8 *)dout;
> +	u8 *p_inbuf = (u8 *)din;
> +
> +	if (!slave)
> +		return -1;
> +
> +	if (flags & SPI_XFER_BEGIN)
> +		spi_cs_activate(slave);
> +
> +	while (n_bytes > 0) {
> +		if (n_bytes < MAX_SPI_BYTES)
> +			blk_size = n_bytes;
> +		else
> +			blk_size = MAX_SPI_BYTES;
> +
> +		n_bits = blk_size * 8;
> +
> +		ret = spi_xchg_single(slave, n_bits, p_outbuf, p_inbuf, 0);
> +
> +		if (ret)
> +			return ret;
> +		if (dout)
> +			p_outbuf += blk_size;
> +		if (din)
> +			p_inbuf += blk_size;
> +		n_bytes -= blk_size;
> +	}
> +
> +	if (flags & SPI_XFER_END)
> +		spi_cs_deactivate(slave);
> +
> +	return 0;
> +}
> +
> +int spi_cs_is_valid(unsigned int bus, unsigned int cs)
> +{
> +	return 1;
> +}
> +
> +void spi_cs_activate(struct spi_slave *slave)
> +{
> +	struct imx_spi_dev_t *dev = to_imx_spi_slave(slave);
> +
> +	spi_io_init(dev, 1);
> +}
> +
> +void spi_cs_deactivate(struct spi_slave *slave)
> +{
> +	struct imx_spi_dev_t *dev = to_imx_spi_slave(slave);
> +	spi_io_init(dev, 0);
> +	writel(0, dev->base + SPI_CON_REG);
> +}
> diff --git a/include/imx_spi.h b/include/imx_spi.h
> new file mode 100644
> index 0000000..32544d9
> --- /dev/null
> +++ b/include/imx_spi.h
> @@ -0,0 +1,97 @@
> +/*
> + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
> + *
> + * 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
> + */
> +
> +#ifndef __IMX_SPI_H__
> +#define __IMX_SPI_H__
> +
> +#include <spi.h>
> +
> +#undef IMX_SPI_DEBUG
> +
> +#define IMX_SPI_ACTIVE_HIGH     1
> +#define IMX_SPI_ACTIVE_LOW      0
> +#define SPI_RETRY_TIMES         100
> +
> +#if defined(IMX_CSPI_VER_0_7)
> +	#define	SPI_RX_DATA		0x0
> +	#define SPI_TX_DATA		0x4
> +	#define SPI_CON_REG		0x8
> +	#define SPI_INT_REG		0xC
> +	#define SPI_DMA_REG		0x10
> +	#define SPI_STAT_REG		0x14
> +	#define SPI_PERIOD_REG		0x18
> +
> +	#define SPI_CTRL_EN		(1 << 0)
> +	#define SPI_CTRL_MODE		(1 << 1)
> +	#define SPI_CTRL_REG_XCH_BIT	(1 << 2)
> +	#define SPI_CTRL_SSPOL		(1 << 7)
> +	#define SPI_CTRL_SSPOL_OFF	(7)
> +	#define SPI_CTRL_SSCTL		(1 << 6)
> +	#define SPI_CTRL_SSCTL_OFF	(6)
> +	#define SPI_CTRL_SCLK_POL	(1 << 4)
> +	#define SPI_CTRL_SCLK_POL_OFF	(4)
> +	#define SPI_CTRL_SCLK_PHA	(1 << 5)
> +	#define SPI_CTRL_SCLK_PHA_OFF	(5)
> +	#define SPI_CTRL_SS_OFF		(12)
> +	#define SPI_CTRL_SS_MASK	(3 << 12)
> +	#define SPI_CTRL_DATA_OFF	(16)
> +	#define SPI_CTRL_DATA_MASK	(7 << 16)
> +	#define SPI_CTRL_BURST_OFF	(20)
> +	#define SPI_CTRL_BURST_MASK	(0xFFF << 20)
> +	#define SPI_INT_STAT_TC		(1 << 7)
> +
> +#elif defined(IMX_CSPI_VER_2_3)
> +	#define	SPI_RX_DATA		0x0
> +	#define SPI_TX_DATA		0x4
> +	#define SPI_CON_REG		0x8
> +	#define SPI_CFG_REG		0xC
> +	#define SPI_INT_REG		0x10
> +	#define SPI_DMA_REG		0x14
> +	#define SPI_STAT_REG		0x18
> +	#define SPI_PERIOD_REG		0x1C

Use struct-based access
> +#endif
> +
> +struct spi_reg_t {
> +	u32 ctrl_reg;
> +	u32 cfg_reg;
> +};
> +
> +struct imx_spi_dev_t {

Drop the _t suffix

> +	struct spi_slave slave;
> +	u32 base;      /* base address of SPI module */
> +	u32 freq;      /* desired clock freq in Hz for this device */
> +	u32 ss_pol;    /* ss polarity: 1=active high; 0=active low */
> +	u32 ss;        /* slave select */
> +	u32 in_sctl;   /* inactive sclk ctl: 1=stay low; 0=stay high */
> +	u32 in_dctl;   /* inactive data ctl: 1=stay low; 0=stay high */
> +	u32 ssctl;     /* single burst mode vs multiple: 0=single; 1=multi */
> +	u32 sclkpol;   /* sclk polarity: active high=0; active low=1 */
> +	u32 sclkpha;   /* sclk phase: 0=phase 0; 1=phase1 */
> +	u32 fifo_sz;   /* fifo size in bytes for either tx or rx. */
> +	u32 us_delay;  /* us delay in each xfer */
> +	struct spi_reg_t reg; /* pointer to a set of SPI registers */
> +};
> +
> +extern void spi_io_init(struct imx_spi_dev_t *dev, int active);
> +extern s32 spi_get_cfg(struct imx_spi_dev_t *dev);
> +
> +#endif /* __IMX_SPI_H__ */


More information about the U-Boot mailing list