diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c4fb46a..12b2477 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -381,6 +381,15 @@ config SUN5I_SPI_NDMA
           This selects SPI DMA mode with DMA transfer
           Y select NDMA mode and N select DDMA mode
 
+config SPI_SUN7I
+	tristate "SUN7I SPI Controller"
+	depends on ARCH_SUN7I
+	help
+		Allwinner Soc SPI controller,present on SUN7I chips.
+		Different DMA Architecture than Sun4i
+
+
+
 config SPI_TEGRA
 	tristate "Nvidia Tegra SPI controller"
 	depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index a13913f..8e9bb71 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_SPI_TXX9)			+= spi-txx9.o
 obj-$(CONFIG_SPI_XILINX)		+= spi-xilinx.o
 obj-$(CONFIG_SPI_SUNXI)			+= spi_sunxi.o
+obj-$(CONFIG_SPI_SUN7I)			+= spi-sun7i.o
 
diff --git a/drivers/spi/spi-sun7i.c b/drivers/spi/spi-sun7i.c
new file mode 100644
index 0000000..52c9d09
--- /dev/null
+++ b/drivers/spi/spi-sun7i.c
@@ -0,0 +1,2226 @@
+/*
+ * drivers/spi/spi-sun7i.c
+ * Copyright (C) 2012 - 2016 Reuuimlla Limited
+ * Pan Nan <pannan@reuuimllatech.com>
+ * James Deng <csjamesdeng@reuuimllatech.com>
+ *
+ * SUN7I SPI Controller Driver
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <plat/sys_config.h>
+#include <mach/spi.h>
+
+#define SPI_DEBUG_LEVEL 2
+#define CONFIG_SUN7I_SPI_NDMA
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+
+#if (SPI_DEBUG_LEVEL == 1)
+    #define spi_dbg(format,args...)     do {} while (0)
+    #define spi_inf(format,args...)     do {} while (0)
+    #define spi_err(format,args...)     printk(KERN_ERR "[spi-err] "format,##args)
+#elif (SPI_DEBUG_LEVEL == 2)
+    #define spi_dbg(format,args...)     do {} while (0)
+    #define spi_inf(format,args...)     printk(KERN_INFO"[spi-inf] "format,##args)
+    #define spi_err(format,args...)     printk(KERN_ERR "[spi-err] "format,##args)
+#elif (SPI_DEBUG_LEVEL == 3)
+    #define spi_dbg(format,args...)     printk(KERN_INFO"[spi-dbg] "format,##args)
+    #define spi_inf(format,args...)     printk(KERN_INFO"[spi-inf] "format,##args)
+    #define spi_err(format,args...)     printk(KERN_ERR "[spi-err] "format,##args)
+#endif
+
+enum spi_duplex_flag {
+    HALF_DUPLEX_RX,                     // half duplex read
+    HALF_DUPLEX_TX,                     // half duplex write
+    FULL_DUPLEX_RX_TX,                  // full duplex read and write
+    DUPLEX_NULL,
+};
+
+enum spi_dma_dir {
+    SPI_DMA_NULL = 0,
+    SPI_DMA_RDEV = 1,
+    SPI_DMA_WDEV = 2,
+};
+
+#define SYS_SPI_PIN
+#ifndef SYS_SPI_PIN
+static void* __iomem gpio_addr = NULL;
+
+/* gpio base address */
+#define _PIO_BASE_ADDRESS    (0x01c20800)
+
+/* gpio spi1 */
+#define _Pn_CFG1(n) ( (n)*0x24 + 0x04 + gpio_addr )
+#define _Pn_DRV1(n) ( (n)*0x24 + 0x14 + gpio_addr )
+#define _Pn_PUL1(n) ( (n)*0x24 + 0x1C + gpio_addr )
+#endif
+
+struct sun7i_spi {
+    struct platform_device *pdev;
+    struct spi_master *master;          /* kzalloc */
+    void __iomem *base_addr;            /* register */
+    struct clk *hclk;                   /* ahb spi gating bit */
+    struct clk *mclk;                   /* ahb spi gating bit */
+    unsigned long gpio_hdle;
+    dma_chan_type_e dma_type;
+    int dma_id_tx;
+    int dma_id_rx;
+    dma_hdl_t dma_hdle_tx;
+    dma_hdl_t dma_hdle_rx;
+    enum spi_duplex_flag duplex_flag;
+    unsigned int irq;                   /* irq NO. */
+    int busy;
+#define SPI_FREE   (1<<0)
+#define SPI_SUSPND (1<<1)
+#define SPI_BUSY   (1<<2)
+    int result;                         /* 0: succeed, -1: fail */
+    struct workqueue_struct *workqueue;
+    struct work_struct work;
+    struct list_head queue;             /* spi messages */
+    spinlock_t lock;
+    struct completion done;             /* wakup another spi transfer */
+    /* keep select during one message */
+    void (*cs_control)(struct spi_device *spi, bool on);
+    /* (1) enable cs1,    cs_bitmap = SPI_CHIP_SELECT_CS1;
+     * (2) enable cs0&cs1,cs_bitmap = SPI_CHIP_SELECT_CS0|SPI_CHIP_SELECT_CS1;
+     */
+#define SPI_CHIP_SELECT_CS0 (0x01)
+#define SPI_CHIP_SELECT_CS1 (0x02)
+    int cs_bitmap;                      /* cs0- 0x1, cs1-0x2, cs0 & cs1-0x3. */
+};
+
+/* config chip select */
+s32 spi_set_cs(u32 chipselect, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+
+    if (chipselect < 4) {
+        reg_val &= ~SPI_CTL_SS_MASK;/* SS-chip select, clear two bits */
+        reg_val |= chipselect << SPI_SS_BIT_POS;/* set chip select */
+
+        writel(reg_val, base_addr + SPI_CTL_REG);
+
+        return 0;
+    } else {
+        spi_err("%s: chip select set fail, cs = %d\n", __func__, chipselect);
+        return -1;
+    }
+}
+
+/* select dma type */
+s32 spi_sel_dma_type(u32 dma_type, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+
+    reg_val &= ~SPI_CTL_DMAMOD;
+    if (dma_type) {
+        reg_val |= 1 << 5;
+    }
+    writel(reg_val, base_addr + SPI_CTL_REG);
+
+    return 0;
+}
+
+/* config spi */
+void spi_config(u32 master, u32 config, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+
+    /* 1. POL */
+    if (config & SPI_POL_ACTIVE_) {
+        reg_val |= SPI_CTL_POL;
+    } else {
+        reg_val &= ~SPI_CTL_POL; /* default POL = 0 */
+    }
+
+    /*2. PHA */
+    if (config & SPI_PHA_ACTIVE_) {
+        reg_val |= SPI_CTL_PHA;
+    } else {
+        reg_val &= ~SPI_CTL_PHA; /* default PHA = 0 */
+    }
+
+    /* 3. SSPOL, chip select signal polarity */
+    if (config & SPI_CS_HIGH_ACTIVE_) {
+        reg_val &= ~SPI_CTL_SSPOL;
+    } else {
+        reg_val |= SPI_CTL_SSPOL; /* default SSPOL = 1,Low level effective */
+    }
+
+    /* 4. LMTF-LSB/MSB transfer first select */
+    if (config & SPI_LSB_FIRST_ACTIVE_) {
+        reg_val |= SPI_CTL_LMTF;
+    } else {
+        reg_val &= ~SPI_CTL_LMTF; /* default LMTF = 0, MSB first */
+    }
+
+    /* master mode: set DDB,DHB,SMC,SSCTL */
+    if (master == 1) {
+        /* 5. dummy burst type */
+        if (config & SPI_DUMMY_ONE_ACTIVE_) {
+            reg_val |= SPI_CTL_DDB;
+        } else {
+            reg_val &= ~SPI_CTL_DDB; /* default DDB =0, ZERO */
+        }
+
+        /* 6. discard hash burst-DHB */
+        if (config & SPI_RECEIVE_ALL_ACTIVE_) {
+            reg_val &= ~SPI_CTL_DHB;
+        } else {
+            reg_val |= SPI_CTL_DHB; /* default DHB =1, discard unused burst */
+        }
+
+        /* 7. set SMC = 1, SSCTL = 0, TPE = 1 */
+        reg_val &= ~SPI_CTL_SSCTL;
+        reg_val |=  SPI_CTL_T_PAUSE_EN;
+    } else {
+        /* tips for slave mode config */
+        spi_inf("%s: slave mode configurate control register\n", __func__);
+    }
+
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* restore reg data after transfer complete */
+void spi_restore_state(u32 master, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+
+    /*
+     * config spi control register
+     * | 15 |  14  |  13  |  12  |  11  |  10  |  9  |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
+     * | DHB|  DDB |     SS      | SMC  |  XCH |TFRST|RFRST|SSCTL| MSB | TBW |SSPOL| POL | PHA | MOD | EN  |
+     * | 1  |   0  |     00      |  1   |   0  |  0  |  0  |  0  |  0  |  0  |  1  |  1  |  1  |  1  |  1  |
+     */
+    /* master mode */
+    if (master) {
+        reg_val |= (SPI_CTL_DHB | SPI_CTL_SSPOL | SPI_CTL_POL | SPI_CTL_PHA | SPI_CTL_FUNC_MODE | SPI_CTL_EN);
+
+        /* new bit,transmit pause enable,stop smart dummy when rxfifo full */
+        reg_val |= SPI_CTL_T_PAUSE_EN;
+
+        /* |SPI_CTL_TBW); //deleted SPI_CTL_TBW bit for aw1623, this bit is defined for dma mode select, 2011-5-26 19:55:32 */
+        reg_val &= ~(SPI_CTL_DDB | SPI_CTL_SS_MASK | SPI_CTL_XCH | SPI_CTL_SSCTL | SPI_CTL_LMTF);
+    } else { /* slave mode */
+        reg_val |= (SPI_CTL_SSPOL | SPI_CTL_POL | SPI_CTL_PHA || SPI_CTL_EN);
+
+        /* |SPI_CTL_TBW); //deleted SPI_CTL_TBW bit for aw1623, this bit is defined for dma mode select, 2011-5-26 19:55:32 */
+        reg_val &= ~(SPI_CTL_DHB | SPI_CTL_FUNC_MODE | SPI_CTL_DDB | SPI_CTL_SS_MASK | SPI_CTL_XCH | SPI_CTL_SSCTL | SPI_CTL_LMTF);
+    }
+
+    spi_dbg("control register set default value: 0x%08x\n", reg_val);
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* set spi clock */
+void spi_set_clk(u32 spi_clk, u32 ahb_clk, void *base_addr)
+{
+    u32 reg_val = 0;
+    u32 N = 0;
+    u32 div_clk = (ahb_clk >> 1) / spi_clk;
+
+    reg_val = readl(base_addr + SPI_CLK_RATE_REG);
+
+    if (div_clk <= SPI_CLK_SCOPE) {
+        if (div_clk != 0) {
+            div_clk--;
+        }
+
+        reg_val &= ~SPI_CLKCTL_CDR2;
+        reg_val |= (div_clk | SPI_CLKCTL_DRS);
+    } else {
+        while (1) {
+            if (div_clk == 1) {
+                break;
+            }
+            div_clk >>= 1;
+            N++;
+        };
+
+        reg_val &= ~(SPI_CLKCTL_CDR1 | SPI_CLKCTL_DRS);
+        reg_val |= (N << 8);
+    }
+
+    writel(reg_val, base_addr + SPI_CLK_RATE_REG);
+}
+
+/* start spi transfer */
+void spi_start_xfer(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val |= SPI_CTL_XCH;
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* query tranfer is completed in smc mode */
+u32 spi_query_xfer(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    return (reg_val & SPI_CTL_XCH);
+}
+
+/* enable spi bus */
+void spi_enable_bus(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val |= SPI_CTL_EN;
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* disbale spi bus */
+void spi_disable_bus(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val &= ~SPI_CTL_EN;
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* set master mode */
+void spi_set_master(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val |= SPI_CTL_FUNC_MODE;
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* set slave mode */
+void spi_set_slave(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val &= ~SPI_CTL_FUNC_MODE;
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* disable irq type */
+void spi_disable_irq(u32 bitmap, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_INT_CTL_REG);
+    bitmap &= SPI_INTEN_MASK;
+    reg_val &= ~bitmap;
+    writel(reg_val, base_addr + SPI_INT_CTL_REG);
+}
+
+/* enable irq type */
+void spi_enable_irq(u32 bitmap, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_INT_CTL_REG);
+    bitmap &= SPI_INTEN_MASK;
+    reg_val |= bitmap;
+    writel(reg_val, (base_addr + SPI_INT_CTL_REG));
+}
+
+/* disable dma irq */
+void spi_disable_dma_irq(u32 bitmap, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_DMA_CTL_REG);
+    bitmap &= SPI_DRQEN_MASK;
+    reg_val &= ~bitmap;
+    writel(reg_val, base_addr + SPI_DMA_CTL_REG);
+}
+
+/* enable dma irq */
+void spi_enable_dma_irq(u32 bitmap, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_DMA_CTL_REG);
+    bitmap &= SPI_DRQEN_MASK;
+    reg_val |= bitmap;
+    writel(reg_val, base_addr + SPI_DMA_CTL_REG);
+}
+
+/* query irq pending */
+u32 spi_qry_irq_pending(void *base_addr)
+{
+    return (SPI_STAT_MASK & readl(base_addr + SPI_STATUS_REG));
+}
+
+/* clear irq pending */
+void spi_clr_irq_pending(u32 pending_bit, void *base_addr)
+{
+    pending_bit &= SPI_STAT_MASK;
+    writel(pending_bit, base_addr + SPI_STATUS_REG);
+}
+
+/* query txfifo bytes */
+u32 spi_query_txfifo(void *base_addr)
+{
+    u32 reg_val = (SPI_FIFO_TXCNT & readl(base_addr + SPI_FIFO_STA_REG));
+    reg_val >>= SPI_TXCNT_BIT_POS;
+    return reg_val;
+}
+
+/* query rxfifo bytes */
+u32 spi_query_rxfifo(void *base_addr)
+{
+    u32 reg_val = (SPI_FIFO_RXCNT & readl(base_addr + SPI_FIFO_STA_REG));
+    reg_val >>= SPI_RXCNT_BIT_POS;
+    return reg_val;
+}
+
+/* reset fifo */
+void spi_reset_fifo(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val |= (SPI_CTL_RST_RXFIFO | SPI_CTL_RST_TXFIFO);
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* set transfer total length BC and transfer length WTC */
+void spi_set_bc_wtc(u32 tx_len, u32 rx_len, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_BC_REG);
+    reg_val &= ~SPI_BC_BC_MASK;
+    reg_val |= (SPI_BC_BC_MASK & (tx_len + rx_len));
+    writel(reg_val, base_addr + SPI_BC_REG);
+	spi_dbg("%s: bc: %u\n", __func__, readl(base_addr + SPI_BC_REG));
+
+    reg_val = readl(base_addr + SPI_TC_REG);
+    reg_val &= ~SPI_TC_WTC_MASK;
+    reg_val |= (SPI_TC_WTC_MASK & tx_len);
+    writel(reg_val, base_addr + SPI_TC_REG);
+	spi_dbg("%s: tc: %u\n", __func__, readl(base_addr + SPI_TC_REG));
+}
+
+/* set ss control */
+void spi_ss_ctrl(void *base_addr, u32 on_off)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+
+    on_off &= 0x1;
+    if (on_off) {
+        reg_val |= SPI_CTL_SS_CTRL;
+    } else {
+        reg_val &= ~SPI_CTL_SS_CTRL;
+    }
+
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* set ss level */
+void spi_ss_level(void *base_addr, u32 hi_lo)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+
+    hi_lo &= 0x1;
+    if (hi_lo) {
+        reg_val |= SPI_CTL_SS_LEVEL;
+    } else {
+        reg_val &= ~SPI_CTL_SS_LEVEL;
+    }
+
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* set wait clock counter */
+void spi_set_waitclk_cnt(u32 waitclk_cnt, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_WAIT_REG);
+    reg_val &= ~SPI_WAIT_CLK_MASK;
+    waitclk_cnt &= SPI_WAIT_CLK_MASK;
+    reg_val |= waitclk_cnt;
+    writel(reg_val, base_addr + SPI_WAIT_REG);
+}
+
+/* set sample delay in high speed mode */
+void spi_set_sample_delay(u32 on_off, void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val &= ~SPI_CTL_MASTER_SDC;
+    reg_val |= on_off;
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+/* keep unused burst */
+void spi_clear_dhb(void *base_addr)
+{
+    u32 reg_val = readl(base_addr + SPI_CTL_REG);
+    reg_val &= ~SPI_CTL_DHB;
+    writel(reg_val, base_addr + SPI_CTL_REG);
+}
+
+static int sun7i_spi_get_cfg_csbitmap(int bus_num);
+
+/* flush d-cache */
+static void sun7i_spi_cleanflush_dcache_region(void *addr, __u32 len)
+{
+    __cpuc_flush_dcache_area(addr, len/*len + (1 << 5) * 2 - 2*/);
+}
+
+static char *spi_dma_rx[] = {"spi0_rx", "spi1_rx", "spi2_rx", "spi3_rx"};
+static char *spi_dma_tx[] = {"spi0_tx", "spi1_tx", "spi2_tx", "spi3_tx"};
+
+static void sun7i_spi_dma_cb_rx(dma_hdl_t dma_hdl, void *parg)
+{
+    struct sun7i_spi *aw_spi = (struct sun7i_spi *)parg;
+    unsigned long flags;
+
+    spi_dbg("%s: spi%d dma read data callback\n", __func__, aw_spi->master->bus_num);
+
+    spin_lock_irqsave(&aw_spi->lock, flags);
+
+    spi_disable_dma_irq(SPI_DRQEN_RR, aw_spi->base_addr);
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+}
+
+static void sun7i_spi_dma_cb_tx(dma_hdl_t dma_hdl, void *parg)
+{
+    struct sun7i_spi *aw_spi = (struct sun7i_spi *)parg;
+    unsigned long flags;
+
+    spi_dbg("%s: spi%d dma write data callback\n", __func__, aw_spi->master->bus_num);
+
+    spin_lock_irqsave(&aw_spi->lock, flags);
+    spi_disable_dma_irq(SPI_DRQEN_TE, aw_spi->base_addr);
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+}
+
+/* request dma channel and set callback function */
+static int sun7i_spi_prepare_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir)
+{
+    int ret = 0;
+    int bus_num = aw_spi->master->bus_num;
+    dma_cb_t done_cb;
+
+	spi_dbg("%s: enter\n", __func__);
+    if (SPI_DMA_RDEV == dma_dir) {
+        aw_spi->dma_hdle_rx = sw_dma_request(spi_dma_rx[bus_num], aw_spi->dma_type);
+        if (!aw_spi->dma_hdle_rx) {
+            spi_err("%s: spi%d request dma rx failed\n", __func__, bus_num);
+            return -EINVAL;
+        }
+
+        done_cb.func = sun7i_spi_dma_cb_rx;
+        done_cb.parg = aw_spi;
+        ret = sw_dma_ctl(aw_spi->dma_hdle_rx, DMA_OP_SET_FD_CB, (void *)&done_cb);
+    } else if (SPI_DMA_WDEV == dma_dir) {
+        aw_spi->dma_hdle_tx = sw_dma_request(spi_dma_tx[bus_num], aw_spi->dma_type);
+        if (!aw_spi->dma_hdle_tx) {
+            spi_err("%s: spi%d request dma tx failed\n", __func__, bus_num);
+            return -EINVAL;
+        }
+ 
+        done_cb.func = sun7i_spi_dma_cb_tx;
+        done_cb.parg = aw_spi;
+        ret = sw_dma_ctl(aw_spi->dma_hdle_tx, DMA_OP_SET_FD_CB, (void *)&done_cb);
+    } else {
+        return -1;
+    }
+
+    return ret;
+}
+
+static int sun7i_spi_config_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir, void *buf, unsigned int len)
+{
+    int bus_num = aw_spi->master->bus_num;
+    unsigned char spi_rx_ndrq[] = {N_SRC_SPI0_RX, N_SRC_SPI1_RX, N_SRC_SPI2_RX, N_SRC_SPI3_RX};
+    unsigned char spi_tx_ndrq[] = {N_DST_SPI0_TX, N_DST_SPI1_TX, N_DST_SPI2_TX, N_DST_SPI3_TX};
+    unsigned char spi_rx_ddrq[] = {D_SRC_SPI0_RX, D_SRC_SPI1_RX, D_SRC_SPI2_RX, D_SRC_SPI3_RX};
+    unsigned char spi_tx_ddrq[] = {D_DST_SPI0_TX, D_DST_SPI1_TX, D_DST_SPI2_TX, D_DST_SPI3_TX};
+    unsigned long spi_phyaddr[] = {SPI0_BASE_ADDR, SPI1_BASE_ADDR, SPI2_BASE_ADDR, SPI3_BASE_ADDR}; /* physical address */
+    dma_config_t spi_hw_conf;
+    dma_hdl_t dma_hdle;
+    u32 src_addr, dst_addr;
+    u32 security = SRC_SECU_DST_SECU;
+
+	spi_dbg("%s: enter\n", __func__);
+    spi_hw_conf.xfer_type.src_data_width    = DATA_WIDTH_8BIT;
+    spi_hw_conf.xfer_type.src_bst_len       = DATA_BRST_1;
+    spi_hw_conf.xfer_type.dst_data_width    = DATA_WIDTH_8BIT;
+    spi_hw_conf.xfer_type.dst_bst_len       = DATA_BRST_1;
+    spi_hw_conf.bconti_mode                 = false;
+    spi_hw_conf.irq_spt                     = CHAN_IRQ_FD;
+    switch (aw_spi->dma_type) {
+        case CHAN_NORMAL:
+            if (SPI_DMA_RDEV == dma_dir) {
+                spi_hw_conf.address_type.src_addr_mode  = NDMA_ADDR_NOCHANGE;
+                spi_hw_conf.address_type.dst_addr_mode  = NDMA_ADDR_INCREMENT;
+                spi_hw_conf.src_drq_type                = spi_rx_ndrq[bus_num];
+                spi_hw_conf.dst_drq_type                = N_DST_SDRAM;
+            } else if (SPI_DMA_WDEV == dma_dir) {
+                spi_hw_conf.address_type.src_addr_mode  = NDMA_ADDR_INCREMENT;
+                spi_hw_conf.address_type.dst_addr_mode  = NDMA_ADDR_NOCHANGE;
+                spi_hw_conf.src_drq_type                = N_SRC_SDRAM;
+                spi_hw_conf.dst_drq_type                = spi_tx_ndrq[bus_num];
+            } else {
+                return -1;
+            }
+            break;
+
+        case CHAN_DEDICATE:
+            if (SPI_DMA_RDEV == dma_dir) {
+                spi_hw_conf.address_type.src_addr_mode  = DDMA_ADDR_IO;
+                spi_hw_conf.address_type.dst_addr_mode  = DDMA_ADDR_LINEAR;
+                spi_hw_conf.src_drq_type                = spi_rx_ddrq[bus_num];
+                spi_hw_conf.dst_drq_type                = D_DST_SDRAM;
+            } else if (SPI_DMA_WDEV == dma_dir) {
+                spi_hw_conf.address_type.src_addr_mode  = DDMA_ADDR_LINEAR;
+                spi_hw_conf.address_type.dst_addr_mode  = DDMA_ADDR_IO;
+                spi_hw_conf.src_drq_type                = D_SRC_SDRAM;
+                spi_hw_conf.dst_drq_type                = spi_tx_ddrq[bus_num];
+            } else {
+                return -1;
+            }
+            break;
+    }
+
+    if (SPI_DMA_RDEV == dma_dir) {
+        dma_hdle = aw_spi->dma_hdle_rx;
+        src_addr = spi_phyaddr[bus_num] + SPI_RXDATA_REG;
+        dst_addr = virt_to_phys(buf);
+    } else if (SPI_DMA_WDEV == dma_dir) {
+        dma_hdle = aw_spi->dma_hdle_tx;
+        src_addr = virt_to_phys(buf);
+        dst_addr = spi_phyaddr[bus_num] + SPI_TXDATA_REG;
+    } else {
+        return -1;
+    }
+
+    /* set src, dst, drq type, configuration */
+    if (sw_dma_config(dma_hdle, &spi_hw_conf)) {
+        spi_err("%s: spi%d config failed\n", __func__, bus_num);
+        return -1;
+    }
+
+    if (sw_dma_ctl(dma_hdle, DMA_OP_SET_SECURITY, &security)) {
+        spi_err("%s: spi%d set dma SRC_SECU_DST_SECU failed\n", __func__,
+                bus_num);
+        return -1;
+    }
+
+    if (CHAN_DEDICATE == aw_spi->dma_type) {
+        dma_para_t para = {
+            .src_wait_cyc = 0x07,
+            .src_blk_sz   = 0x1f,
+            .dst_wait_cyc = 0x07,
+            .dst_blk_sz   = 0x1f
+        };
+
+        if (sw_dma_ctl(dma_hdle, DMA_OP_SET_PARA_REG, &para)) {
+            spi_err("%s: spi%d set dma para failed\n", __func__, bus_num);
+            return -1;
+        }
+    }
+
+    /* 1. flush d-cache */
+    sun7i_spi_cleanflush_dcache_region((void *)buf, len);
+
+    /* 2. enqueue dma transfer */
+    return sw_dma_enqueue(dma_hdle, src_addr, dst_addr, len);
+}
+
+/* set dma start flag, if queue, it will auto restart to transfer next queue */
+static int sun7i_spi_start_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir)
+{
+    int ret = 0;
+
+	spi_dbg("%s: enter\n", __func__);
+    if (SPI_DMA_RDEV == dma_dir) {
+        ret = sw_dma_ctl(aw_spi->dma_hdle_rx, DMA_OP_START, NULL);
+    } else if (SPI_DMA_WDEV == dma_dir) {
+        ret = sw_dma_ctl(aw_spi->dma_hdle_tx, DMA_OP_START, NULL);
+    } else {
+        return -1;
+    }
+
+    return ret;
+}
+
+/* release dma channel, and set queue status to idle. */
+static int sun7i_spi_release_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir)
+{
+    int ret = 0;
+    unsigned long flags;
+
+	spi_dbg("%s: enter\n", __func__);
+    if (SPI_DMA_RDEV == dma_dir) {
+        spin_lock_irqsave(&aw_spi->lock, flags);
+
+        ret  = sw_dma_ctl(aw_spi->dma_hdle_rx, DMA_OP_STOP, NULL);
+        ret += sw_dma_release(aw_spi->dma_hdle_rx);
+        aw_spi->dma_hdle_rx = NULL;
+        spin_unlock_irqrestore(&aw_spi->lock, flags);
+    } else if (SPI_DMA_WDEV == dma_dir) {
+        spin_lock_irqsave(&aw_spi->lock, flags);
+
+        ret  = sw_dma_ctl(aw_spi->dma_hdle_tx, DMA_OP_STOP, NULL);
+        ret += sw_dma_release(aw_spi->dma_hdle_tx);
+        aw_spi->dma_hdle_tx = NULL;
+        spin_unlock_irqrestore(&aw_spi->lock, flags);
+    } else {
+        return -1;
+    }
+
+    return ret;
+}
+
+/* check the valid of cs id */
+static int sun7i_spi_check_cs(int cs_id, struct sun7i_spi *aw_spi)
+{
+    int ret = -1;
+    switch (cs_id) {
+        case 0:
+            ret = (aw_spi->cs_bitmap & SPI_CHIP_SELECT_CS0) ? 0 : -1;
+            break;
+        case 1:
+            ret = (aw_spi->cs_bitmap & SPI_CHIP_SELECT_CS1) ? 0 : -1;
+            break;
+        default:
+            spi_err("%s: spi%d chip select not support, cs = %d\n",
+                    __func__, aw_spi->master->bus_num, cs_id);
+            break;
+    }
+
+    return ret;
+}
+
+/* spi device on or off control */
+static void sun7i_spi_cs_control(struct spi_device *spi, bool on)
+{
+    struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master);
+    unsigned int cs = 0;
+    if (aw_spi->cs_control) {
+        if (on) {
+            /* set active */
+            cs = (spi->mode & SPI_CS_HIGH) ? 1 : 0;
+        } else {
+            /* set inactive */
+            cs = (spi->mode & SPI_CS_HIGH) ? 0 : 1;
+        }
+        spi_ss_level(aw_spi->base_addr, cs);
+    }
+}
+
+/*
+ * change the properties of spi device with spi transfer.
+ * every spi transfer must call this interface to update the master to the excute transfer
+ * set clock frequecy, bits per word, mode etc...
+ * return:  >= 0 : succeed;    < 0: failed.
+ */
+static int sun7i_spi_xfer_setup(struct spi_device *spi, struct spi_transfer *t)
+{
+    /* get at the setup function, the properties of spi device */
+    struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master);
+    struct sun7i_spi_config *config = spi->controller_data; // allocate in the setup, and free in the cleanup
+    void *__iomem base_addr = aw_spi->base_addr;
+
+    spi_dbg("%s: enter\n", __func__);
+    config->max_speed_hz  = (t && t->speed_hz) ? t->speed_hz : spi->max_speed_hz;
+    config->bits_per_word = (t && t->bits_per_word) ? t->bits_per_word : spi->bits_per_word;
+    config->bits_per_word = ((config->bits_per_word + 7) / 8) * 8;
+
+    if (config->bits_per_word != 8) {
+        spi_err("%s: spi%d just support 8bits per word\n", __func__, spi->master->bus_num);
+        return -EINVAL;
+    }
+
+    if (spi->chip_select >= spi->master->num_chipselect) {
+        spi_err("%s: spi%d device's chip select = %d exceeds the master supported cs_num[%d] \n",
+                __func__, spi->master->bus_num, spi->chip_select, spi->master->num_chipselect);
+        return -EINVAL;
+    }
+
+    /* check again board info */
+    if (sun7i_spi_check_cs(spi->chip_select, aw_spi)) {
+        spi_err("%s: sun7i_spi_check_cs failed, spi_device cs =%d\n", __func__, spi->chip_select);
+        return -EINVAL;
+    }
+
+    /* set cs */
+    spi_set_cs(spi->chip_select, base_addr);
+    /*
+     *  master: set spi module clock;
+     *  set the default frequency   10MHz
+     */
+    spi_set_master(base_addr);
+    if (config->max_speed_hz > SPI_MAX_FREQUENCY) {
+        return -EINVAL;
+    }
+
+#ifndef CONFIG_AW_FPGA_PLATFORM
+    spi_set_clk(config->max_speed_hz, clk_get_rate(aw_spi->mclk), base_addr);
+#else
+    spi_set_clk(config->max_speed_hz, 24000000, base_addr);
+#endif
+
+    /*
+     *  master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0.
+     *  set bit width-default: 8 bits
+     */
+    spi_config(1, spi->mode, base_addr);
+
+    return 0;
+}
+
+/*
+ * <= 64 : cpu ;  > 64 : dma
+ * wait for done completion in this function, wakup in the irq hanlder
+ */
+static int sun7i_spi_xfer(struct spi_device *spi, struct spi_transfer *t)
+{
+    struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master);
+    void __iomem *base_addr = aw_spi->base_addr;
+    unsigned long flags = 0;
+    unsigned tx_len = t->len;
+    unsigned rx_len = t->len;
+    unsigned char *rx_buf = (unsigned char *)t->rx_buf;
+    unsigned char *tx_buf = (unsigned char *)t->tx_buf;
+    enum spi_dma_dir dma_dir = SPI_DMA_NULL;
+    int ret = 0;
+
+#if (SPI_DEBUG_LEVEL== 3)
+    if(tx_len > 2)
+    	spi_inf("%s: spi%d  txbuf %p, start: %2x rxbuf %p, len %d\n",
+            __func__, spi->master->bus_num, tx_buf, tx_buf[0], rx_buf, tx_len);
+    else
+    	spi_inf("%s: spi%d begin transfer, txbuf %p, start: %4x rxbuf %p, len %d\n",
+            __func__, spi->master->bus_num, tx_buf, tx_buf[0]<<8 | tx_buf[1], rx_buf, tx_len);
+#endif
+
+
+    if ((!t->tx_buf && !t->rx_buf) || !t->len) {
+        spi_err("%s: spi%d invalid buffer or len\n", __func__, spi->master->bus_num);
+        return -EINVAL;
+    }
+    /* write 1 to clear 0 */
+    spi_clr_irq_pending(SPI_STAT_MASK, base_addr);
+    /* disable all DRQ */
+    spi_disable_dma_irq(SPI_DRQEN_MASK, base_addr);
+    /* reset tx/rx fifo */
+    spi_reset_fifo(base_addr);
+
+    if (aw_spi->duplex_flag != DUPLEX_NULL) {
+        spi_err("%s: spi%d duplex flag not null\n", __func__, spi->master->bus_num);
+        return -EINVAL;
+    }
+
+    /* set the Burst Counter and Write Transmit Counter, auto put dummy data into the txFIFO! */
+    /* check if use full duplex or half duplex */
+    if (tx_buf && rx_buf) {
+        spin_lock_irqsave(&aw_spi->lock, flags);
+
+        aw_spi->duplex_flag = FULL_DUPLEX_RX_TX;
+        spi_set_bc_wtc(tx_len, 0, base_addr);
+        spin_unlock_irqrestore(&aw_spi->lock, flags);
+    } else {
+        if (tx_buf) {
+            spin_lock_irqsave(&aw_spi->lock, flags);
+
+            aw_spi->duplex_flag = HALF_DUPLEX_TX;
+            spi_set_bc_wtc(tx_len, 0, base_addr);
+            spin_unlock_irqrestore(&aw_spi->lock, flags);
+        } else if (rx_buf) {
+            spin_lock_irqsave(&aw_spi->lock, flags);
+
+            aw_spi->duplex_flag = HALF_DUPLEX_RX;
+            spi_set_bc_wtc(0, rx_len, base_addr);
+            spin_unlock_irqrestore(&aw_spi->lock, flags);
+        }
+    }
+
+    /*
+     * 1. Tx/Rx error irq, process in IRQ;
+     * 2. Transfer Complete Interrupt Enable
+     */
+    spi_enable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr);
+
+    /* > 64 use DMA transfer, or use cpu */
+    if (t->len > BULK_DATA_BOUNDARY) {
+        spi_sel_dma_type(aw_spi->dma_type, base_addr);
+
+        switch (aw_spi->duplex_flag) {
+            case HALF_DUPLEX_RX: {
+                spi_enable_dma_irq(SPI_DRQEN_RR, base_addr);
+
+                dma_dir |= SPI_DMA_RDEV;
+                ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_RDEV);
+                if (ret < 0) {
+                    spi_disable_dma_irq(SPI_DRQEN_RR, base_addr);
+                    spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr);
+                    return -EINVAL;
+                }
+
+                sun7i_spi_config_dma(aw_spi, SPI_DMA_RDEV, (void *)rx_buf, rx_len);
+                sun7i_spi_start_dma(aw_spi, SPI_DMA_RDEV);
+
+                spi_start_xfer(base_addr);
+                break;
+            }
+
+            case HALF_DUPLEX_TX: {
+                spi_start_xfer(base_addr);
+                spi_enable_dma_irq(SPI_DRQEN_TE, base_addr);
+
+                dma_dir |= SPI_DMA_WDEV;
+                ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_WDEV);
+                if (ret < 0) {
+                    spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr);
+                    spi_disable_dma_irq(SPI_DRQEN_TE, base_addr);
+                    return -EINVAL;
+                }
+
+                sun7i_spi_config_dma(aw_spi, SPI_DMA_WDEV, (void *)tx_buf, tx_len);
+                sun7i_spi_start_dma(aw_spi, SPI_DMA_WDEV);
+                break;
+            }
+
+            case FULL_DUPLEX_RX_TX: {
+                spi_enable_dma_irq(SPI_DRQEN_RR, base_addr);
+
+                dma_dir |= SPI_DMA_RDEV;
+                ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_RDEV);
+                if (ret < 0) {
+                    spi_disable_dma_irq(SPI_DRQEN_RR, base_addr);
+                    spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr);
+                    return -EINVAL;
+                }
+
+                sun7i_spi_config_dma(aw_spi, SPI_DMA_RDEV, (void *)rx_buf, rx_len);
+                sun7i_spi_start_dma(aw_spi, SPI_DMA_RDEV);
+
+                spi_start_xfer(base_addr);
+                spi_enable_dma_irq(SPI_DRQEN_TE, base_addr);
+
+                dma_dir |= SPI_DMA_WDEV;
+                ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_WDEV);
+                if (ret < 0) {
+                    spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr);
+                    spi_disable_dma_irq(SPI_DRQEN_TE, base_addr);
+                    return -EINVAL;
+                }
+
+                sun7i_spi_config_dma(aw_spi, SPI_DMA_WDEV, (void *)tx_buf, tx_len);
+                sun7i_spi_start_dma(aw_spi, SPI_DMA_WDEV);
+                break;
+            }
+
+            default:
+                return -1;
+        }
+    } else {
+        switch (aw_spi->duplex_flag) {
+            case HALF_DUPLEX_RX: {
+                unsigned int poll_time = 0x7ffff;
+                /* SMC=1,XCH trigger the transfer */
+                spi_start_xfer(base_addr);
+                while (rx_len && (--poll_time > 0)) {
+                    /* rxFIFO counter */
+                    if (spi_query_rxfifo(base_addr)) {
+                        *rx_buf++ = readb(base_addr + SPI_RXDATA_REG);
+                        --rx_len;
+                    }
+                }
+                if (poll_time <= 0) {
+                    spi_err("%s: spi%d cpu receive data time out\n", 
+                            __func__, spi->master->bus_num);
+                }
+                break;
+            }
+            case HALF_DUPLEX_TX: {
+                unsigned int poll_time = 0xfffff;
+                spi_start_xfer(base_addr);
+
+                spin_lock_irqsave(&aw_spi->lock, flags);
+
+                for (; tx_len > 0; --tx_len) {
+                    writeb(*tx_buf++, base_addr + SPI_TXDATA_REG);
+                }
+                spin_unlock_irqrestore(&aw_spi->lock, flags);
+
+                while (spi_query_txfifo(base_addr) && (--poll_time > 0)); /* txFIFO counter */
+                if (poll_time <= 0) {
+                    spi_err("%s: spi%d cpu transfer data time out\n",
+                            __func__, spi->master->bus_num);
+                }
+                break;
+            }
+            case FULL_DUPLEX_RX_TX: {
+                unsigned int poll_time_tx = 0xfffff;
+                unsigned int poll_time_rx = 0x7ffff;
+
+                if ((rx_len == 0) || (tx_len == 0))
+                    return -EINVAL;
+
+                spi_start_xfer(base_addr);
+
+                spin_lock_irqsave(&aw_spi->lock, flags);
+
+                for (; tx_len > 0; --tx_len) {
+                    writeb(*tx_buf++, base_addr + SPI_TXDATA_REG);
+                }
+                spin_unlock_irqrestore(&aw_spi->lock, flags);
+
+                while (spi_query_txfifo(base_addr) && (--poll_time_tx > 0)); /* txFIFO counter */
+                if (poll_time_tx <= 0) {
+                    spi_err("%s: spi%d cpu transfer data time out\n",
+                            __func__, spi->master->bus_num);
+                    break;
+                }
+
+                while (rx_len && (--poll_time_rx > 0)) {
+                    /* rxFIFO counter */
+                    if (spi_query_rxfifo(base_addr)) {
+                        *rx_buf++ =  readb(base_addr + SPI_RXDATA_REG);
+                        --rx_len;
+                    }
+                }
+                if (poll_time_rx <= 0) {
+                    spi_err("%s: spi%d cpu receive data time out\n",
+                            __func__, spi->master->bus_num);
+                }
+                break;
+            }
+            default:
+                return -1;
+        }
+    }
+
+    /* wait for xfer complete in the isr. */
+    spi_dbg("%s: spi%d wait for xfer complete\n", __func__, spi->master->bus_num);
+    wait_for_completion(&aw_spi->done);
+    /* get the isr return code */
+    if (aw_spi->result != 0) {
+        spi_err("%s: spi%d xfer failed\n", __func__, spi->master->bus_num);
+        ret = -1;
+    }
+
+    /* release dma resource if neccessary */
+    if (SPI_DMA_RDEV & dma_dir) {
+        sun7i_spi_release_dma(aw_spi, SPI_DMA_RDEV);
+    }
+    if (SPI_DMA_WDEV & dma_dir) {
+        sun7i_spi_release_dma(aw_spi, SPI_DMA_WDEV);
+    }
+    
+    if (aw_spi->duplex_flag != DUPLEX_NULL) {
+        aw_spi->duplex_flag = DUPLEX_NULL;
+    }
+
+    return ret;
+}
+
+
+/* helper for long interbyte usecs */
+static void sun7i_spi_safe_udelay(unsigned long usecs)
+{
+        while (usecs > MAX_UDELAY_US) {
+                udelay(MAX_UDELAY_US);
+                usecs -= MAX_UDELAY_US;
+        }
+        udelay(usecs);
+}
+
+#include<linux/time.h>
+
+/* spi core xfer process */
+static void sun7i_spi_work(struct work_struct *work)
+{
+    struct sun7i_spi *aw_spi = container_of(work, struct sun7i_spi, work);
+    unsigned long flags = 0;
+
+
+    spin_lock_irqsave(&aw_spi->lock, flags);
+    aw_spi->busy = SPI_BUSY;
+    /*
+     * get from messages queue, and then do with them,
+     * if message queue is empty ,then return and set status to free,
+     * otherwise process them.
+     */
+    while (!list_empty(&aw_spi->queue)) {
+        struct spi_message *msg = NULL;
+        struct spi_device  *spi = NULL;
+        struct spi_transfer *t  = NULL;
+        struct timespec t_bef,t_now;
+        unsigned int cs_change = 0;
+        unsigned int previous_delay = 0;
+        int status;
+        /* get message from message queue in sun7i_spi. */
+        msg = container_of(aw_spi->queue.next, struct spi_message, queue);
+        /* then delete from the message queue,now it is alone.*/
+
+
+        list_del_init(&msg->queue);
+        spin_unlock_irqrestore(&aw_spi->lock, flags);
+     /* get spi device from this message */
+        spi = msg->spi;
+        /* set default value,no need to change cs,keep select until spi transfer require to change cs. */
+        cs_change = 1;
+        /* set message status to succeed. */
+        status = -1;
+
+        getnstimeofday(&t_bef);
+        /* search the spi transfer in this message, deal with it alone. */
+        list_for_each_entry(t, &msg->transfers, transfer_list) {
+            if ((status == -1) || (t->bits_per_word != spi->bits_per_word) || (t->speed_hz != spi->max_speed_hz)) { /* if spi transfer is zero,use spi device value. */
+                status = sun7i_spi_xfer_setup(spi, t); /* set the value every spi transfer */
+                if (status < 0)
+                    break; /* fail, quit */
+                spi_dbg("[spi-%d]: xfer setup \n", aw_spi->master->bus_num);
+            }
+            
+            /* first active the cs */
+            if (cs_change) {
+                aw_spi->cs_control(spi, 1);
+                spi_dbg("[spi-%d]: cs active",aw_spi->master->bus_num);
+            }
+            /* update the new cs value */
+            cs_change = t->cs_change;
+            
+            /* full duplex mode */
+            if (t->rx_buf && t->tx_buf)
+                spi_clear_dhb(aw_spi->base_addr);
+            
+            /*
+             * do transfer
+             * > 64 : dma ;  <= 64 : cpu
+             * wait for done completion in this function, wakup in the irq hanlder
+             */
+            if(previous_delay) {
+//            	int tx_val;
+            	long passed_nsec, delay_usec;
+            	getnstimeofday(&t_now);
+            	passed_nsec=((t_now.tv_sec > t_bef.tv_sec) ? t_now.tv_nsec + NSEC_PER_SEC : t_now.tv_nsec) - t_bef.tv_nsec;
+            	delay_usec=(long) previous_delay - (passed_nsec) / 1000L - 5L;
+            	if(delay_usec<0)
+            		delay_usec=0;
+//            	spi_inf("[spi-%d]: before transfer of %04x would still delay %5ldus\n",
+//            			aw_spi->master->bus_num, tx_val, delay_usec);
+            	sun7i_spi_safe_udelay(delay_usec);
+            }
+            if (t->interbyte_usecs) {
+            	getnstimeofday(&t_bef);
+            	previous_delay=t->interbyte_usecs;
+            }
+
+            status = sun7i_spi_xfer(spi, t);
+            if (status)
+                break; /* fail quit, zero means succeed */
+            /* accmulate the value in the message */
+            msg->actual_length += t->len;
+            /* may be need to delay */
+ //           getnstimeofday(&taft);
+ //           spi_inf("[spi-%d]: usec before delay and since last delay: %6ld", aw_spi->master->bus_num, (taft.tv_nsec-tbef.tv_nsec)/1000L);
+            if (t->delay_usecs) {
+           		udelay(t->delay_usecs);
+            }
+  //          getnstimeofday(&tbef);
+            /* if zero, keep active, otherwise deactived. */
+            if (cs_change) {
+                aw_spi->cs_control(spi, 0);
+                spi_dbg("[spi-%d]: cs inactive",aw_spi->master->bus_num);
+            }
+
+        }
+        
+        /*
+         * spi message complete,succeed or failed
+         * return value
+         */
+        msg->status = status;
+        /* wakup the uplayer caller,complete one message */
+        msg->complete(msg->context);
+        /* fail or need to change cs */
+        if (status || !cs_change) {
+            aw_spi->cs_control(spi, 0);
+            spi_dbg("[spi-%d]: cs inactive",aw_spi->master->bus_num);
+        }
+        
+        /* restore default value. */
+        //sun7i_spi_xfer_setup(spi, NULL);
+        spin_lock_irqsave(&aw_spi->lock, flags);
+    }
+    
+    /* set spi to free */
+    aw_spi->busy = SPI_FREE;
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+}
+
+/* wake up the sleep thread, and give the result code */
+static irqreturn_t sun7i_spi_handler(int irq, void *dev_id)
+{
+    struct sun7i_spi *aw_spi = (struct sun7i_spi *)dev_id;
+    void *base_addr = aw_spi->base_addr;
+
+    unsigned int status = spi_qry_irq_pending(base_addr);
+    spi_clr_irq_pending(status, base_addr);
+
+    aw_spi->result = 0; /* assume succeed */
+    /* master mode, Transfer Complete Interrupt */
+    if (status & SPI_STAT_TC) {
+        spi_dbg("%s: spi%d TC comes, irq status = 0x%08x\n",
+			    __func__, aw_spi->master->bus_num, status);
+        spi_disable_irq(SPI_STAT_TC | SPI_STAT_ERR, base_addr);
+        /*
+         * just check dma+callback receive, skip other condition.
+         * dma+callback receive: when TC comes, dma may be still not complete fetch data from rxFIFO.
+         * other receive: cpu or dma+poll, just skip this.
+         */
+        if (aw_spi->dma_hdle_rx) {
+            unsigned int poll_time = 0xffff;
+            /* during poll, dma maybe complete rx, rx_dma_used is 0. then return.*/
+            while (spi_query_rxfifo(base_addr) && (--poll_time > 0));
+            if (poll_time <= 0) {
+                spi_err("%s: spi%d dma callback method, rx data time out in irq\n",
+                        __func__, aw_spi->master->bus_num);
+                aw_spi->result = -1;
+                complete(&aw_spi->done);
+                return -1;
+            } else if (poll_time < 0xffff) {
+                spi_dbg("%s: spi%d rx irq comes first, dma last. wait = 0x%x\n",
+                        __func__, aw_spi->master->bus_num, poll_time);
+            }
+        }
+
+        /* wakup uplayer, by the sem */
+        complete(&aw_spi->done);
+        return IRQ_HANDLED;
+    }
+    /* master mode: err */
+    else if (status & SPI_STAT_ERR) {
+        spi_err("%s: spi%d ERR comes, irq status = 0x%08x\n",
+			    __func__, aw_spi->master->bus_num, status);
+        /* error process, release dma in the workqueue,should not be here */
+        spi_disable_irq(SPI_STAT_TC | SPI_STAT_ERR, base_addr);
+        spi_restore_state(1, base_addr);
+        aw_spi->result = -1;
+        complete(&aw_spi->done);
+        spi_err("%s: spi%d master mode error: txFIFO overflow/rxFIFO underrun or overflow\n",
+                __func__, aw_spi->master->bus_num);
+        return IRQ_HANDLED;
+    }
+
+    spi_dbg("%s: spi%d NONE comes\n", __func__, aw_spi->master->bus_num);
+    return IRQ_NONE;
+}
+
+/* interface 1 */
+static int sun7i_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+    struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master);
+    unsigned long flags;
+    msg->actual_length = 0;
+    msg->status = -EINPROGRESS;
+
+    spi_dbg("%s: enter, spi_msg tx:%d, spi_msg rx:%d\n", __func__, aw_spi->dma_id_tx, aw_spi->dma_id_rx);
+    spin_lock_irqsave(&aw_spi->lock, flags);
+    /* add msg to the sun7i_spi queue */
+    list_add_tail(&msg->queue, &aw_spi->queue);
+    /* add work to the workqueue,schedule the cpu. */
+    queue_work(aw_spi->workqueue, &aw_spi->work);
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+    /* return immediately and wait for completion in the uplayer caller. */
+    return 0;
+}
+
+/* interface 2, setup the frequency and default status */
+static int sun7i_spi_setup(struct spi_device *spi)
+{
+    struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master);
+    struct sun7i_spi_config *config = spi->controller_data; /* general is null. */
+    unsigned long flags;
+
+    spi_inf("%s: enter, bpw: %d, mshz: %d, mode: %d\n", __func__, spi->bits_per_word, spi->max_speed_hz, spi->mode);
+    /* just support 8 bits per word */
+    if (spi->bits_per_word != 8)
+        return -EINVAL;
+    /* first check its valid,then set it as default select,finally set its */
+    if (sun7i_spi_check_cs(spi->chip_select, aw_spi)) {
+        spi_err("%s: spi%d not support cs-%d \n", __func__,
+                spi->master->bus_num, spi->chip_select);
+        return -EINVAL;
+    }
+
+    if (spi->max_speed_hz > SPI_MAX_FREQUENCY)
+        return -EINVAL;
+
+    if (!config) {
+        config = kzalloc(sizeof * config, GFP_KERNEL);
+        if (!config) {
+            spi_err("%s: spi%d alloc memory for config failed\n",
+                    __func__, spi->master->bus_num);
+            return -ENOMEM;
+        }
+        spi->controller_data = config;
+    }
+
+    /*
+     * set the default vaule with spi device
+     * can change by every spi transfer
+     */
+    config->bits_per_word = spi->bits_per_word;
+    config->max_speed_hz  = spi->max_speed_hz;
+    config->mode          = spi->mode;
+
+    spin_lock_irqsave(&aw_spi->lock, flags);
+
+    /* if aw16xx spi is free, then deactived the spi device */
+    if (aw_spi->busy & SPI_FREE) {
+        /* set chip select number */
+        spi_set_cs(spi->chip_select, aw_spi->base_addr);
+        /* deactivate chip select */
+        aw_spi->cs_control(spi, 0);
+    }
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+
+    return 0;
+}
+
+/* interface 3 */
+static void sun7i_spi_cleanup(struct spi_device *spi)
+{
+    if (spi->controller_data) {
+        kfree(spi->controller_data);
+        spi->controller_data = NULL;
+    }
+}
+
+static int sun7i_spi_set_gpio(struct sun7i_spi *aw_spi, bool on)
+{
+    if(on) {
+        if(aw_spi->master->bus_num == 0) {
+            aw_spi->gpio_hdle = gpio_request_ex("spi0_para", NULL);
+            if(!aw_spi->gpio_hdle) {
+                spi_err("spi0 request gpio fail!\n");
+                return -1;
+            }
+            //hex_dump("gpio regs:", (void __iomem*)SW_VA_PORTC_IO_BASE, 0x200, 2);
+        }
+        else if(aw_spi->master->bus_num == 1) {
+			/**
+			 * PI8		SPI1_CS0
+			 * PI9		SPI1_CS1
+			 * PI10    	SPI1_CLK
+			 * PI11    	SPI1_MOSI
+			 * PI12	    SPI1_MISO
+			 */
+            #ifndef SYS_SPI_PIN
+		    unsigned int  reg_val = readl(_Pn_CFG1(8));
+			/* set spi function */
+		    reg_val &= ~0x77777;
+		    reg_val |=  0x22222;
+		    writel(reg_val, _Pn_CFG1(8));
+            /* set pull up */
+    		reg_val = readl(_Pn_PUL1(8));
+    		reg_val &= ~(0x3ff<<16);
+    		reg_val |=  (0x155<<16);
+    		writel(reg_val, _Pn_PUL1(8));
+    		/* no need to set driver,default is driver 1. */
+    		#else
+            aw_spi->gpio_hdle = gpio_request_ex("spi1_para", NULL);
+            if(!aw_spi->gpio_hdle) {
+                spi_err("spi1 request gpio fail!\n");
+                return -1;
+            }
+    		#endif
+        }
+        else if(aw_spi->master->bus_num == 2) {
+            aw_spi->gpio_hdle = gpio_request_ex("spi2_para", NULL);
+            if(!aw_spi->gpio_hdle) {
+                spi_err("spi2 request gpio fail!\n");
+                return -1;
+            }
+        }
+
+        #ifdef AW1623_FPGA
+        {
+            #include <mach/platform.h>
+            void __iomem* pi_cfg0 = (void __iomem*)(SW_VA_PORTC_IO_BASE+0x48);
+            u32 rval = readl(pi_cfg0) & (~(0x70777));
+            writel(rval|(0x30333), pi_cfg0);
+        }
+        #endif
+    }
+    else {
+        if(aw_spi->master->bus_num == 0) {
+            gpio_release(aw_spi->gpio_hdle, 0);
+        }
+        else if(aw_spi->master->bus_num == 1) {
+        	#ifndef SYS_SPI_PIN
+		    unsigned int  reg_val = readl(_Pn_CFG1(8));
+			/* set default */
+		    reg_val &= ~0x77777;
+		    writel(reg_val, _Pn_CFG1(8));
+            #else
+		    gpio_release(aw_spi->gpio_hdle, 0);
+		    #endif
+        }
+        else if(aw_spi->master->bus_num == 2) {
+            gpio_release(aw_spi->gpio_hdle, 0);
+        }
+    }
+	return 0;
+}
+
+static int sun7i_spi_set_mclk(struct sun7i_spi *aw_spi, u32 mod_clk)
+{
+    struct clk *source_clock = NULL;
+    char *name = NULL;
+#ifdef CONFIG_AW_FPGA_PLATFORM
+//    u32 source = 0;
+#elif defined CONFIG_AW_ASIC_PLATFORM
+//	u32 source = 2;
+#endif
+	u32 source = 1;
+    int ret = 0;
+
+    switch (source) {
+        case 0:
+            source_clock = clk_get(NULL, "hosc");
+            name = "hosc";
+            break;
+        case 1:
+            source_clock = clk_get(NULL, "sdram_pll_p");
+            name = "sdram_pll_p";
+            break;
+        case 2:
+            source_clock = clk_get(NULL, "sata_pll");
+            name = "sata_pll";
+            break;
+        default:
+            return -1;
+    }
+
+    if (IS_ERR(source_clock)) {
+        ret = PTR_ERR(source_clock);
+        spi_err("%s: Unable to get spi%d source clock resource\n",
+                __func__, aw_spi->master->bus_num);
+        return -1;
+    }
+
+    if (clk_set_parent(aw_spi->mclk, source_clock)) {
+        spi_err("%s: spi%d clk_set_parent failed\n", __func__,
+                aw_spi->master->bus_num);
+        ret = -1;
+        goto out;
+    }
+
+    if (clk_set_rate(aw_spi->mclk, mod_clk)) {
+        spi_err("%s: spi%d clk_set_rate failed\n", __func__,
+                aw_spi->master->bus_num);
+        ret = -1;
+        goto out;
+    }
+
+    spi_inf("%s: spi%d source = %s, src_clk = %u, mclk %u\n",
+            __func__, aw_spi->master->bus_num,  name,
+            (unsigned)clk_get_rate(source_clock), (unsigned)clk_get_rate(aw_spi->mclk));
+
+    if (clk_enable(aw_spi->mclk)) {
+        spi_err("%s: spi%d couldn't enable module clock 'spi'\n",
+                __func__, aw_spi->master->bus_num);
+        ret = -EBUSY;
+        goto out;
+    }
+
+    ret = 0;
+out:
+    clk_put(source_clock);
+    return ret;
+}
+
+static int sun7i_spi_hw_init(struct sun7i_spi *aw_spi)
+{
+    void *base_addr = aw_spi->base_addr;
+    unsigned long sclk_freq = 0;
+    char *mclk_name[] = {"spi0", "spi1", "spi2", "spi3"};
+
+    aw_spi->mclk = clk_get(&aw_spi->pdev->dev, mclk_name[aw_spi->pdev->id]);
+    if (IS_ERR(aw_spi->mclk)) {
+        spi_err("%s: spi%d unable to acquire module clock 'spi'\n",
+                __func__, aw_spi->master->bus_num);
+        return -1;
+    }
+    if (sun7i_spi_set_mclk(aw_spi, 100000000)) {
+        spi_err("%s: spi%d sun7i_spi_set_mclk 'spi'\n",
+                __func__, aw_spi->master->bus_num);
+        clk_put(aw_spi->mclk);
+        return -1;
+    }
+
+    /* 1. enable the spi module */
+    spi_enable_bus(base_addr);
+
+    /* 2. set the default chip select */
+    if (sun7i_spi_check_cs(0, aw_spi)) {
+        spi_set_cs(1, base_addr);
+    } else {
+        spi_set_cs(0, base_addr);
+    }
+
+    /*
+     * 3. master: set spi module clock;
+     * 4. set the default frequency 10MHz
+     */
+    spi_set_master(base_addr);
+    sclk_freq  = clk_get_rate(aw_spi->mclk);
+    spi_set_clk(10000000, sclk_freq, base_addr);
+
+    /*
+     * 5. master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0.
+     * 6. set bit width-default: 8 bits
+     */
+    spi_config(1, SPI_MODE_0, base_addr);
+
+    /* 7. manual control the chip select */
+    spi_ss_ctrl(base_addr, 1);
+    return 0;
+}
+
+static int sun7i_spi_hw_exit(struct sun7i_spi *aw_spi)
+{
+    /* disable the spi controller */
+    spi_disable_bus(aw_spi->base_addr);
+    /* disable module clock */
+    clk_disable(aw_spi->mclk);
+    clk_put(aw_spi->mclk);
+
+    return 0;
+}
+
+static int __devinit sun7i_spi_probe(struct platform_device *pdev)
+{
+    struct resource *mem_res, *dma_res_rx, *dma_res_tx;
+    struct sun7i_spi *aw_spi;
+    struct sun7i_spi_platform_data *pdata;
+    struct spi_master *master;
+    int ret = 0, err = 0, irq;
+    int cs_bitmap = 0;
+
+    spi_inf("%s: sun7i spi probe", __func__);
+
+    if (pdev->id < 0) {
+        spi_err("%s: invalid platform device id: %d\n", __func__, pdev->id);
+        return -ENODEV;
+    }
+
+    if (pdev->dev.platform_data == NULL) {
+        spi_err("%s: platform_data missing\n", __func__);
+        return -ENODEV;
+    }
+
+    pdata = pdev->dev.platform_data;
+    if (!pdata->clk_name) {
+        spi_err("%s: platform data must initialize\n", __func__);
+        return -EINVAL;
+    }
+
+    /* Check for availability of necessary resource */
+    dma_res_rx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+    if (dma_res_rx == NULL) {
+        spi_err("%s: Unable to get spi DMA RX resource\n", __func__);
+        return -ENXIO;
+    }
+
+    dma_res_tx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+    if (dma_res_tx == NULL) {
+        spi_err("%s: Unable to get spi DMA TX resource\n", __func__);
+        return -ENXIO;
+    }
+
+    mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+    if (mem_res == NULL) {
+        spi_err("%s: Unable to get spi MEM resource\n", __func__);
+        return -ENXIO;
+    }
+
+    irq = platform_get_irq(pdev, 0);
+    if (irq < 0) {
+        spi_err("%s: NO SPI IRQ specified\n", __func__);
+        return -ENXIO;
+    }
+
+    /* create spi master */
+    master = spi_alloc_master(&pdev->dev, sizeof(struct sun7i_spi));
+    if (master == NULL) {
+        spi_err("%s: Unable to allocate SPI Master\n", __func__);
+        return -ENOMEM;
+    }
+
+    platform_set_drvdata(pdev, master);
+    aw_spi = spi_master_get_devdata(master);
+    memset(aw_spi, 0, sizeof(struct sun7i_spi));
+
+    aw_spi->master          = master;
+    aw_spi->irq             = irq;
+#ifdef CONFIG_SUN7I_SPI_NDMA
+	aw_spi->dma_type        = CHAN_NORMAL;
+#else
+	aw_spi->dma_type        = CHAN_DEDICATE;
+#endif
+	spi_inf("%s: spi%d dma type: %s\n", __func__, pdev->id,
+			aw_spi->dma_type == CHAN_NORMAL ? "normal" : "dedicate");
+    aw_spi->dma_id_tx       = dma_res_tx->start;
+    aw_spi->dma_id_rx       = dma_res_rx->start;
+    aw_spi->dma_hdle_rx     = 0;
+    aw_spi->dma_hdle_tx     = 0;
+    aw_spi->cs_control      = sun7i_spi_cs_control;
+    aw_spi->cs_bitmap       = pdata->cs_bitmap; /* cs0-0x1; cs1-0x2; cs0&cs1-0x3. */
+    aw_spi->busy            = SPI_FREE;
+    aw_spi->duplex_flag     = DUPLEX_NULL;
+
+    master->bus_num         = pdev->id;
+    master->setup           = sun7i_spi_setup;
+    master->cleanup         = sun7i_spi_cleanup;
+    master->transfer        = sun7i_spi_transfer;
+    master->num_chipselect  = pdata->num_cs;
+    /* the spi->mode bits understood by this driver: */
+    master->mode_bits       = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
+
+    /* update the cs bitmap */
+    cs_bitmap = sun7i_spi_get_cfg_csbitmap(pdev->id);
+    if (cs_bitmap & 0x3) {
+        aw_spi->cs_bitmap = cs_bitmap & 0x3;
+        spi_inf("%s: spi%d cs bitmap: 0x%x\n",
+                __func__, master->bus_num, cs_bitmap);
+    }
+
+    err = request_irq(aw_spi->irq, sun7i_spi_handler, IRQF_DISABLED, pdev->name, aw_spi);
+    if (err) {
+        spi_err("%s: Cannot claim IRQ\n", __func__);
+        goto err0;
+    }
+
+    if (request_mem_region(mem_res->start,
+                           resource_size(mem_res), pdev->name) == NULL) {
+        spi_err("%s: Req mem region failed\n", __func__);
+        ret = -ENXIO;
+        goto err1;
+    }
+
+    aw_spi->base_addr = ioremap(mem_res->start, resource_size(mem_res));
+    if (aw_spi->base_addr == NULL) {
+        spi_err("%s: Unable to remap IO\n", __func__);
+        ret = -ENXIO;
+        goto err2;
+    }
+
+    /* Setup clocks */
+    aw_spi->hclk = clk_get(&pdev->dev, pdata->clk_name);
+    if (IS_ERR(aw_spi->hclk)) {
+        spi_err("%s: Unable to get clock 'spi'\n", __func__);
+        ret = PTR_ERR(aw_spi->hclk);
+        goto err3;
+    }
+
+    if (clk_enable(aw_spi->hclk)) {
+        spi_err("%s: Couldn't enable clock 'spi'\n", __func__);
+        ret = -EBUSY;
+        goto err4;
+    }
+
+    aw_spi->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent));
+    if (aw_spi->workqueue == NULL) {
+        spi_err("%s: Unable to create workqueue\n", __func__);
+        ret = -ENOMEM;
+        goto err5;
+    }
+
+    aw_spi->pdev = pdev;
+
+    /* Setup Deufult Mode */
+    sun7i_spi_hw_init(aw_spi);
+
+#ifndef SYS_SPI_PIN
+    /* set gpio */
+    gpio_addr = ioremap(_PIO_BASE_ADDRESS, 0x1000);
+#endif
+    sun7i_spi_set_gpio(aw_spi, 1);
+
+    spin_lock_init(&aw_spi->lock);
+    init_completion(&aw_spi->done);
+    INIT_WORK(&aw_spi->work, sun7i_spi_work); /* banding the process handler */
+    INIT_LIST_HEAD(&aw_spi->queue);
+
+    if (spi_register_master(master)) {
+        spi_err("%s: Cannot register SPI master\n", __func__);
+        ret = -EBUSY;
+        goto err6;
+    }
+
+    spi_inf("%s: reuuimlla's SoC SPI Driver loaded for Bus SPI%d with %d Slaves at most\n",
+            __func__, pdev->id, master->num_chipselect);
+    spi_inf("%s: spi%d driver probe succeed, base %p, irq %d, dma_id_rx %d, dma_id_tx %d\n",
+            __func__, master->bus_num, aw_spi->base_addr, aw_spi->irq,
+            aw_spi->dma_id_rx, aw_spi->dma_id_tx);
+    return 0;
+
+err6:
+    destroy_workqueue(aw_spi->workqueue);
+err5:
+    iounmap((void *)aw_spi->base_addr);
+err4:
+err3:
+err2:
+    release_mem_region(mem_res->start, resource_size(mem_res));
+err1:
+    free_irq(aw_spi->irq, aw_spi);
+err0:
+    platform_set_drvdata(pdev, NULL);
+    spi_master_put(master);
+    return ret;
+}
+
+static int sun7i_spi_remove(struct platform_device *pdev)
+{
+    struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+    struct sun7i_spi *aw_spi = spi_master_get_devdata(master);
+    struct resource *mem_res;
+    unsigned long flags;
+
+    spi_dbg("%s: sun7i spi removal", __func__);
+
+    spin_lock_irqsave(&aw_spi->lock, flags);
+
+    aw_spi->busy |= SPI_FREE;
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+
+    while (aw_spi->busy & SPI_BUSY) {
+        spi_inf("%s: spi%d busy\n", __func__, master->bus_num);
+        msleep(10);
+    }
+
+    sun7i_spi_hw_exit(aw_spi);
+    spi_unregister_master(master);
+    destroy_workqueue(aw_spi->workqueue);
+
+    clk_disable(aw_spi->hclk);
+    clk_put(aw_spi->hclk);
+
+    iounmap((void *) aw_spi->base_addr);
+    mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+    if (mem_res != NULL)
+        release_mem_region(mem_res->start, resource_size(mem_res));
+    free_irq(aw_spi->irq, aw_spi);
+    platform_set_drvdata(pdev, NULL);
+    spi_master_put(master);
+    spi_inf("%s: sun7i spi removed", __func__);
+
+    return 0;
+}
+
+#ifdef CONFIG_PM
+static int sun7i_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+    struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+    struct sun7i_spi *aw_spi = spi_master_get_devdata(master);
+    unsigned long flags;
+
+    spin_lock_irqsave(&aw_spi->lock, flags);
+
+    aw_spi->busy |= SPI_SUSPND;
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+
+    while (aw_spi->busy & SPI_BUSY) {
+        spi_inf("%s: spi%d busy\n", __func__, master->bus_num);
+        msleep(10);
+    }
+
+    /* Disable the clock */
+    clk_disable(aw_spi->hclk);
+
+    spi_inf("%s: spi%d suspend okay...\n", __func__, master->bus_num);
+    return 0;
+}
+
+static int sun7i_spi_resume(struct platform_device *pdev)
+{
+    struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+    struct sun7i_spi  *aw_spi = spi_master_get_devdata(master);
+    unsigned long flags;
+
+    /* Enable the clock */
+    clk_enable(aw_spi->hclk);
+    sun7i_spi_hw_init(aw_spi);
+
+    spin_lock_irqsave(&aw_spi->lock, flags);
+
+    aw_spi->busy = SPI_FREE;
+    spin_unlock_irqrestore(&aw_spi->lock, flags);
+
+    spi_inf("%s: spi%d resume okay...\n", __func__, master->bus_num);
+    return 0;
+}
+#else
+#define sun7i_spi_suspend   NULL
+#define sun7i_spi_resume    NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver sun7i_spi_driver = {
+    .probe   = sun7i_spi_probe,
+    .remove  = sun7i_spi_remove,
+    .suspend = sun7i_spi_suspend,
+    .resume  = sun7i_spi_resume,
+    .driver = {
+        .name   = "sun7i-spi",
+        .owner = THIS_MODULE,
+    },
+};
+
+/* spi resouce and platform data start */
+struct sun7i_spi_platform_data sun7i_spi0_pdata = {
+    .cs_bitmap  = 0x3,
+    .num_cs     = 2,
+    .clk_name   = "ahb_spi0",
+};
+
+static struct resource sun7i_spi0_resources[] = {
+    [0] = {
+        .start  = SPI0_BASE_ADDR,
+        .end    = SPI0_BASE_ADDR + 1024,
+        .flags  = IORESOURCE_MEM,
+    },
+#ifdef CONFIG_SUN7I_SPI_NDMA
+    [1] = {
+        .start  = N_SRC_SPI0_RX,
+        .end    = N_SRC_SPI0_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = N_DST_SPI0_TX,
+        .end    = N_DST_SPI0_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#else
+    [1] = {
+        .start  = D_SRC_SPI0_RX,
+        .end    = D_SRC_SPI0_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = D_DST_SPI0_TX,
+        .end    = D_DST_SPI0_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#endif
+    [3] = {
+        .start	= SW_INT_IRQNO_SPI00,
+        .end	= SW_INT_IRQNO_SPI00,
+        .flags  = IORESOURCE_IRQ,
+    }
+};
+
+static struct platform_device sun7i_spi0_device = {
+    .name           = "sun7i-spi",
+    .id             = 0,
+    .num_resources  = ARRAY_SIZE(sun7i_spi0_resources),
+    .resource       = sun7i_spi0_resources,
+    .dev            = {
+        .platform_data = &sun7i_spi0_pdata,
+    },
+};
+
+struct sun7i_spi_platform_data sun7i_spi1_pdata = {
+    .cs_bitmap  = 0x3,
+    .num_cs     = 2,
+    .clk_name   = "ahb_spi1",
+};
+
+static struct resource sun7i_spi1_resources[] = {
+    [0] = {
+        .start  = SPI1_BASE_ADDR,
+        .end    = SPI1_BASE_ADDR + 1024,
+        .flags  = IORESOURCE_MEM,
+    },
+#ifdef CONFIG_SUN7I_SPI_NDMA
+    [1] = {
+        .start  = N_SRC_SPI1_RX,
+        .end    = N_SRC_SPI1_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = N_DST_SPI1_TX,
+        .end    = N_DST_SPI1_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#else
+    [1] = {
+        .start  = D_SRC_SPI1_RX,
+        .end    = D_SRC_SPI1_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = N_DST_SPI1_TX,
+        .end    = N_DST_SPI1_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#endif
+    [3] = {
+        .start	= SW_INT_IRQNO_SPI01,
+        .end	= SW_INT_IRQNO_SPI01,
+        .flags  = IORESOURCE_IRQ,
+    }
+};
+
+static struct platform_device sun7i_spi1_device = {
+    .name           = "sun7i-spi",
+    .id             = 1,
+    .num_resources  = ARRAY_SIZE(sun7i_spi1_resources),
+    .resource       = sun7i_spi1_resources,
+    .dev            = {
+        .platform_data = &sun7i_spi1_pdata,
+    },
+};
+
+static struct resource sun7i_spi2_resources[] = {
+    [0] = {
+        .start  = SPI2_BASE_ADDR,
+        .end    = SPI2_BASE_ADDR + 1024,
+        .flags  = IORESOURCE_MEM,
+    },
+#ifdef CONFIG_SUN7I_SPI_NDMA
+    [1] = {
+        .start  = N_SRC_SPI2_RX,
+        .end    = N_SRC_SPI2_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = N_DST_SPI2_TX,
+        .end    = N_DST_SPI2_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#else
+    [1] = {
+        .start  = D_SRC_SPI2_RX,
+        .end    = D_SRC_SPI2_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = N_DST_SPI2_TX,
+        .end    = N_DST_SPI2_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#endif
+    [3] = {
+        .start	= SW_INT_IRQNO_SPI02,
+        .end	= SW_INT_IRQNO_SPI02,
+        .flags  = IORESOURCE_IRQ,
+    }
+};
+
+struct sun7i_spi_platform_data sun7i_spi2_pdata = {
+    .cs_bitmap  = 0x3,
+    .num_cs     = 2,
+    .clk_name   = "ahb_spi2",
+};
+
+static struct platform_device sun7i_spi2_device = {
+    .name           = "sun7i-spi",
+    .id             = 2,
+    .num_resources  = ARRAY_SIZE(sun7i_spi2_resources),
+    .resource       = sun7i_spi2_resources,
+    .dev            = {
+        .platform_data = &sun7i_spi2_pdata,
+    },
+};
+
+static struct resource sun7i_spi3_resources[] = {
+    [0] = {
+        .start  = SPI3_BASE_ADDR,
+        .end    = SPI3_BASE_ADDR + 1024,
+        .flags  = IORESOURCE_MEM,
+    },
+#ifdef CONFIG_SUN7I_SPI_NDMA
+    [1] = {
+        .start  = N_SRC_SPI3_RX,
+        .end    = N_SRC_SPI3_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = N_DST_SPI3_TX,
+        .end    = N_DST_SPI3_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#else
+    [1] = {
+        .start  = D_SRC_SPI3_RX,
+        .end    = D_SRC_SPI3_RX,
+        .flags  = IORESOURCE_DMA,
+    },
+    [2] = {
+        .start  = N_DST_SPI3_TX,
+        .end    = N_DST_SPI3_TX,
+        .flags  = IORESOURCE_DMA,
+    },
+#endif
+    [3] = {
+        .start	= SW_INT_IRQNO_SPI3,
+        .end	= SW_INT_IRQNO_SPI3,
+        .flags  = IORESOURCE_IRQ,
+    }
+};
+
+struct sun7i_spi_platform_data sun7i_spi3_pdata = {
+    .cs_bitmap  = 0x3,
+    .num_cs     = 2,
+    .clk_name = "ahb_spi3",
+};
+
+static struct platform_device sun7i_spi3_device = {
+    .name           = "sun7i-spi",
+    .id             = 3,
+    .num_resources  = ARRAY_SIZE(sun7i_spi3_resources),
+    .resource       = sun7i_spi3_resources,
+    .dev            = {
+        .platform_data = &sun7i_spi3_pdata,
+    },
+};
+
+static struct spi_board_info *spi_boards = NULL;
+static int sun7i_spi_register_spidev(void)
+{
+    int spi_dev_num = 0;
+    int ret = 0;
+    int i = 0;
+    unsigned int irq_gpio = 0;
+    char spi_board_name[32] = {0};
+    struct spi_board_info* board;
+
+    ret = script_parser_fetch("spi_devices", "spi_dev_num", &spi_dev_num, sizeof(int));
+    if(ret != SCRIPT_PARSER_OK){
+        spi_err("Get spi devices number failed\n");
+        return -1;
+    }
+    spi_inf("Found %d spi devices in config files\n", spi_dev_num);
+
+    /* alloc spidev board information structure */
+    spi_boards = (struct spi_board_info*)kzalloc(sizeof(struct spi_board_info) * spi_dev_num, GFP_KERNEL);
+    if (spi_boards == NULL)
+    {
+        spi_err("Alloc spi board information failed \n");
+        return -1;
+    }
+
+    spi_inf("%-10s %-16s %-16s %-8s %-4s %-4s\n", "boards num", "modalias", "max_spd_hz", "bus_num", "cs", "mode");
+    for (i=0; i<spi_dev_num; i++)
+    {
+        board = &spi_boards[i];
+        sprintf(spi_board_name, "spi_board%d", i);
+        ret = script_parser_fetch(spi_board_name, "modalias", (void*)board->modalias, sizeof(char*));
+        if(ret != SCRIPT_PARSER_OK) {
+            spi_err("Get spi devices modalias failed\n");
+            goto fail;
+        }
+        ret = script_parser_fetch(spi_board_name, "max_speed_hz", (void*)&board->max_speed_hz, sizeof(int));
+        if(ret != SCRIPT_PARSER_OK) {
+            spi_err("Get spi devices max_speed_hz failed\n");
+            goto fail;
+        }
+        ret = script_parser_fetch(spi_board_name, "bus_num", (void*)&board->bus_num, sizeof(u16));
+        if(ret != SCRIPT_PARSER_OK) {
+            spi_err("Get spi devices bus_num failed\n");
+            goto fail;
+        }
+        ret = script_parser_fetch(spi_board_name, "chip_select", (void*)&board->chip_select, sizeof(u16));
+        if(ret != SCRIPT_PARSER_OK) {
+            spi_err("Get spi devices chip_select failed\n");
+            goto fail;
+        }
+        ret = script_parser_fetch(spi_board_name, "mode", (void*)&board->mode, sizeof(u8));
+        if(ret != SCRIPT_PARSER_OK) {
+            spi_err("Get spi devices mode failed\n");
+            goto fail;
+        }
+	ret = script_parser_fetch(spi_board_name, "irq_gpio", (void*)&irq_gpio, sizeof(unsigned int));
+	if (ret != SCRIPT_PARSER_OK) {
+		spi_inf("%s irq gpio not used\n", spi_board_name);
+		board->irq = -1;
+	} else if(gpio_request(irq_gpio, spi_board_name) == 0)
+		board->irq = gpio_to_irq(irq_gpio);
+        /*
+        ret = script_parser_fetch(spi_board_name, "full_duplex", &board->full_duplex, sizeof(int));
+        if(ret != SCRIPT_PARSER_OK) {
+            spi_err("Get spi devices full_duplex failed\n");
+            goto fail;
+        }
+        ret = script_parser_fetch(spi_board_name, "manual_cs", &board->manual_cs, sizeof(int));
+        if(ret != SCRIPT_PARSER_OK) {
+            spi_err("Get spi devices manual_cs failed\n");
+            goto fail;
+        }
+        */
+        spi_inf("%-10d %-16s %-16d %-8d %-4d 0x%-4d\n", i, board->modalias, board->max_speed_hz, board->bus_num, board->chip_select, board->mode);
+    }
+
+    /* register boards */
+    ret = spi_register_board_info(spi_boards, spi_dev_num);
+    if (ret)
+    {
+        spi_err("Register board information failed\n");
+        goto fail;
+    }
+    return 0;
+fail:
+    if (spi_boards)
+    {
+        kfree(spi_boards);
+        spi_boards = NULL;
+    }
+    return -1;
+
+}
+
+static int sun7i_spi_get_cfg_csbitmap(int bus_num)
+{
+    int value = 0;
+    int ret   = 0;
+    char *main_name[] = {"spi0_para", "spi1_para", "spi2_para", "spi3_para"};
+    char *sub_name = "spi_cs_bitmap";
+    ret = script_parser_fetch(main_name[bus_num], sub_name, &value, sizeof(int));
+    if(ret != SCRIPT_PARSER_OK){
+        spi_err("get spi %d para failed, err code = %d \n", bus_num, ret);
+        return 0;
+    }
+    spi_inf("bus num = %d, spi used = %d \n", bus_num, value);
+    return value;
+
+}
+
+#ifdef CONFIG_SUN7I_SPI_NORFLASH
+#include <linux/spi/flash.h>
+#include <linux/mtd/partitions.h>
+
+/*struct mtd_partition part = {
+	.name = "p1",
+	.size = 8388608,
+	.offset = 0,
+};*/
+
+static struct flash_platform_data at25df641_info = {
+    .name = "m25p80",
+    .parts = NULL,
+    .nr_parts = 0,
+    .type = "at25df641",
+};
+
+static struct spi_board_info norflash = {
+    .modalias = "m25p80",
+    .platform_data = &at25df641_info,
+    .mode = SPI_MODE_0,
+    .irq = 0,
+    .max_speed_hz = 10 * 1000 * 1000,
+    .bus_num = 0,
+    .chip_select = 0,
+};
+
+static void __init sun7i_spi_norflash(void)
+{
+    if (spi_register_board_info(&norflash, 1)) {
+        spi_err("%s: Register norflash:%s information failed\n",
+                __func__, at25df641_info.type);
+    } else {
+        spi_inf("%s: Register norflash:%s information OK\n",
+                __func__, at25df641_info.type);
+    }
+}
+#else
+static void __init sun7i_spi_norflash(void)
+{}
+#endif /* CONFIG_SUN7I_SPI_NORFLASH */
+
+/* get configuration in the script */
+#define SPI0_USED_MASK 0x1
+#define SPI1_USED_MASK 0x2
+#define SPI2_USED_MASK 0x4
+#define SPI3_USED_MASK 0x8
+static int spi_used = 0;
+
+static int __init spi_sun7i_init(void)
+{
+    int used = 0;
+    int i = 0;
+    int ret = 0;
+    char spi_para[16] = {0};
+
+    spi_dbg("sw spi init !!\n");
+    spi_used = 0;
+    for (i=0; i<4; i++)
+    {
+        used = 0;
+        sprintf(spi_para, "spi%d_para", i);
+        ret = script_parser_fetch(spi_para, "spi_used", &used, sizeof(int));
+        if (ret)
+        {
+            spi_err("sw spi init fetch spi%d uning configuration failed\n", i);
+            continue;
+        }
+        if (used)
+            spi_used |= 1 << i;
+    }
+
+    ret = sun7i_spi_register_spidev();
+    if (ret)
+    {
+        spi_err("register spi devices board info failed \n");
+    }
+
+//    sun7i_spi_norflash();
+
+    if (spi_used & SPI0_USED_MASK)
+        platform_device_register(&sun7i_spi0_device);
+    if (spi_used & SPI1_USED_MASK)
+        platform_device_register(&sun7i_spi1_device);
+    if (spi_used & SPI2_USED_MASK)
+        platform_device_register(&sun7i_spi2_device);
+    if (spi_used & SPI3_USED_MASK)
+        platform_device_register(&sun7i_spi3_device);
+
+    if (spi_used)
+    {
+        return platform_driver_register(&sun7i_spi_driver);
+    }
+    else
+    {
+        pr_warning("spi: cannot find any using configuration for \
+                    all 4 spi controllers, return directly!\n");
+        return 0;
+    }
+}
+
+static void __exit spi_sun7i_exit(void)
+{
+    if (spi_used & SPI0_USED_MASK)
+        platform_device_unregister(&sun7i_spi0_device);
+    if (spi_used & SPI1_USED_MASK)
+        platform_device_unregister(&sun7i_spi1_device);
+    if (spi_used & SPI2_USED_MASK)
+        platform_device_unregister(&sun7i_spi2_device);
+    if (spi_used & SPI3_USED_MASK)
+        platform_device_unregister(&sun7i_spi3_device);
+
+    if (spi_used)
+        platform_driver_unregister(&sun7i_spi_driver);
+    kfree(spi_boards);
+}
+
+module_init(spi_sun7i_init);
+module_exit(spi_sun7i_exit);
+
+MODULE_AUTHOR("pannan");
+MODULE_DESCRIPTION("SUN7I SPI BUS Driver");
+MODULE_ALIAS("platform:sun7i-spi");
+MODULE_LICENSE("GPL");