summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/imx_spi.c329
-rw-r--r--drivers/spi/imx_spi_pmic.c127
3 files changed, 458 insertions, 0 deletions
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 824d8e7..0e8bf26 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -34,6 +34,8 @@ COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o
COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o
+COBJS-$(CONFIG_IMX_SPI) += imx_spi.o
+COBJS-$(CONFIG_IMX_SPI_PMIC) += imx_spi_pmic.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c
new file mode 100644
index 0000000..6146da9
--- /dev/null
+++ b/drivers/spi/imx_spi.c
@@ -0,0 +1,329 @@
+/*
+ * (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
+ */
+
+#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/imx_spi.h>
+
+#ifdef DEBUG
+
+/* -----------------------------------------------
+ * Helper functions to peek into tx and rx buffers
+ * ----------------------------------------------- */
+static const char * const hex_digit = "0123456789ABCDEF";
+
+static char quickhex(int i)
+{
+ return hex_digit[i];
+}
+
+static void memdump(const void *pv, int num)
+{
+
+}
+
+#else /* !DEBUG */
+
+#define memdump(p, n)
+
+#endif /* DEBUG */
+
+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_get_cfg(struct imx_spi_dev_t *dev)
+{
+ switch (dev->slave.cs) {
+ case 0:
+ /* pmic */
+ dev->base = CSPI1_BASE_ADDR;
+ dev->freq = 2500000;
+ dev->ss_pol = IMX_SPI_ACTIVE_HIGH;
+ dev->ss = 0;
+ dev->fifo_sz = 64 * 4;
+ dev->us_delay = 0;
+ break;
+ case 1:
+ /* spi_nor */
+ dev->base = CSPI1_BASE_ADDR;
+ dev->freq = 2500000;
+ dev->ss_pol = IMX_SPI_ACTIVE_LOW;
+ dev->ss = 1;
+ dev->fifo_sz = 64 * 4;
+ dev->us_delay = 0;
+ break;
+ default:
+ printf("Invalid Bus ID! \n");
+ break;
+ }
+
+ return 0;
+}
+
+static s32 spi_reset(struct spi_slave *slave)
+{
+ u32 clk_src = mxc_get_clock(MXC_CSPI_CLK);
+ s32 pre_div = 0, post_div = 0, i, 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);
+ /* Reset spi */
+ writel(0, dev->base + SPI_CON_REG);
+ writel((reg_ctrl | 0x1), dev->base + SPI_CON_REG);
+
+ /* Control register setup */
+ if (clk_src > dev->freq) {
+ pre_div = clk_src / dev->freq;
+ if (pre_div > 16) {
+ post_div = pre_div / 16;
+ pre_div = 15;
+ }
+ if (post_div != 0) {
+ for (i = 0; i < 16; i++) {
+ if ((1 << i) >= post_div)
+ break;
+ }
+ if (i == 16) {
+ printf("Error: no divider can meet the freq: %d\n",
+ dev->freq);
+ return -1;
+ }
+ post_div = i;
+ }
+ }
+
+ debug("pre_div = %d, post_div=%d\n", pre_div, post_div);
+ reg_ctrl = (reg_ctrl & ~(3 << 18)) | dev->ss << 18;
+ reg_ctrl = (reg_ctrl & ~(0xF << 12)) | pre_div << 12;
+ reg_ctrl = (reg_ctrl & ~(0xF << 8)) | post_div << 8;
+ reg_ctrl |= 1 << (dev->ss + 4); /* always set to master mode !!!! */
+ reg_ctrl &= ~0x1; /* disable spi */
+
+ reg_config = readl(dev->base + SPI_CFG_REG);
+ /* configuration register setup */
+ reg_config = (reg_config & ~(1 << ((dev->ss + 12)))) |
+ (dev->ss_pol << (dev->ss + 12));
+ reg_config = (reg_config & ~(1 << ((dev->ss + 20)))) |
+ (dev->in_sctl << (dev->ss + 20));
+ reg_config = (reg_config & ~(1 << ((dev->ss + 16)))) |
+ (dev->in_dctl << (dev->ss + 16));
+ reg_config = (reg_config & ~(1 << ((dev->ss + 8)))) |
+ (dev->ssctl << (dev->ss + 8));
+ reg_config = (reg_config & ~(1 << ((dev->ss + 4)))) |
+ (dev->sclkpol << (dev->ss + 4));
+ reg_config = (reg_config & ~(1 << ((dev->ss + 0)))) |
+ (dev->sclkpha << (dev->ss + 0));
+
+ debug("reg_ctrl = 0x%x\n", reg_ctrl);
+ writel(reg_ctrl, dev->base + SPI_CON_REG);
+ debug("reg_config = 0x%x\n", reg_config);
+ writel(reg_config, dev->base + SPI_CFG_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 *)malloc(sizeof(struct imx_spi_dev_t));
+ if (!imx_spi_slave)
+ return NULL;
+
+ imx_spi_slave->slave.bus = bus;
+ imx_spi_slave->slave.cs = cs;
+
+ spi_get_cfg(imx_spi_slave);
+
+ spi_io_init(imx_spi_slave);
+
+ 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 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)
+{
+ s32 val = SPI_RETRY_TIMES;
+ u32 *p_buf;
+ u32 reg;
+ s32 len = 0,
+ ret_val = 0;
+ s32 burst_bytes = bitlen >> 3;
+ s32 tmp = 0;
+ struct imx_spi_dev_t *dev = to_imx_spi_slave(slave);
+ struct spi_reg_t *spi_reg = &(dev->reg);
+
+ if (!slave)
+ return -1;
+
+ if (burst_bytes > (MAX_SPI_BYTES)) {
+ printf("Error: maximum burst size is 0x%x bytes, asking 0x%x\n",
+ MAX_SPI_BYTES, burst_bytes);
+ return -1;
+ }
+
+ if (flags & SPI_XFER_BEGIN) {
+ spi_cs_activate(slave);
+
+ 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) | \
+ ((burst_bytes * 8 - 1) << 20);
+
+ writel(spi_reg->ctrl_reg | 0x1, dev->base + SPI_CON_REG);
+ writel(spi_reg->cfg_reg, dev->base + SPI_CFG_REG);
+ debug("ctrl_reg=0x%x, cfg_reg=0x%x\n",
+ readl(dev->base + SPI_CON_REG),
+ readl(dev->base + SPI_CFG_REG));
+
+ /* move data to the tx fifo */
+ if (dout) {
+ for (p_buf = (u32 *)dout, len = burst_bytes; len > 0;
+ p_buf++, len -= 4)
+ writel(*p_buf, dev->base + SPI_TX_DATA);
+ } else {
+ for (len = burst_bytes; len > 0; len -= 4)
+ writel(tmp, dev->base + SPI_TX_DATA);
+ }
+
+ reg = readl(dev->base + SPI_CON_REG);
+ reg |= (1 << 2); /* set xch bit */
+ debug("control reg = 0x%08x\n", reg);
+ writel(reg, dev->base + SPI_CON_REG);
+
+ /* poll on the TC bit (transfer complete) */
+ while ((val-- > 0) &&
+ (readl(dev->base + SPI_STAT_REG) & (1 << 7)) == 0) {
+ udelay(100);
+ }
+
+ /* clear the TC bit */
+ writel(3 << 6, dev->base + SPI_STAT_REG);
+ if (val <= 0) {
+ printf("Error: re-tried %d times without response. Give up\n",
+ SPI_RETRY_TIMES);
+ ret_val = -1;
+ goto error;
+ }
+ }
+
+ /* move data in the rx buf */
+ if (flags & SPI_XFER_END) {
+ if (din) {
+ for (p_buf = (u32 *)din, len = burst_bytes; len > 0;
+ ++p_buf, len -= 4)
+ *p_buf = readl(dev->base + SPI_RX_DATA);
+ } else {
+ for (len = burst_bytes; len > 0; len -= 4)
+ tmp = readl(dev->base + SPI_RX_DATA);
+ }
+
+ spi_cs_deactivate(slave);
+ }
+
+ return ret_val;
+
+error:
+ spi_cs_deactivate(slave);
+ return ret_val;
+}
+
+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);
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct imx_spi_dev_t *dev = to_imx_spi_slave(slave);
+
+ writel(0, dev->base + SPI_CON_REG);
+}
+
diff --git a/drivers/spi/imx_spi_pmic.c b/drivers/spi/imx_spi_pmic.c
new file mode 100644
index 0000000..bce42c2
--- /dev/null
+++ b/drivers/spi/imx_spi_pmic.c
@@ -0,0 +1,127 @@
+/*
+ * (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
+ */
+
+#include <config.h>
+#include <common.h>
+#include <spi.h>
+#include <asm/errno.h>
+#include <linux/types.h>
+
+#include <asm/arch/imx_spi.h>
+
+static u32 pmic_tx, pmic_rx;
+
+/*!
+ * To read/write to a PMIC register. For write, it does another read for the
+ * actual register value.
+ *
+ * @param reg register number inside the PMIC
+ * @param val data to be written to the register; don't care for read
+ * @param write 0 for read; 1 for write
+ *
+ * @return the actual data in the PMIC register
+ */
+u32 pmic_reg(struct spi_slave *slave, u32 reg, u32 val, u32 write)
+{
+ if (!slave)
+ return 0;
+
+ if (reg > 63 || write > 1) {
+ printf("<reg num> = %d is invalide. Should be less then 63\n",
+ reg);
+ return 0;
+ }
+ pmic_tx = (write << 31) | (reg << 25) | (val & 0x00FFFFFF);
+ printf("reg=0x%x, val=0x%08x\n", reg, pmic_tx);
+
+ spi_xfer(slave, 4 << 3, (u8 *)&pmic_tx,
+ (u8 *)&pmic_rx, 0);
+
+ if (write) {
+ pmic_tx &= ~(1 << 31);
+ spi_xfer(slave, 4 << 3,
+ (u8 *)&pmic_tx, (u8 *)&pmic_rx, 0);
+ }
+
+ return pmic_rx;
+}
+
+void show_pmic_info(struct spi_slave *slave)
+{
+ volatile u32 rev_id;
+
+ if (!slave)
+ return;
+
+ rev_id = pmic_reg(slave, 7, 0, 0);
+ printf("PMIC ID: 0x%08x [Rev: ", rev_id);
+ switch (rev_id & 0x1F) {
+ case 0x1:
+ printf("1.0");
+ break;
+ case 0x9:
+ printf("1.1");
+ break;
+ case 0xA:
+ printf("1.2");
+ break;
+ case 0x10:
+ printf("2.0");
+ break;
+ case 0x11:
+ printf("2.1");
+ break;
+ case 0x18:
+ printf("3.0");
+ break;
+ case 0x19:
+ printf("3.1");
+ break;
+ case 0x1A:
+ printf("3.2");
+ break;
+ case 0x2:
+ printf("3.2A");
+ break;
+ case 0x1B:
+ printf("3.3");
+ break;
+ case 0x1D:
+ printf("3.5");
+ break;
+ default:
+ printf("unknown");
+ break;
+ }
+ printf("]\n");
+}
+
+struct spi_slave *spi_pmic_probe()
+{
+ return spi_setup_slave(0, CONFIG_IMX_SPI_PMIC_CS, 2500000, 0);
+}
+
+void spi_pmic_free(struct spi_slave *slave)
+{
+ if (slave)
+ spi_free_slave(slave);
+}