aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMingjun Zhang <troy.zhangmingjun@huawei.com>2013-01-29 15:52:30 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-02-21 16:12:29 +0800
commitb6ce50d779dd46750c35a5c0d548ef212dec13dd (patch)
treed20c7ade008843988720f6ac03c193827aa47cab /drivers
parent2ec5131cc00286299f807c894cfda0cf0d87dc4d (diff)
mtd: HS SFC: add SFC driver for serial NOR flash
Hisilicon Serial NOR Flash controller provide three ways to access the serial NOR flash chip: by reg interface, mapping it to a range of bus address and using dma. This driver implement the dma read/write ops and reg erase ops. Signed-off-by: Mingjun Zhang <troy.zhangmingjun@huawei.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/devices/Kconfig11
-rw-r--r--drivers/mtd/devices/Makefile3
-rw-r--r--drivers/mtd/devices/hs_sfc/Makefile1
-rw-r--r--drivers/mtd/devices/hs_sfc/flash_info.c41
-rw-r--r--drivers/mtd/devices/hs_sfc/flash_info.h161
-rw-r--r--drivers/mtd/devices/hs_sfc/hisfc350.c860
-rw-r--r--drivers/mtd/devices/hs_sfc/hisfc350.h191
7 files changed, 1267 insertions, 1 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 46dcb54c32e..214e8481967 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -343,4 +343,15 @@ config MTD_DOCPROBE_55AA
LinuxBIOS or if you need to recover a DiskOnChip Millennium on which
you have managed to wipe the first block.
+config MTD_HS_SFC
+ tristate "hisilicon MTD serial NOR Support through SFC controller"
+ depends on ARCH_HS
+ default y
+ help
+ This enable SNOR support on hisilicon platforms using SFC controller.
+ Hisilicon SFC have three ways to access the serial NOR flash: using
+ reg interface, mapping flash-chip to a range of bus address, using DMA
+ Using DMA is more faster and convenient. this driver uses dma to
+ read/write flash, and use reg interface to control erasing it.
+
endmenu
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 395733a30ef..9de3c7ed6ae 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -20,5 +20,6 @@ obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
+obj-$(CONFIG_MTD_HS_SFC) += hs_sfc/
-CFLAGS_docg3.o += -I$(src) \ No newline at end of file
+CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/hs_sfc/Makefile b/drivers/mtd/devices/hs_sfc/Makefile
new file mode 100644
index 00000000000..62ec536ce94
--- /dev/null
+++ b/drivers/mtd/devices/hs_sfc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MTD_HS_SFC) += flash_info.o hisfc350.o
diff --git a/drivers/mtd/devices/hs_sfc/flash_info.c b/drivers/mtd/devices/hs_sfc/flash_info.c
new file mode 100644
index 00000000000..debbd160d4e
--- /dev/null
+++ b/drivers/mtd/devices/hs_sfc/flash_info.c
@@ -0,0 +1,41 @@
+/******************************************************************************
+* Copyright (c) 2009-2010 by HiC.
+* All rights reserved.
+* ***
+* Create by HiC 2010-12-18
+*
+******************************************************************************/
+#include "flash_info.h"
+
+/*****************************************************************************/
+char *hisfc_get_iftype_str(int iftype)
+{
+ static char *iftype_str[HISFC_IFTYPE_MAX] = {
+ "single",
+ "single",/* fast */
+ "dual",
+ "dual-addr",
+ "dual-cmd",
+ "quad",
+ "quad-addr",
+ "quad-cmd",
+ "invalid"
+ };
+
+ if (iftype >= HISFC_IFTYPE_MAX)
+ return iftype_str[HISFC_IFTYPE_MAX - 1];
+ else
+ return iftype_str[iftype];
+}
+/*****************************************************************************/
+const struct flash_info hisfc_ids_table[] = {
+ {"S25FL128P-0", 0x012018, 0x0300, 3, _256K, _256K * 64,
+ RD(FAST, 1, 104), WR(STD, 0, 104), ES(0, 104)},
+ {"S25FL128P-1", 0x012018, 0x0301, 3, _64K, (_64K * 256),
+ RD(FAST, 1, 104), WR(STD, 0, 104), ES(0, 104)},
+
+ /* make sure the last entry of the table is 0 filled, so probe code will
+ * know it's the end of the table
+ */
+ {0}
+};
diff --git a/drivers/mtd/devices/hs_sfc/flash_info.h b/drivers/mtd/devices/hs_sfc/flash_info.h
new file mode 100644
index 00000000000..19b71165a3d
--- /dev/null
+++ b/drivers/mtd/devices/hs_sfc/flash_info.h
@@ -0,0 +1,161 @@
+/******************************************************************************
+ * Copyright (c) 2009-2010 HiC.
+ * All rights reserved.
+ * ***
+ * HiC 2010-12-18
+ *
+ ******************************************************************************/
+
+#ifndef _FLASH_INFO_H
+#define _FLASH_INFO_H
+
+#include <linux/types.h>
+
+/*****************************************************************************/
+#define _1K (0x400)
+#define _2K (0x800)
+
+#define _4K (0x1000)
+#define _8K (0x2000)
+#define _16K (0x4000)
+#define _32K (0x8000)
+
+#define _64K (0x10000)
+#define _128K (0x20000)
+#define _256K (0x40000)
+#define _512K (0x80000)
+
+#define _1M (0x100000)
+#define _2M (0x200000)
+#define _4M (0x400000)
+#define _8M (0x800000)
+
+#define _16M (0x1000000)
+#define _32M (0x2000000)
+
+#define INFINITE (0xFFFFFFFF)
+/*****************************************************************************/
+/* sector erase, 64K */
+#define SPI_IF_ERASE_SECTOR (0x01)
+/* chip erase */
+#define SPI_IF_ERASE_CHIP (0x02)
+/* 4K */
+#define SPI_IF_ERASE_4K (0x04)
+/* 8K */
+#define SPI_IF_ERASE_8K (0x08)
+/*****************************************************************************/
+/* Write Enable */
+#define SPI_CMD_WREN 0x06
+/*---------------------------------------------------------------------------*/
+/* 4KB sector Erase */
+#define SPI_CMD_SE_4K 0x20
+/* 32KB sector Erase */
+#define SPI_CMD_SE_32K 0x52
+/* 64KB Sector Erase */
+#define SPI_CMD_SE 0xD8
+/* chip erase */
+#define SPI_CMD_BE 0xC7
+/*---------------------------------------------------------------------------*/
+/* Read Status Register */
+#define SPI_CMD_RDSR 0x05
+/* Read Identification */
+#define SPI_CMD_RDID 0x9F
+/*---------------------------------------------------------------------------*/
+/* Page Programming */
+#define SPI_CMD_WRITE_STD 0x02
+/* fast program dual input */
+#define SPI_CMD_WRITE_DUAL 0xA2
+/* fast program quad input */
+#define SPI_CMD_WRITE_QUAD 0x32
+/* Dual I/O High Performance Write */
+#define SPI_CMD_WRITE_DUAL_ADDR 0xD2
+/* Quad I/O High Performance Write */
+#define SPI_CMD_WRITE_QUAD_ADDR 0x12
+/*---------------------------------------------------------------------------*/
+/* Read Data bytes */
+#define SPI_CMD_READ_STD 0x03
+/* Read Data Bytes at Higher Speed */
+#define SPI_CMD_READ_FAST 0x0B
+/* fast read dual output */
+#define SPI_CMD_READ_DUAL 0x3B
+/* fast read quad output */
+#define SPI_CMD_READ_QUAD 0x6B
+/* Dual I/O High Performance Read */
+#define SPI_CMD_READ_DUAL_ADDR 0xBB
+/* Quad I/O High Performance Read */
+#define SPI_CMD_READ_QUAD_ADDR 0xEB
+/*---------------------------------------------------------------------------*/
+/* Write in Progress */
+#define SPI_CMD_SR_WIP 1
+/* Write Enable Latch */
+#define SPI_CMD_SR_WEL 2
+/*---------------------------------------------------------------------------*/
+/* enter to 4 bytes mode and set 4 byte bit as '1' */
+#define SPI_CMD_EN4B 0xB7
+/* exit 4 bytes mode and clear 4 byte bit as '0' */
+#define SPI_CMD_EX4B 0xE9
+/*---------------------------------------------------------------------------*/
+enum {
+ HISFC_IFTYPE_STD,
+#define HISFC_IFTYPE_STD HISFC_IFTYPE_STD
+ HISFC_IFTYPE_FAST,
+#define HISFC_IFTYPE_FAST HISFC_IFTYPE_FAST
+ HISFC_IFTYPE_DUAL,
+#define HISFC_IFTYPE_DUAL HISFC_IFTYPE_DUAL
+ HISFC_IFTYPE_DUAL_ADDR,
+#define HISFC_IFTYPE_DUAL_ADDR HISFC_IFTYPE_DUAL_ADDR
+ HISFC_IFTYPE_DUAL_CMD,
+#define HISFC_IFTYPE_DUAL_CMD HISFC_IFTYPE_DUAL_CMD
+ HISFC_IFTYPE_QUAD,
+#define HISFC_IFTYPE_QUAD HISFC_IFTYPE_QUAD
+ HISFC_IFTYPE_QUAD_ADDR,
+#define HISFC_IFTYPE_QUAD_ADDR HISFC_IFTYPE_QUAD_ADDR
+ HISFC_IFTYPE_QUAD_CMD,
+#define HISFC_IFTYPE_QUAD_CMD HISFC_IFTYPE_QUAD_CMD
+ HISFC_IFTYPE_INVALID,
+#define HISFC_IFTYPE_INVALID HISFC_IFTYPE_INVALID
+ HISFC_IFTYPE_MAX,
+#define HISFC_IFTYPE_MAX HISFC_IFTYPE_MAX
+};
+
+#define RD(iftype, dummy, clk) \
+{ HISFC_IFTYPE_##iftype, SPI_CMD_READ_##iftype, dummy, clk }
+
+#define WR(iftype, dummy, clk) \
+{ HISFC_IFTYPE_##iftype, SPI_CMD_WRITE_##iftype, dummy, clk }
+
+/* use sector erase */
+#define ES(dummy, clk) \
+{ HISFC_IFTYPE_STD, SPI_CMD_SE, dummy, clk }
+
+/*****************************************************************************/
+struct sfc_operation {
+ unsigned char iftype;
+ unsigned char cmd;
+ unsigned char dummy;
+ unsigned int clock;
+};
+
+struct flash_info {
+ char *name;
+ /* JEDEC id zero means "no ID" (most older chips); otherwise it has
+ * a high byte of zero plus three data bytes: the manufacturer id,
+ * then a two byte device id.
+ */
+ u32 jedec_id;
+ u16 ext_id;
+
+ u16 addr_width;
+ unsigned long erase_size;
+ unsigned long chip_size;
+
+ struct sfc_operation read;
+ struct sfc_operation write;
+ struct sfc_operation erase;
+};
+
+extern const struct flash_info hisfc_ids_table[];
+
+char *hisfc_get_iftype_str(int iftype);
+/******************************************************************************/
+#endif
diff --git a/drivers/mtd/devices/hs_sfc/hisfc350.c b/drivers/mtd/devices/hs_sfc/hisfc350.c
new file mode 100644
index 00000000000..97292e0546b
--- /dev/null
+++ b/drivers/mtd/devices/hs_sfc/hisfc350.c
@@ -0,0 +1,860 @@
+/*****************************************************************************
+ * Copyright (c) 2009-2011 by Hisi
+ * All rights reserved.
+ * ***
+ * Create by CCC 2010-09-01
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/setup.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/semaphore.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include "hisfc350.h"
+
+/*****************************************************************************/
+/* Don't change the follow config */
+#define HISFC350_SUPPORT_READ (SPI_IF_READ_STD \
+ | SPI_IF_READ_FAST \
+ | SPI_IF_READ_DUAL \
+ | SPI_IF_READ_DUAL_ADDR \
+ | SPI_IF_READ_QUAD \
+ | SPI_IF_READ_QUAD_ADDR)
+
+#define HISFC350_SUPPORT_WRITE (SPI_IF_WRITE_STD \
+ | SPI_IF_WRITE_DUAL \
+ | SPI_IF_WRITE_DUAL_ADDR \
+ | SPI_IF_WRITE_QUAD \
+ | SPI_IF_WRITE_QUAD_ADDR)
+
+#define HISFC350_SUPPORT_MAX_DUMMY (7)
+
+/* this function only for debug, reg read is slower then dma read */
+#undef HISFCV350_SUPPORT_REG_READ
+
+#define DRIVER_NAME "hi_sfc"
+
+/*****************************************************************************/
+static char *ultohstr(unsigned long long size, char *buffer)
+{
+ char *fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT", "%uT"};
+ int ix;
+
+ for (ix = 0; (ix < 5) && !(size & 0x3FF) && size; ix++)
+ size = (size >> 10);
+
+ sprintf(buffer, fmt[ix], size);
+ return buffer;
+}
+static int hisfc350_wait_ready(struct hisfc_host *host,
+ struct hisfc_flash_device *flash)
+{
+ unsigned long val;
+ unsigned long deadline = jiffies + HISFC350_MAX_READY_WAIT_JIFFIES;
+
+ do {
+ hisfc_write(HISFC350_CMD_INS, SPI_CMD_RDSR);
+ hisfc_write(HISFC350_CMD_CONFIG,
+ HISFC350_CMD_CONFIG_SEL_CS(flash->cs)
+ | HISFC350_CMD_CONFIG_DATA_CNT(1)
+ | HISFC350_CMD_CONFIG_DATA_EN
+ | HISFC350_CMD_CONFIG_RW_READ
+ | HISFC350_CMD_CONFIG_START);
+
+ HISFC350_CMD_WAIT_CPU_FINISH(host);
+
+ val = hisfc_read(HISFC350_CMD_DATABUF0);
+ if (!(val & SPI_CMD_SR_WIP))
+ return 0;
+
+ cond_resched();
+
+ } while (!time_after_eq(jiffies, deadline));
+
+ pr_info(KERN_ERR "Wait flash-dev ready timeout.\n");
+
+ return -EBUSY;
+}
+/*****************************************************************************/
+static void hisfc350_flash_set_4byte(struct hisfc_flash_device *flash,
+ int enable)
+{
+ struct hisfc_host *host = flash->priv;
+
+ if (enable) {
+ hisfc_write(HISFC350_CMD_INS, SPI_CMD_EN4B);
+ flash->addr_mode = addr_mode_4byte;
+ } else {
+ hisfc_write(HISFC350_CMD_INS, SPI_CMD_EX4B);
+ flash->addr_mode = addr_mode_3byte;
+ }
+
+ hisfc_write(HISFC350_CMD_CONFIG,
+ HISFC350_CMD_CONFIG_SEL_CS(flash->cs)
+ | HISFC350_CMD_CONFIG_START);
+
+ HISFC350_CMD_WAIT_CPU_FINISH(host);
+}
+
+/* config host to co-operate with the flash chip */
+static inline void hisfc350_host_select_flash(struct hisfc_host *host,
+ struct hisfc_flash_device *flash)
+{
+ unsigned int val;
+
+ if (host->addr_mode != flash->addr_mode) {
+ val = hisfc_read(HISFC350_GLOBAL_CONFIG);
+ if (flash->addr_mode == addr_mode_4byte)
+ val |= HISFC350_GLOBAL_CONFIG_ADDR_MODE_4B;
+ else
+ val &= ~HISFC350_GLOBAL_CONFIG_ADDR_MODE_4B;
+ hisfc_write(HISFC350_GLOBAL_CONFIG, val);
+ host->addr_mode = flash->addr_mode;
+ }
+
+ host->cs = flash->cs;
+}
+
+/*****************************************************************************/
+static void hisfc350_dma_bus_config(struct hisfc_host *host,
+ struct hisfc_flash_device *flash)
+{
+ unsigned int val = 0;
+
+ val |= HISFC350_BUS_CONFIG1_WRITE_INS(flash->info.write.cmd);
+ val |= HISFC350_BUS_CONFIG1_WRITE_DUMMY_CNT(flash->info.write.dummy);
+ val |= HISFC350_BUS_CONFIG1_WRITE_IF_TYPE(flash->info.write.iftype);
+
+ val |= HISFC350_BUS_CONFIG1_READ_PREF_CNT(0);
+ val |= HISFC350_BUS_CONFIG1_READ_INS(flash->info.read.cmd);
+ val |= HISFC350_BUS_CONFIG1_READ_DUMMY_CNT(flash->info.read.dummy);
+ val |= HISFC350_BUS_CONFIG1_READ_IF_TYPE(flash->info.read.iftype);
+
+ hisfc_write(HISFC350_BUS_CONFIG1, val);
+}
+/*****************************************************************************/
+static void hisfc350_map_iftype(struct hisfc_flash_device *flash)
+{
+ struct flash_info *info = &flash->info;
+ char iftype_hw_map[HISFC_IFTYPE_MAX] = {
+ [HISFC_IFTYPE_STD] = HISFC350_IF_HW_STD,
+ [HISFC_IFTYPE_FAST] = HISFC350_IF_HW_STD,
+ [HISFC_IFTYPE_DUAL] = HISFC350_IF_HW_DUAL,
+ [HISFC_IFTYPE_DUAL_ADDR] = HISFC350_IF_HW_DUAL_ADDR,
+ [HISFC_IFTYPE_DUAL_CMD] = HISFC350_IF_HW_DUAL_CMD,
+ [HISFC_IFTYPE_QUAD] = HISFC350_IF_HW_QUAD,
+ [HISFC_IFTYPE_QUAD_ADDR] = HISFC350_IF_HW_QUAD_ADDR,
+ [HISFC_IFTYPE_QUAD_CMD] = HISFC350_IF_HW_QUAD_CMD,
+ [HISFC_IFTYPE_INVALID] = HISFC_IFTYPE_INVALID,
+ };
+
+ if (info->read.iftype >= HISFC_IFTYPE_INVALID)
+ info->read.iftype = HISFC_IFTYPE_INVALID;
+ else
+ info->read.iftype = iftype_hw_map[info->read.iftype];
+
+ if (info->write.iftype >= HISFC_IFTYPE_INVALID)
+ info->write.iftype = HISFC_IFTYPE_INVALID;
+ else
+ info->write.iftype = iftype_hw_map[info->write.iftype];
+}
+/*****************************************************************************/
+static void hisfc350_dma_transfer(struct hisfc_host *host,
+ unsigned int spi_start_addr, dma_addr_t dma_buffer,
+ unsigned char is_read, unsigned int size, unsigned char cs)
+{
+ hisfc_write(HISFC350_BUS_DMA_MEM_SADDR, dma_buffer);
+
+ hisfc_write(HISFC350_BUS_DMA_FLASH_SADDR,
+ spi_start_addr);
+
+ hisfc_write(HISFC350_BUS_DMA_LEN,
+ HISFC350_BUS_DMA_LEN_DATA_CNT(size));
+
+ hisfc_write(HISFC350_BUS_DMA_AHB_CTRL,
+ HISFC350_BUS_DMA_AHB_CTRL_INCR4_EN
+ | HISFC350_BUS_DMA_AHB_CTRL_INCR8_EN
+ | HISFC350_BUS_DMA_AHB_CTRL_INCR16_EN);
+
+ hisfc_write(HISFC350_BUS_DMA_CTRL,
+ HISFC350_BUS_DMA_CTRL_RW(is_read)
+ | HISFC350_BUS_DMA_CTRL_CS(cs)
+ | HISFC350_BUS_DMA_CTRL_START);
+
+ HISFC350_DMA_WAIT_CPU_FINISH(host);
+
+}
+/*****************************************************************************/
+#ifdef HISFCV350_SUPPORT_REG_READ
+static char *hisfc350_reg_read_buf(struct hisfc_host *host,
+ struct hisfc_flash_device *flash, unsigned int spi_start_addr,
+ unsigned int size, unsigned char *buffer)
+{
+ int numread;
+ int index = 0;
+
+ hisfc_write(HISFC350_CMD_INS, flash->info.read.cmd);
+ hisfc_write(HISFC350_CMD_ADDR,
+ (spi_start_addr & HISFC350_CMD_ADDR_MASK));
+ hisfc_write(HISFC350_CMD_CONFIG,
+ HISFC350_CMD_CONFIG_MEM_IF_TYPE(flash->info.read.iftype)
+ | HISFC350_CMD_CONFIG_DATA_CNT(size)
+ | HISFC350_CMD_CONFIG_RW_READ
+ | HISFC350_CMD_CONFIG_DATA_EN
+ | HISFC350_CMD_CONFIG_DUMMY_CNT(flash->info.read.dummy)
+ | HISFC350_CMD_CONFIG_ADDR_EN
+ | HISFC350_CMD_CONFIG_SEL_CS(flash->cs)
+ | HISFC350_CMD_CONFIG_START);
+
+ HISFC350_CMD_WAIT_CPU_FINISH(host);
+
+ memcpy(buffer, host->io_base + HISFC350_CMD_DATABUF0, size);
+
+ return buffer;
+}
+/*****************************************************************************/
+static int hisfc350_reg_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int num;
+ int result = -EIO;
+ unsigned char *ptr = buf;
+ struct hisfc_host *host = MTD_TO_HOST(mtd);
+ struct hisfc_flash_device *flash = host->flash;
+
+ mutex_lock(&host->lock);
+
+ if (hisfc350_wait_ready(host, flash))
+ goto fail;
+ /* clk_round_rate(host->clk, flash->info.read.clock); */
+
+ while (len > 0) {
+ num = len > HISFC350_REG_BUF_SIZE ?
+ HISFC350_REG_BUF_SIZE : len;
+
+ hisfc350_reg_read_buf(host, flash, from, num, ptr);
+ from += num;
+ ptr += num;
+ len -= num;
+ }
+ result = 0;
+ *retlen = (size_t)(ptr - buf);
+fail:
+ mutex_unlock(&host->lock);
+ return result;
+}
+#endif /* HISFCV350_SUPPORT_REG_READ */
+/*****************************************************************************/
+
+static int hisfc350_dma_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int num;
+ int result = -EIO;
+ unsigned char *ptr = buf;
+ struct hisfc_flash_device *flash = mtd->priv;
+ struct hisfc_host *host = flash->priv;
+
+ mutex_lock(&host->lock);
+
+ hisfc350_host_select_flash(host, flash);
+
+ if (hisfc350_wait_ready(host, flash))
+ goto fail;
+ hisfc350_dma_bus_config(host, flash);
+
+ /* clk_round_rate(host->clk, flash->info.read.clock); */
+
+ /* when start addr is not aligned, sfc-dma engine will misfunction
+ * at the first aligned boudary. so we break the dma read ops to
+ * two parts: 1) read the not aligned part; 2) read the reset
+ */
+ if (from & HISFC350_DMA_ALIGN_MASK) {/* 1) not aligned part */
+ num = HISFC350_DMA_ALIGN_SIZE -
+ (from & HISFC350_DMA_ALIGN_MASK);
+ if (num > len)
+ num = len;
+
+ hisfc350_dma_transfer(host, from, host->dma_buffer, READ,
+ num, flash->cs);
+ memcpy(ptr, host->buffer, num);
+ from += num;
+ ptr += num;
+ len -= num;
+ }
+
+ while (len) {/* 2) the reset */
+ num = len > HISFC350_DMA_MAX_SIZE ? HISFC350_DMA_MAX_SIZE : len;
+ hisfc350_dma_transfer(host, from, host->dma_buffer, READ,
+ num, flash->cs);
+ memcpy(ptr, host->buffer, num);
+ ptr += num;
+ from += num;
+ len -= num;
+ }
+
+ result = 0;
+ *retlen = (size_t)(ptr - buf);
+fail:
+ mutex_unlock(&host->lock);
+ return result;
+}
+/*****************************************************************************/
+static int hisfc350_read_id(struct hisfc_host *host, int cs, u8 *id, int len)
+{
+ hisfc_write(HISFC350_CMD_INS, SPI_CMD_RDID);
+ hisfc_write(HISFC350_CMD_CONFIG,
+ HISFC350_CMD_CONFIG_SEL_CS(cs)
+ | HISFC350_CMD_CONFIG_RW_READ
+ | HISFC350_CMD_CONFIG_DATA_EN
+ | HISFC350_CMD_CONFIG_DATA_CNT(len)
+ | HISFC350_CMD_CONFIG_START);
+
+ HISFC350_CMD_WAIT_CPU_FINISH(host);
+
+ memcpy(id, host->io_base + HISFC350_CMD_DATABUF0, len);
+
+ return 0;
+}
+/*****************************************************************************/
+static int hisfc350_write_enable(struct hisfc_host *host,
+ struct hisfc_flash_device *flash)
+{
+ unsigned int val = 0;
+
+ hisfc_write(HISFC350_CMD_INS, SPI_CMD_WREN);
+
+ val = HISFC350_CMD_CONFIG_SEL_CS(flash->cs)
+ | HISFC350_CMD_CONFIG_START;
+ hisfc_write(HISFC350_CMD_CONFIG, val);
+
+ HISFC350_CMD_WAIT_CPU_FINISH(host);
+
+ return 0;
+}
+/*****************************************************************************/
+static int hisfc350_sector_erase(struct hisfc_host *host,
+ struct hisfc_flash_device *flash, u32 offset)
+{
+ if (hisfc350_wait_ready(host, flash))
+ return -EBUSY;
+ hisfc350_write_enable(host, flash);
+
+ /* clk_round_rate(host->clk, flash->info.erase.clock); */
+
+ hisfc_write(HISFC350_CMD_INS, flash->info.erase.cmd);
+
+ hisfc_write(HISFC350_CMD_ADDR, offset & HISFC350_CMD_ADDR_MASK);
+
+ hisfc_write(HISFC350_CMD_CONFIG,
+ HISFC350_CMD_CONFIG_SEL_CS(flash->cs)
+ | HISFC350_CMD_CONFIG_MEM_IF_TYPE(flash->info.erase.iftype)
+ | HISFC350_CMD_CONFIG_DUMMY_CNT(flash->info.erase.dummy)
+ | HISFC350_CMD_CONFIG_ADDR_EN
+ | HISFC350_CMD_CONFIG_START);
+
+ HISFC350_CMD_WAIT_CPU_FINISH(host);
+
+ return 0;
+}
+/*****************************************************************************/
+static int hisfc350_dma_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ int num;
+ int result = -EIO;
+
+ const u_char *ptr = buf;
+ struct hisfc_flash_device *flash = mtd->priv;
+ struct hisfc_host *host = flash->priv;
+
+ mutex_lock(&host->lock);
+ hisfc350_host_select_flash(host, flash);
+
+ if (hisfc350_wait_ready(host, flash))
+ goto fail;
+ hisfc350_write_enable(host, flash);
+ hisfc350_dma_bus_config(host, flash);
+
+ /* clk_round_rate(host->clk, flash->info.write.clock); */
+
+ /* see comment in dma_read */
+ if (to & HISFC350_DMA_ALIGN_MASK) {/* 1) not aligned part */
+ num = HISFC350_DMA_ALIGN_SIZE - (to & HISFC350_DMA_ALIGN_MASK);
+ if (num > len)
+ num = len;
+
+ memcpy(host->buffer, ptr, num);
+ hisfc350_dma_transfer(host, to, host->dma_buffer, WRITE,
+ num, flash->cs);
+ to += num;
+ ptr += num;
+ len -= num;
+ }
+
+ while (len > 0) {/* 2) the reset */
+ num = len >= HISFC350_DMA_MAX_SIZE
+ ? HISFC350_DMA_MAX_SIZE : len;
+
+ memcpy(host->buffer, ptr, num);
+ hisfc350_dma_transfer(host, to, host->dma_buffer, WRITE,
+ num, flash->cs);
+
+ to += num;
+ ptr += num;
+ len -= num;
+ }
+ *retlen = (size_t)(ptr - buf);
+ result = 0;
+fail:
+ mutex_unlock(&host->lock);
+ return result;
+}
+/*****************************************************************************/
+static int hisfc350_reg_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct hisfc_flash_device *flash = mtd->priv;
+ struct hisfc_host *host = flash->priv;
+ uint64_t addr = instr->addr;
+ uint64_t len = instr->len;
+ int ret;
+
+ mutex_lock(&host->lock);
+ hisfc350_host_select_flash(host, flash);
+
+ while (len) {
+ ret = hisfc350_sector_erase(host, flash, (u32)addr);
+ if (ret) {
+ instr->state = MTD_ERASE_FAILED;
+ mutex_unlock(&host->lock);
+ return ret;
+ }
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ mutex_unlock(&host->lock);
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+}
+/*****************************************************************************/
+static const struct flash_info *__devinit hisfc_flash_probe(
+ struct hisfc_host *host, int cs)
+{
+ int tmp;
+ u8 id[5];
+ u32 jedec;
+ u16 ext_jedec;
+ const struct flash_info *info;
+
+ /* JEDEC also defines an optional "extended device information"
+ * string for after vendor-specific data, after the three bytes
+ * we use here. Supporting some chips might require using it.
+ */
+ tmp = hisfc350_read_id(host, cs, id, 5);
+ if (tmp < 0) {
+ dev_dbg(host->dev, "error %d reading JEDEC ID\n", tmp);
+ return ERR_PTR(tmp);
+ }
+ jedec = id[0];
+ jedec = jedec << 8;
+ jedec |= id[1];
+ jedec = jedec << 8;
+ jedec |= id[2];
+
+ ext_jedec = id[3] << 8 | id[4];
+
+ /* make sure the last entry of the table is 0 filled */
+ for (tmp = 0; hisfc_ids_table[tmp].name; tmp++) {
+ info = &hisfc_ids_table[tmp];
+ if (info->jedec_id == jedec && info->ext_id == ext_jedec)
+ return info;
+ }
+
+ if (jedec == 0 || jedec == 0xffffff)
+ dev_info(host->dev, "no flash chip found on cs%d\n", cs);
+ else
+ dev_err(host->dev, "unrecognized JEDEC id %06x\n", jedec);
+
+ return ERR_PTR(-ENODEV);
+}
+static inline void dump_flash_info(struct hisfc_flash_device *flash)
+{
+ struct flash_info *info = &flash->info;
+ char buf1[20];
+ char buf2[20];
+
+ pr_info("SFC(cs%d): Name:\"%s\" Block:%sB Chip:%sB\n",
+ flash->cs,
+ info->name,
+ ultohstr(info->erase_size, buf1),
+ ultohstr(info->chip_size, buf2));
+
+ pr_info("addr_width:%sB read:%s,0x%02x,%dM\n",
+ info->addr_width == 3 ? "3" : "4",
+ hisfc_get_iftype_str(info->read.iftype),
+ info->read.cmd,
+ info->read.clock);
+
+ pr_info("write:%s,0x%02x,%dM\n",
+ hisfc_get_iftype_str(info->write.iftype),
+ info->write.cmd,
+ info->write.clock);
+
+ pr_info("erase:%s,0x%02x,%dM\n",
+ hisfc_get_iftype_str(info->erase.iftype),
+ info->erase.cmd,
+ info->erase.clock);
+}
+static int hisfc350_setup_flash_device(struct hisfc_host *host, int cs)
+{
+ struct hisfc_flash_device *flash;
+ const struct flash_info *info;
+ int ret;
+
+ info = hisfc_flash_probe(host, cs);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ flash = devm_kzalloc(host->dev, sizeof(*flash), GFP_ATOMIC);
+ if (!flash)
+ return -ENOMEM;
+
+ host->flash[cs] = flash;
+ flash->cs = cs;
+ flash->priv = host;
+ flash->mtd.priv = flash;
+ memcpy(&flash->info, info, sizeof(*info));
+ dump_flash_info(flash);
+ /*
+ * use a fixed name instead of flash node's to
+ * make it easier to config the cmdline partition,
+ * like mtdparts="hi_sfc:xxx"
+ */
+ flash->mtd.name = DRIVER_NAME;
+ flash->mtd.type = MTD_NORFLASH;
+ flash->mtd.size = info->chip_size;
+ flash->mtd.writesize = 1;
+ flash->mtd.erasesize = info->erase_size;
+ flash->mtd.flags = MTD_CAP_NORFLASH;
+ flash->mtd.owner = THIS_MODULE;
+ flash->mtd._erase = hisfc350_reg_erase;
+ flash->mtd._write = hisfc350_dma_write;
+ flash->mtd._read = hisfc350_dma_read;
+
+ hisfc350_map_iftype(flash);
+
+ if (info->addr_width == 4)
+ hisfc350_flash_set_4byte(flash, 1);
+
+ /* use the default cmdlinepart parser */
+ ret = mtd_device_parse_register(&flash->mtd, NULL, NULL, NULL, 0);
+ if (ret) {
+ dev_err(host->dev, "Err MTD partition=%d\n", ret);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static void hisfc350_hw_init(struct hisfc_host *host)
+{
+ /* ret = clk_prepare_enable(host->clk); */
+
+ hisfc_write(HISFC350_TIMING,
+ HISFC350_TIMING_TCSS(0x6)
+ | HISFC350_TIMING_TCSH(0x6)
+ | HISFC350_TIMING_TSHSL(0xf));
+}
+
+/*****************************************************************************/
+#ifdef CONFIG_PM
+/*****************************************************************************/
+static int hisfc350_driver_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct hisfc_host *host = platform_get_drvdata(pdev);
+ struct hisfc_flash_device *flash;
+ int cs;
+
+ /* see comment in suspend() */
+ for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) {
+ flash = host->flash[cs];
+ if (!flash)
+ continue;
+ if (flash->info.addr_width == 4)
+ hisfc350_flash_set_4byte(flash, 0);
+ }
+
+ /* clk_disable_unprepare(host->clk); */
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hisfc350_driver_resume(struct platform_device *pdev)
+{
+ struct hisfc_host *host = platform_get_drvdata(pdev);
+ struct hisfc_flash_device *flash;
+ int cs;
+
+ hisfc350_hw_init(host);
+
+ for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) {
+ flash = host->flash[cs];
+ if (!flash)
+ continue;
+ if (flash->info.addr_width == 4)
+ hisfc350_flash_set_4byte(flash, 1);
+ }
+
+ host->addr_mode = -1;
+ hisfc350_host_select_flash(host, host->flash[host->cs]);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+/*****************************************************************************/
+#ifdef CONFIG_OF
+static int __devinit hisfc_probe_config_dt(struct platform_device *pdev,
+ struct device_node *np)
+{
+ struct hisfc_host *host = platform_get_drvdata(pdev);
+ unsigned int version;
+
+ if (!np)
+ return -ENODEV;
+
+ /* TODO: get clk here */
+
+ if (!of_property_read_u32(np, "version_reg_offset", &version))
+ host->version = version;
+
+ return 0;
+}
+#else
+static int __devinit hisfc_probe_config_dt(struct platform_device *pdev,
+ struct device_node *np)
+{
+ return -ENOSYS;
+}
+#endif
+
+/*****************************************************************************/
+static inline void hisfc_check_version(struct hisfc_host *host)
+{
+ int version = hisfc_read(host->version);
+
+ pr_info("hs-sfc controller version 0x%x\n", version);
+}
+
+static int __devinit hisfc350_driver_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct hisfc_host *host;
+ struct hisfc_flash_device *flash;
+ struct resource *res;
+ int ret, tmp, cs;
+
+ if (np) {
+ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+ if (!host) {
+ pr_err("%s: ERROR: no memory", __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, host);
+ ret = hisfc_probe_config_dt(pdev, np);
+ if (ret) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "no platform data\n");
+ goto err_res;
+ }
+ } else {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "no device node found\n");
+ goto err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto err_res;
+ }
+ dev_dbg(dev, "res.start=0x%x, res.end=0x%x\n", res->start, res->end);
+
+ host->io_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!host->io_base) {
+ ret = -EIO;
+ dev_err(&pdev->dev, "devm_request_and_ioremap fail\n");
+ goto err_res;
+ }
+
+ host->dev = dev;
+ host->buffer = dma_alloc_coherent(host->dev, HISFC350_DMA_MAX_SIZE,
+ &host->dma_buffer, GFP_KERNEL);
+ if (!host->buffer) {
+ pr_err("sfc alloc dma buffer failed.\n");
+ goto err_res;
+ }
+ hisfc_check_version(host);
+#if 0
+ host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk)) {
+ ret = PTR_ERR(dev->clk);
+ goto free;
+ }
+
+ ret = clk_prepare_enable(host->clk);
+ if (ret)
+ goto err;
+#endif
+
+ mutex_init(&host->lock);
+ hisfc350_hw_init(host);
+
+ /* loop for each serial nor-flash which is connected to sfc */
+ for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) {
+ ret = hisfc350_setup_flash_device(host, cs);
+ if (ret == -ENODEV)
+ continue;
+ else if (ret < 0) {
+ dev_err(host->dev, "cs%d setup failed!", cs);
+ goto err_setup;
+ }
+ }
+
+ return 0;
+
+err_setup:
+ /* clean up for all nor flash */
+ for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) {
+ flash = host->flash[cs];
+ if (!flash)
+ continue;
+
+ /* clean up mtd stuff */
+ tmp = mtd_device_unregister(&flash->mtd);
+ if (tmp)
+ dev_err(&pdev->dev, "error removing mtd\n");
+ }
+
+ if (host->buffer)
+ dma_free_coherent(host->dev, HISFC350_DMA_MAX_SIZE,
+ host->buffer, host->dma_buffer);
+ mutex_destroy(&host->lock);
+err_res:
+ platform_set_drvdata(pdev, NULL);
+err:
+ return ret;
+}
+
+static int __devexit hisfc350_driver_remove(struct platform_device *pdev)
+{
+ struct hisfc_host *host = platform_get_drvdata(pdev);
+ struct hisfc_flash_device *flash;
+ int cs, ret;
+
+ /* clean up for all nor flash */
+ for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) {
+ flash = host->flash[cs];
+ if (!flash)
+ continue;
+
+ /* clean up mtd stuff */
+ ret = mtd_device_unregister(&flash->mtd);
+ if (ret)
+ dev_err(&pdev->dev, "error removing mtd\n");
+ }
+
+ if (host->buffer)
+ dma_free_coherent(host->dev, HISFC350_DMA_MAX_SIZE,
+ host->buffer, host->dma_buffer);
+ mutex_destroy(&host->lock);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static void hisfc350_driver_shutdown(struct platform_device *pdev)
+{
+ /* after system reboot, bootmonitor will read the flash using
+ * 3-byte address mode. Usually flash-chip will not reset with
+ * the soc-chip, so we have to command the flash-chip into 3-byte
+ * mode if necessary
+ */
+ struct hisfc_host *host = platform_get_drvdata(pdev);
+ struct hisfc_flash_device *flash;
+ int cs;
+
+ /* clean up for all nor flash */
+ for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) {
+ flash = host->flash[cs];
+ if (!flash)
+ continue;
+ if (flash->info.addr_width == 4)
+ hisfc350_flash_set_4byte(flash, 0);
+ }
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sfc_id_table[] = {
+ { .compatible = "hs,sfc350" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sfc_id_table);
+#endif
+
+static struct platform_driver hisfc350_driver_pltdrv = {
+ .probe = hisfc350_driver_probe,
+ .remove = hisfc350_driver_remove,
+ .shutdown = hisfc350_driver_shutdown,
+#ifdef CONFIG_PM
+ .suspend = hisfc350_driver_suspend,
+ .resume = hisfc350_driver_resume,
+#endif /* CONFIG_PM */
+
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(sfc_id_table),
+#endif
+ }
+};
+
+static int __init hisfc350_module_init(void)
+{
+ return platform_driver_register(&hisfc350_driver_pltdrv);
+}
+module_init(hisfc350_module_init);
+
+static void __exit hisfc350_module_exit(void)
+{
+ platform_driver_unregister(&hisfc350_driver_pltdrv);
+}
+module_exit(hisfc350_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("HiC");
+MODULE_DESCRIPTION("Hisilicon Spi Flash Controller V350 Device Driver, Version 1.00");
diff --git a/drivers/mtd/devices/hs_sfc/hisfc350.h b/drivers/mtd/devices/hs_sfc/hisfc350.h
new file mode 100644
index 00000000000..bbfb692f90b
--- /dev/null
+++ b/drivers/mtd/devices/hs_sfc/hisfc350.h
@@ -0,0 +1,191 @@
+/******************************************************************************
+ * Copyright (c) 2009-2010 by Hisi.
+ * All rights reserved.
+ * ***
+ * Create by CCCC. 2010-12-16
+ *
+ ******************************************************************************/
+
+#ifndef HISFC350H
+#define HISFC350H
+/******************************************************************************/
+#include "flash_info.h"
+
+#define CONFIG_HISFC350_CHIP_NUM (2)
+/*****************************************************************************/
+#define HISFC350_MAX_READY_WAIT_JIFFIES (40 * HZ)
+
+/*****************************************************************************/
+#define HISFC350_DMA_ALIGN_SIZE (256)
+#define HISFC350_DMA_ALIGN_MASK (HISFC350_DMA_ALIGN_SIZE - 1)
+
+#define HISFC350_DMA_MAX_SIZE (4096)
+#define HISFC350_DMA_MAX_MASK (HISFC350_DMA_MAX_SIZE - 1)
+
+/*****************************************************************************/
+#define HISFC350_GLOBAL_CONFIG 0x0100
+#define HISFC350_GLOBAL_CONFIG_READ_DELAY(n) (((n) & 0x03) << 3)
+#define HISFC350_GLOBAL_CONFIG_ADDR_MODE_4B (1 << 2)
+#define HISFC350_GLOBAL_CONFIG_WRITE_PROTECT (1 << 1)
+#define HISFC350_GLOBAL_CONFIG_SPI_MODE3 (1 << 0)
+
+#define HISFC350_TIMING 0x0110
+#define HISFC350_TIMING_TSHSL(n) ((n) & 0xF)
+#define HISFC350_TIMING_TCSS(n) (((n) & 0x7) << 8)
+#define HISFC350_TIMING_TCSH(n) (((n) & 0x7) << 12)
+
+#define HISFC350_INT_RAW_STATUS 0x0120
+#define HISFC350_INT_RAW_STATUS_DMA_DONE (1 << 1)
+#define HISFC350_INT_STATUS 0x0124
+#define HISFC350_INT_MASK 0x0128
+#define HISFC350_INT_CLEAR 0x012C
+#define HISFC350_INT_CLEAR_DMA_DONE (1 << 1)
+
+#define HISFC350_BUS_CONFIG1 0x0200
+#define HISFC350_BUS_CONFIG1_READ_EN (1 << 31)
+#define HISFC350_BUS_CONFIG1_WRITE_EN (1 << 30)
+#define HISFC350_BUS_CONFIG1_WRITE_INS(n) ((n & 0xFF) << 22)
+#define HISFC350_BUS_CONFIG1_WRITE_DUMMY_CNT(n) ((n & 0x7) << 19)
+#define HISFC350_BUS_CONFIG1_WRITE_IF_TYPE(n) ((n & 0x7) << 16)
+#define HISFC350_BUS_CONFIG1_READ_INS(n) ((n & 0xFF) << 8)
+#define HISFC350_BUS_CONFIG1_READ_PREF_CNT(n) ((n & 0x3) << 6)
+#define HISFC350_BUS_CONFIG1_READ_DUMMY_CNT(n) ((n & 0x7) << 3)
+#define HISFC350_BUS_CONFIG1_READ_IF_TYPE(n) (n & 0x7)
+
+#define HISFC350_BUS_FLASH_SIZE 0x0210
+#define HISFC350_BUS_FLASH_SIZE_CS0_MASK 0x0F
+#define HISFC350_BUS_FLASH_SIZE_CS1_MASK (0x0F << 8)
+#define HISFC350_BUS_BASE_ADDR_CS0 0x0214
+#define HISFC350_BUS_BASE_ADDR_CS1 0x0218
+#define HISFC350_BUS_ALIAS_ADDR 0x021C
+#define HISFC350_BUS_ALIAS_CS 0x0220
+#define HISFC350_BUS_DMA_CTRL 0x0240
+#define HISFC350_BUS_DMA_CTRL_START (1 << 0)
+#define HISFC350_BUS_DMA_CTRL_RW(_rw) ((_rw) << 1)
+#define HISFC350_BUS_DMA_CTRL_CS(_cs) (((_cs) & 0x01) << 4)
+
+#define HISFC350_BUS_DMA_MEM_SADDR 0x0244
+#define HISFC350_BUS_DMA_FLASH_SADDR 0x0248
+#define HISFC350_BUS_DMA_LEN 0x024C
+#define HISFC350_BUS_DMA_LEN_DATA_CNT(n) ((n - 1) & 0x0FFFFFFF)
+#define HISFC350_BUS_DMA_AHB_CTRL 0x0250
+#define HISFC350_BUS_DMA_AHB_CTRL_INCR4_EN (1 << 0)
+#define HISFC350_BUS_DMA_AHB_CTRL_INCR8_EN (1 << 1)
+#define HISFC350_BUS_DMA_AHB_CTRL_INCR16_EN (1 << 2)
+
+#define HISFC350_CMD_CONFIG 0x0300
+#define HISFC350_CMD_CONFIG_MEM_IF_TYPE(n) (((n) & 0x07) << 17)
+#define HISFC350_CMD_CONFIG_DATA_CNT(n) (((n - 1) & 0x3F) << 9)
+#define HISFC350_CMD_CONFIG_RW_READ (1 << 8)
+#define HISFC350_CMD_CONFIG_DATA_EN (1 << 7)
+#define HISFC350_CMD_CONFIG_DUMMY_CNT(n) (((n) & 0x07) << 4)
+#define HISFC350_CMD_CONFIG_ADDR_EN (1 << 3)
+#define HISFC350_CMD_CONFIG_SEL_CS(_cs) (((_cs) & 0x01) << 1)
+#define HISFC350_CMD_CONFIG_START (1 << 0)
+
+#define HISFC350_CMD_INS 0x0308
+#define HISFC350_CMD_ADDR 0x030C
+#define HISFC350_CMD_ADDR_MASK 0x3FFFFFFF
+#define HISFC350_CMD_DATABUF0 0x0400
+#define HISFC350_CMD_DATABUF15 0x043C
+
+/* hw iftype definition */
+#define HISFC350_IF_HW_STD 0
+#define HISFC350_IF_HW_DUAL 1
+#define HISFC350_IF_HW_DUAL_ADDR 2
+#define HISFC350_IF_HW_DUAL_CMD 3
+#define HISFC350_IF_HW_QUAD 5
+#define HISFC350_IF_HW_QUAD_ADDR 6
+#define HISFC350_IF_HW_QUAD_CMD 7
+
+#define HISFC350_REG_BUF_SIZE \
+ (HISFC350_CMD_DATABUF15 - HISFC350_CMD_DATABUF0 + 0x04)
+
+#undef READ
+#define READ 1
+
+#undef WRITE
+#define WRITE 0
+
+#undef FALSE
+#define FALSE 0
+
+#undef TRUE
+#define TRUE 1
+
+/*****************************************************************************/
+#define HISFC350_VERSION (0x01F8)
+
+enum {
+ addr_mode_3byte = 0,
+ addr_mode_4byte = 1,
+};
+
+struct hisfc_flash_device {
+ struct mtd_info mtd;
+ int cs;
+ int addr_mode;
+
+ void *priv; /* host */
+ struct flash_info info;
+};
+
+struct hisfc_host {
+ void __iomem *io_base;
+ int version;/* version reg */
+
+ struct clk *clk;
+ struct device *dev;
+
+ struct mutex lock;
+ int cs;/* current selected cs */
+ int addr_mode;/* current host work_mode */
+
+ char *buffer;
+ dma_addr_t dma_buffer;/* dma addr of buffer */
+
+ struct hisfc_flash_device *flash[CONFIG_HISFC350_CHIP_NUM];
+};
+
+/*****************************************************************************/
+#define hisfc_read(reg) readl(host->io_base + reg)
+
+#define hisfc_write(reg, val) writel(val, host->io_base + reg)
+
+#define HISFC350_CMD_WAIT_CPU_FINISH(_host) do {\
+ unsigned int timeout = 0x10000000; \
+ while (((hisfc_read(HISFC350_CMD_CONFIG) \
+ & HISFC350_CMD_CONFIG_START)) && timeout)\
+ --timeout; \
+ if (!timeout) \
+ DBG_BUG("cmd wait cpu finish timeout\n"); \
+} while (0)
+
+#define HISFC350_DMA_WAIT_CPU_FINISH(_host) do {\
+ unsigned int timeout = 0x10000000; \
+ while (((hisfc_read(HISFC350_BUS_DMA_CTRL) \
+ & HISFC350_BUS_DMA_CTRL_START)) && timeout) {\
+ --timeout; cond_resched(); } \
+ if (!timeout) \
+ DBG_BUG("dma wait cpu finish timeout\n");\
+} while (0)
+
+/*****************************************************************************/
+#if 0
+# define DBG_MSG(_fmt, arg...)
+#else
+# define DBG_MSG(_fmt, arg...) \
+ pr_info("%s(%d): " _fmt, __FILE__, __LINE__, ##arg);
+#endif
+
+#define DBG_WARN(_fmt, arg...) \
+ pr_info("%s(%d): " _fmt, __FILE__, __LINE__, ##arg);
+
+#define DBG_BUG(fmt, args...) do {\
+ pr_err("%s(%d): BUG: " fmt, __FILE__, __LINE__, ##args); \
+ while (1)\
+ ;\
+} while (0)
+
+/******************************************************************************/
+#endif /* HISFC350H */