From 5001b99606655a6f91984afb54625e4af1ed1058 Mon Sep 17 00:00:00 2001 From: Terry Lv Date: Wed, 27 May 2009 21:43:40 +0800 Subject: ENGR00112845 spi nor boot and pmic support for BBG2. spi nor boot support for BBG2. Signed-off-by: Terry Lv --- drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/imx_spi_nor.c | 558 +++++++++++++++++++++++++++++++++++ drivers/mtd/spi/spi_flash_internal.h | 1 + drivers/spi/Makefile | 2 + drivers/spi/imx_spi.c | 329 +++++++++++++++++++++ drivers/spi/imx_spi_pmic.c | 127 ++++++++ 6 files changed, 1018 insertions(+) create mode 100644 drivers/mtd/spi/imx_spi_nor.c create mode 100644 drivers/spi/imx_spi.c create mode 100644 drivers/spi/imx_spi_pmic.c (limited to 'drivers') diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index e3e0292..ed23a76 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -32,6 +32,7 @@ COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o COBJS-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o +COBJS-$(CONFIG_SPI_FLASH_IMX) += imx_spi_nor.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/spi/imx_spi_nor.c b/drivers/mtd/spi/imx_spi_nor.c new file mode 100644 index 0000000..24af8b2 --- /dev/null +++ b/drivers/mtd/spi/imx_spi_nor.c @@ -0,0 +1,558 @@ +/* + * (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 +#include +#include +#include +#include +#include +#include + +#include +#include + +static u8 g_tx_buf[256]; +static u8 g_rx_buf[256]; + +#define WRITE_ENABLE(a) spi_nor_cmd_1byte(a, WREN) +#define WRITE_DISABLE(a) spi_nor_cmd_1byte(a, WRDI) +#define ENABLE_WRITE_STATUS(a) spi_nor_cmd_1byte(a, EWSR) + +struct imx_spi_flash_params { + u8 idcode1; + u32 block_size; + u32 block_count; + u32 device_size; + const char *name; +}; + +struct imx_spi_flash { + const struct imx_spi_flash_params *params; + struct spi_flash flash; +}; + +static inline struct imx_spi_flash * +to_imx_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct imx_spi_flash, flash); +} + +static const struct imx_spi_flash_params imx_spi_flash_table[] = { + { + .idcode1 = 0x25, + .block_size = SZ_64K, + .block_count = 32, + .device_size = SZ_64K * 32, + .name = "SST25VF016B - 2MB", + }, +}; + +static s32 spi_nor_flash_query(struct spi_flash *flash, void* data) +{ + u8 au8Tmp[4] = { 0 }; + u8 *pData = (u8 *)data; + + g_tx_buf[3] = JEDEC_ID; + + if (spi_xfer(flash->spi, (4 << 3), g_tx_buf, au8Tmp, + SPI_XFER_BEGIN | SPI_XFER_END)) { + return -1; + } + + printf("JEDEC ID: 0x%02x:0x%02x:0x%02x\n", + au8Tmp[2], au8Tmp[1], au8Tmp[0]); + + pData[0] = au8Tmp[2]; + pData[1] = au8Tmp[1]; + pData[2] = au8Tmp[0]; + + return 0; +} + +static s32 spi_nor_cmd_1byte(struct spi_flash *flash, u8 cmd) +{ + g_tx_buf[0] = cmd; + + if (spi_xfer(flash->spi, (1 << 3), g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(): %d\n", __func__, __LINE__); + return -1; + } + return 0; +} + +static s32 spi_nor_status(struct spi_flash *flash) +{ + g_tx_buf[1] = RDSR; + + if (spi_xfer(flash->spi, 2 << 3, g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(): %d\n", __func__, __LINE__); + return 0; + } + return g_rx_buf[0]; +} + +static int spi_nor_program_1byte(struct spi_flash *flash, + u8 data, void *addr) +{ + u32 addr_val = (u32)addr; + + /* need to do write-enable command */ + if (WRITE_ENABLE(flash) != 0) { + printf("Error : %d\n", __LINE__); + return -1; + } + g_tx_buf[0] = BYTE_PROG; /* need to skip bytes 1, 2, 3 */ + g_tx_buf[4] = data; + g_tx_buf[5] = addr_val & 0xFF; + g_tx_buf[6] = (addr_val >> 8) & 0xFF; + g_tx_buf[7] = (addr_val >> 16) & 0xFF; + + debug("0x%x: 0x%x\n", *(u32 *)g_tx_buf, *(u32 *)(g_tx_buf + 4)); + debug("addr=0x%x\n", addr_val); + + if (spi_xfer(flash->spi, 5 << 3, g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(%d): failed\n", __FILE__, __LINE__); + return -1; + } + + while (spi_nor_status(flash) & RDSR_BUSY) + ; + + return 0; +} + +/*! + * Write 'val' to flash WRSR (write status register) + */ +static int spi_nor_write_status(struct spi_flash *flash, u8 val) +{ + g_tx_buf[0] = val; + g_tx_buf[1] = WRSR; + + if (spi_xfer(flash->spi, 2 << 3, g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(): %d\n", __func__, __LINE__); + return -1; + } + return 0; +} + +/*! + * Erase a block_size data from block_addr offset in the flash + */ +static int spi_nor_erase_block(struct spi_flash *flash, + void *block_addr, u32 block_size) +{ + u32 *cmd = (u32 *)g_tx_buf; + u32 addr = (u32) block_addr; + + if (block_size != SZ_64K && + block_size != SZ_32K && + block_size != SZ_4K) { + printf("Error - block_size is not " + "4kB, 32kB or 64kB: 0x%x\n", + block_size); + return -1; + } + + if ((addr & (block_size - 1)) != 0) { + printf("Error - block_addr is not " + "4kB, 32kB or 64kB aligned: %p\n", + block_addr); + return -1; + } + + if (ENABLE_WRITE_STATUS(flash) != 0 || + spi_nor_write_status(flash, 0) != 0) { + printf("Error: %s: %d\n", __func__, __LINE__); + return -1; + } + + /* need to do write-enable command */ + if (WRITE_ENABLE(flash) != 0) { + printf("Error : %d\n", __LINE__); + return -1; + } + + if (block_size == SZ_64K) + *cmd = (ERASE_64K << 24) | (addr & 0x00FFFFFF); + else if (block_size == SZ_32K) + *cmd = (ERASE_32K << 24) | (addr & 0x00FFFFFF); + else if (block_size == SZ_4K) + *cmd = (ERASE_4K << 24) | (addr & 0x00FFFFFF); + + /* now do the block erase */ + if (spi_xfer(flash->spi, 4 << 3, g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + return -1; + } + + while (spi_nor_status(flash) & RDSR_BUSY) + ; + + return 0; +} + +static int spi_nor_flash_read(struct spi_flash *flash, u32 offset, + size_t len, void *buf) +{ + struct imx_spi_flash *imx_sf = to_imx_spi_flash(flash); + u32 *cmd = (u32 *)g_tx_buf; + u32 max_rx_sz = (MAX_SPI_BYTES) - 4; + u8 *d_buf = (u8 *)buf; + u8 *s_buf; + s32 s32remain_size = len; + int i; + + if (!(flash->spi)) + return -1; + + debug("%s(from flash=0x%08x to ram=%p len=0x%x)\n", + __func__, + offset, buf, len); + + if (len == 0) + return 0; + + *cmd = (READ << 24) | ((u32)offset & 0x00FFFFFF); + + for (; s32remain_size > 0; s32remain_size -= max_rx_sz, *cmd += max_rx_sz) { + debug("Addr:0x%p=>Offset:0x%08x, %d bytes transferred\n", + d_buf, + (*cmd & 0x00FFFFFF), + (len - s32remain_size)); + debug("%d%% completed\n", ((len - s32remain_size) * 100 / len)); + + if (s32remain_size < max_rx_sz) { + debug("100%% completed\n"); + + if (spi_xfer(flash->spi, (s32remain_size + 4) << 3, + g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(%d): failed\n", __FILE__, __LINE__); + return -1; + } + /* throw away 4 bytes (5th received bytes is real) */ + s_buf = g_rx_buf + 4; + + /* now adjust the endianness */ + for (i = s32remain_size; i >= 0; i -= 4, s_buf += 4) { + if (i < 4) { + if (i == 1) { + *d_buf = s_buf[0]; + } else if (i == 2) { + *d_buf++ = s_buf[1]; + *d_buf++ = s_buf[0]; + } else if (i == 3) { + *d_buf++ = s_buf[2]; + *d_buf++ = s_buf[1]; + *d_buf++ = s_buf[0]; + } + printf("\n"); + return 0; + } + /* copy 4 bytes */ + *d_buf++ = s_buf[3]; + *d_buf++ = s_buf[2]; + *d_buf++ = s_buf[1]; + *d_buf++ = s_buf[0]; + } + } + + /* now grab max_rx_sz data (+4 is + *needed due to 4-throw away bytes */ + if (spi_xfer(flash->spi, (max_rx_sz + 4) << 3, + g_tx_buf, g_rx_buf, SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(%d): failed\n", __FILE__, __LINE__); + return -1; + } + /* throw away 4 bytes (5th received bytes is real) */ + s_buf = g_rx_buf + 4; + /* now adjust the endianness */ + for (i = 0; i < max_rx_sz; i += 4, s_buf += 4) { + *d_buf++ = s_buf[3]; + *d_buf++ = s_buf[2]; + *d_buf++ = s_buf[1]; + *d_buf++ = s_buf[0]; + } + + if ((s32remain_size % imx_sf->params->block_size) == 0) + printf("."); + } + printf("\n"); + + return -1; +} + +static int spi_nor_flash_write(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) +{ + struct imx_spi_flash *imx_sf = to_imx_spi_flash(flash); + u32 d_addr = offset; + u8 *s_buf = (u8 *)buf; + s32 s32remain_size = len; + + if (!(flash->spi)) + return -1; + + if (len == 0) + return 0; + + write("%s(flash addr=0x%08x, ram=%p, len=0x%x)\n", + __func__, offset, buf, len); + + if (ENABLE_WRITE_STATUS(flash) != 0 || + spi_nor_write_status(flash, 0) != 0) { + printf("Error: %s: %d\n", __func__, __LINE__); + return -1; + } + + if ((d_addr & 1) != 0) { + /* program 1st byte */ + if (spi_nor_program_1byte(flash, s_buf[0], + (void *)d_addr) != 0) { + printf("Error: %s(%d)\n", __func__, __LINE__); + return -1; + } + if (--s32remain_size == 0) + return 0; + d_addr++; + s_buf++; + } + + /* need to do write-enable command */ + if (WRITE_ENABLE(flash) != 0) { + printf("Error : %d\n", __LINE__); + return -1; + } + + /* + These two bytes write will be copied to txfifo first with + g_tx_buf[1] being shifted out and followed by g_tx_buf[0]. + The reason for this is we will specify burst len=6. So SPI will + do this kind of data movement. + */ + g_tx_buf[0] = d_addr >> 16; + g_tx_buf[1] = AAI_PROG; /* need to skip bytes 1, 2 */ + /* byte shifted order is: 7, 6, 5, 4 */ + g_tx_buf[4] = s_buf[1]; + g_tx_buf[5] = s_buf[0]; + g_tx_buf[6] = d_addr; + g_tx_buf[7] = d_addr >> 8; + if (spi_xfer(flash->spi, 6 << 3, g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(%d): failed\n", + __FILE__, __LINE__); + return -1; + } + + while (spi_nor_status(flash) & RDSR_BUSY) + ; + + for (d_addr += 2, s_buf += 2, s32remain_size -= 2; + s32remain_size > 1; + d_addr += 2, s_buf += 2, s32remain_size -= 2) { + debug("%d%% transferred\n", + ((len - s32remain_size) * 100 / len)); + /* byte shifted order is: 2,1,0 */ + g_tx_buf[2] = AAI_PROG; + g_tx_buf[1] = s_buf[0]; + g_tx_buf[0] = s_buf[1]; + + if (spi_xfer(flash->spi, 3 << 3, g_tx_buf, g_rx_buf, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error: %s(%d): failed\n", + __FILE__, __LINE__); + return -1; + } + + while (spi_nor_status(flash) & RDSR_BUSY) + ; + + if ((s32remain_size % imx_sf->params->block_size) == 0) + printf("."); + } + printf("\n"); + debug("100%% transferred\n"); + + WRITE_DISABLE(flash); + while (spi_nor_status(flash) & RDSR_BUSY) + ; + + if (WRITE_ENABLE(flash) != 0) { + printf("Error : %d\n", __LINE__); + return -1; + } + + if (len == 1) { + /* need to do write-enable command */ + /* only 1 byte left */ + if (spi_nor_program_1byte(flash, s_buf[0], + (void *)d_addr) != 0) { + printf("Error: %s(%d)\n", + __func__, __LINE__); + return -1; + } + } + return 0; +} + +static int spi_nor_flash_erase(struct spi_flash *flash, u32 offset, + size_t len) +{ + struct imx_spi_flash *imx_sf = to_imx_spi_flash(flash); + s32 s32remain_size = len; + + if (!(flash->spi)) + return -1; + + if ((len % SZ_4K) != 0 || len == 0) { + printf("Error: size (0x%x) is not integer multiples of 4kB(0x1000)\n", + len); + return -1; + } + if ((offset & (SZ_4K - 1)) != 0) { + printf("Error - addr is not 4kB(0x1000) aligned: 0x%08x\n", + offset); + return -1; + } + for (; s32remain_size > 0; s32remain_size -= SZ_4K, offset += SZ_4K) { + debug("Erasing 0x%08x, %d%% erased\n", + offset, + ((len - s32remain_size) * 100 / len)); + if (spi_nor_erase_block(flash, + (void *)offset, SZ_4K) != 0) { + printf("Error: spi_nor_flash_erase(): %d\n", __LINE__); + return -1; + } + printf("."); + } + printf("\n"); + debug("100%% erased\n"); + return 0; +} + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi = NULL; + const struct imx_spi_flash_params *params = NULL; + struct imx_spi_flash *imx_sf = NULL; + u8 idcode[4] = { 0 }; + u32 i = 0; + s32 ret = 0; + + if (CONFIG_SPI_FLASH_CS != cs) { + printf("Invalid cs for SPI NOR.\n"); + return NULL; + } + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + + if (!spi) { + debug("SF: Failed to set up slave\n"); + return NULL; + } + + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + imx_sf = (struct imx_spi_flash *)malloc(sizeof(struct imx_spi_flash)); + + if (!imx_sf) { + debug("SF: Failed to allocate memory\n"); + spi_free_slave(spi); + return NULL; + } + + imx_sf->flash.spi = spi; + + /* Read the ID codes */ + ret = spi_nor_flash_query(&(imx_sf->flash), idcode); + if (ret) + goto err_read_id; + + for (i = 0; i < ARRAY_SIZE(imx_spi_flash_table); ++i) { + params = &imx_spi_flash_table[i]; + if (params->idcode1 == idcode[1]) + break; + } + + if (i == ARRAY_SIZE(imx_spi_flash_table)) { + debug("SF: Unsupported DataFlash ID %02x\n", + idcode[1]); + + goto err_invalid_dev; + } + + imx_sf->params = params; + + imx_sf->flash.name = params->name; + imx_sf->flash.size = params->device_size; + + imx_sf->flash.read = spi_nor_flash_read; + imx_sf->flash.write = spi_nor_flash_write; + imx_sf->flash.erase = spi_nor_flash_erase; + + debug("SF: Detected %s with block size %lu, " + "block count %lu, total %u bytes\n", + params->name, + params->block_size, + params->block_count, + params->device_size); + + return &(imx_sf->flash); + +err_read_id: + spi_release_bus(spi); +err_invalid_dev: + if (imx_sf) + free(imx_sf); +err_claim_bus: + if (spi) + spi_free_slave(spi); + return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ + struct imx_spi_flash *imx_sf = NULL; + + if (!flash) + return; + + imx_sf = to_imx_spi_flash(flash); + + if (flash->spi) { + spi_free_slave(flash->spi); + flash->spi = NULL; + } + + free(imx_sf); +} + diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 0612383..c4b561c 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -49,3 +49,4 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_imx(struct spi_slave *spi, u8 *idcode); 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 +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include +#include +#include +#include + +#include + +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(" = %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); +} -- cgit v1.1