> From: Eric Nelson <eric.nel...@boundarydevices.com> > > Signed-off-by: Eric Nelson <eric.nel...@boundarydevices.com> > CC: Jason Liu <jason....@linaro.org> > CC: Stefano Babic <sba...@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__ */ _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot