I attached the Allwinner SPI driver from the 3.4 kernel. That would be
a good place to start when modifying it for mainline.

Look at other spi drivers in the mainline kernel.   drivers/spi/...
They will show you how to fix probe up to work with device trees.

On Sun, Aug 3, 2014 at 2:45 AM, bruce bushby <[email protected]> wrote:
> Hi
>
> I've been wanting to write an SPI skeleton driver to learn the basics. I'm
> hoping a driver guru could offer some guidance.
>
> The idea is to write a skeleton driver that simply prints the chip id
>
> Kernel: stock standard mainline 3.16.0-rc7 kernel on Olimex A20-SOM (EVK)
> Device: MPU9250 Break out board
>
> My thinking so far:
>
> 1. DTS
> Add an SPI slave device to my DTS file.
>
> I will connect the Break out board to the second SPI bus .... so need to add
> the slave device to SPI1
>
> Existing SPI1 device (from dts file)
>
>         spi1: spi@01c06000 {
>             pinctrl-names = "default";
>             pinctrl-0 = <&spi1_pins_a>;
>             status = "okay";
>         };
>
>
> Adding SPI1 slave device:
>
>         spi1: spi@01c06000 {
>             pinctrl-names = "default";
>             pinctrl-0 = <&spi1_pins_a>;
>             status = "okay";
>
>                         mpu9250@0 {
>                                  compatible = "mpu9250";
>                                  reg = <0>;
>                                  spi-max-frequency = <1000000>;
>                                  ???? interrupt pin .... gpio ???????
>                          };
>
>         };
> 2. The driver will create an entry in "sysfs" and a "corresponding" entry in
> /dev. Catting the device " /dev/mpu9250 " will return the device id.
>
> Then the driver will be a standard Linux kernel module with support for SPI
> ....
> #include <linux/module.h>
> #include <linux/spi/spi.h>
> ....
>
> struct spi_driver
> spi_register_driver  (struct device dev;   dictates the name of the device)
> spi_alloc_device
> spi_add_device
>
>
>
> Am I on the right track? Any similar simple examples floating about?
>
>
> Thanks
> Bruce
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.



-- 
Jon Smirl
[email protected]

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.
/*
 * (C) Copyright 2010-2015
 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
 *
 * Pan Nan <[email protected]>
 * Victor Wei <[email protected]>
 *
 * SUNXI SPI Platform 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/module.h>
#include <linux/init.h>
#include <linux/spinlock.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/spi/spi.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>

#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>

#include <asm/io.h>
#include <plat/dma.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <plat/sys_config.h>
#include <mach/spi.h>
#include <asm/cacheflush.h>

#define SUNXI_SPI_DEBUG

#ifdef SUNXI_SPI_DEBUG
#define spi_wrn(...)		do {printk("[spi]: %s(L%d) ", __FILE__, __LINE__); printk(__VA_ARGS__);} while(0)
#define spi_msg(...)		printk("[spi]: "__VA_ARGS__)
#else
#define spi_wrn(...)
#define spi_msg(...)
#endif


#if 0
static int hex_dump(char* str, void __iomem* buf, u32 len, u32 mode/*0-8bit, 1-16bit, 2-32bit*/)
{
    u32 i;
    u32 step[3] = {1, 2, 4};
    char* mod[3] = {"%02x ", "%04x ", "%08x "};

    printk("\nDump %s:", str);
    for (i=0; i<len; i+=step[mode])
    {
        if (!(i&0xf))
            printk("\n0x%p : ", buf + i);
        printk(mod[mode], readl(buf + i));
    }
    printk("\n");

    return 0;
}
#endif

#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

//#define AW1623_FPGA

struct sunxi_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;

	#ifdef CONFIG_SPI_SUNXI_NDMA
	enum sw_dma_ch dma_id;
	enum sw_dmadir dma_dir;
	int dma_hdle;
	#else
	enum sw_dma_ch dma_tx_id;
	enum sw_dma_ch dma_rx_id;
	int dma_tx_hdle;
	int dma_rx_hdle;
	#endif

	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 aw_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);

        //spi_msg("Chip Select set succeed! cs = %d\n", chipselect);
        return AW_SPI_OK;
    }
    else
    {
        spi_wrn("Chip Select set fail! cs = %d\n", chipselect);
        return AW_SPI_FAIL;
    }
}

/* select dma type */
s32 aw_spi_sel_dma_type(u32 dma_type, void *base_addr)
{
    u32 reg_val = readl(base_addr + SPI_CTL_REG);

    //spi_msg("set dma type %s\n", dma_mode ? "ddma" : "ndma");
    reg_val &= ~SPI_CTL_DMAMOD;
    if (dma_type)
    {
        reg_val |= 1 << 5;
    }
    writel(reg_val, base_addr + SPI_CTL_REG);
    return AW_SPI_OK;
}

/* config spi */
void aw_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_msg("slave mode configurate control register.\n");
    }
    writel(reg_val, base_addr + SPI_CTL_REG);
    return;
}

/* restore reg data after transfer complete */
void aw_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_msg("control register set default value: %x \n", reg_val);
    writel(reg_val, base_addr + SPI_CTL_REG);
    return;
}

/* set spi clock */
void aw_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;

    //spi_msg("set spi clock %d, mclk %d\n", spi_clk, ahb_clk);
    reg_val = readl(base_addr + SPI_CLK_RATE_REG);

    /* CDR2 */
    if(div_clk <= SPI_CLK_SCOPE)
    {
        if (div_clk != 0)
        {
            div_clk--;
        }
        reg_val &= ~SPI_CLKCTL_CDR2;
        reg_val |= (div_clk|SPI_CLKCTL_DRS);
        //spi_msg("CDR2 - n = %d \n", div_clk);
    }
    else /* CDR1 */
    {
        //search 2^N
        while(1)
        {
            if(div_clk == 1)
            {
                break;
            }
            div_clk >>= 1;//divide by 2
            N++;
        };
        reg_val &= ~(SPI_CLKCTL_CDR1|SPI_CLKCTL_DRS);
        reg_val |= (N<<8);
        //spi_msg("CDR1 - n = %d \n", N);
    }
    writel(reg_val, base_addr + SPI_CLK_RATE_REG);
    return;
}

/* start spi transfer */
void aw_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 aw_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 aw_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 aw_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 aw_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 aw_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 aw_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 aw_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 aw_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 aw_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 aw_spi_qry_irq_pending(void *base_addr)
{
    return ( SPI_STAT_MASK & readl(base_addr + SPI_STATUS_REG) );
}

/* clear irq pending */
void aw_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 aw_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 aw_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 aw_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 aw_spi_set_bc_wtc(u32 bc, u32 tc, void *base_addr)
{
    u32 reg_val = readl(base_addr + SPI_BC_REG);
    reg_val &= ~SPI_BC_BC_MASK;
    reg_val |= ( SPI_BC_BC_MASK & bc );
    writel(reg_val, base_addr + SPI_BC_REG);
    //spi_msg("\n-- BC = %d --\n", 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 & tc);
    writel(reg_val, base_addr + SPI_TC_REG);
    //spi_msg("\n-- TC = %d --\n", readl(base_addr + SPI_TC_REG));
}

/* set ss control */
void aw_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 aw_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 aw_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 aw_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);
}


static int spi_sunxi_get_cfg_csbitmap(int bus_num);

/* flush d-cache */
static void spi_sunxi_cleanflush_dcache_region(void *addr, __u32 len)
{
	__cpuc_flush_dcache_area(addr, len + (1 << 5) * 2 - 2);
}


/* ------------------------------- dma operation start----------------------------- */
#if defined CONFIG_ARCH_SUN4I
static struct sw_dma_client spi_dma_client[] = {
	[0] = {
	    .name		= "sun4i-spi0",
	},
	[1] = {
	    .name		= "sun4i-spi1",
	},
	[2] = {
	    .name		= "sun4i-spi2",
	},
    [3] = {
	    .name		= "sun4i-spi3",
	}
};
#elif defined CONFIG_ARCH_SUN5I
static struct sw_dma_client spi_dma_client[] = {
	[0] = {
	    .name		= "sun5i-spi0",
	},
	[1] = {
	    .name		= "sun5i-spi1",
	},
	[2] = {
	    .name		= "sun5i-spi2",
	}
};
#endif

#ifdef CONFIG_SPI_SUNXI_NDMA
/*
 * rx dma callback, disable the 1/4 fifo rx drq.
 * tx dma callback, disable the tx empty drq.
 * it can do this in the isr,too.
 * @ buf: client's id
 * @ size:
 * @ result:
 */
static void spi_sunxi_dma_cb(struct sw_dma_chan *dma_ch, void *buf, int size, enum sw_dma_buffresult result)
{
	struct sunxi_spi *aw_spi = (struct sunxi_spi *)buf;
	unsigned long flags;

    //spi_msg("spi_sunxi_dma_cb\n");
	spin_lock_irqsave(&aw_spi->lock, flags);
	if (result != SW_RES_OK) {
	    spi_wrn("spi dma callback: fail NO. = %d\n", result);
	    spin_unlock_irqrestore(&aw_spi->lock, flags);
		return;
	}
	if(aw_spi->dma_dir == SW_DMA_RDEV) {
        //spi_msg("spi dma rx -read data\n");
        aw_spi_disable_dma_irq(SPI_DRQEN_RHF, aw_spi->base_addr);
    }
    else if(aw_spi->dma_dir == SW_DMA_WDEV) {
        //spi_msg("spi dma tx -write data\n");
        aw_spi_disable_dma_irq(SPI_DRQEN_THE, aw_spi->base_addr);
    }
    else {
        spi_wrn("unknow dma direction = %d \n",aw_spi->dma_dir);
    }
    aw_spi_sel_dma_type(0, aw_spi->base_addr);
	spin_unlock_irqrestore(&aw_spi->lock, flags);
}
#else
/*
 * tx dma callback, disable the tx empty drq.
 * it can do this in the isr,too.
 * @ buf: client's id
 * @ size:
 * @ result:
 */
static void spi_sunxi_dma_tx_cb(struct sw_dma_chan *dma_ch, void *buf, int size, enum sw_dma_buffresult result)
{
	struct sunxi_spi *aw_spi = (struct sunxi_spi *)buf;
	unsigned long flags;

    //spi_msg("spi_sunxi_dma_cb\n");
	spin_lock_irqsave(&aw_spi->lock, flags);
	if (result != SW_RES_OK) {
	    spi_wrn("spi dma callback: fail NO. = %d\n", result);
	    spin_unlock_irqrestore(&aw_spi->lock, flags);
		return;
	}

    //spi_msg("spi dma tx -write data\n");
    aw_spi_disable_dma_irq(SPI_DRQEN_THE, aw_spi->base_addr);
    aw_spi_sel_dma_type(0, aw_spi->base_addr);
	spin_unlock_irqrestore(&aw_spi->lock, flags);
}


/*
 * rx dma callback, disable the 1/4 fifo rx drq.
 * it can do this in the isr,too.
 * @ buf: client's id
 * @ size:
 * @ result:
 */
static void spi_sunxi_dma_rx_cb(struct sw_dma_chan *dma_ch, void *buf, int size, enum sw_dma_buffresult result)
{
	struct sunxi_spi *aw_spi = (struct sunxi_spi *)buf;
	unsigned long flags;

    //spi_msg("spi_sunxi_dma_cb\n");
	spin_lock_irqsave(&aw_spi->lock, flags);
	if (result != SW_RES_OK) {
	    spi_wrn("spi dma callback: fail NO. = %d\n", result);
	    spin_unlock_irqrestore(&aw_spi->lock, flags);
		return;
	}
    //spi_msg("spi dma rx -read data\n");
    aw_spi_disable_dma_irq(SPI_DRQEN_RHF, aw_spi->base_addr);
    aw_spi_sel_dma_type(0, aw_spi->base_addr);
	spin_unlock_irqrestore(&aw_spi->lock, flags);
}
#endif

/*
 * config dma src and dst address,
 * io or linear address,
 * drq type,
 * then enqueue
 * but not trigger dma start
 */
static int spi_sunxi_config_dma(struct sunxi_spi *aw_spi, enum sw_dmadir dma_dir, void *buf, unsigned len)
{
    int ret = 0;
    int bus_num = aw_spi->master->bus_num;
#if defined CONFIG_ARCH_SUN4I
    unsigned char spi_drq[] = {DRQ_TYPE_SPI0, DRQ_TYPE_SPI1, DRQ_TYPE_SPI2, DRQ_TYPE_SPI3};
    unsigned long spi_phyaddr[] = {SPI0_BASE_ADDR, SPI1_BASE_ADDR, SPI2_BASE_ADDR, SPI3_BASE_ADDR};/* physical address */
#elif defined CONFIG_ARCH_SUN5I
    unsigned char spi_drq[] = {DRQ_TYPE_SPI0, DRQ_TYPE_SPI1, DRQ_TYPE_SPI2};
    unsigned long spi_phyaddr[] = {SPI0_BASE_ADDR, SPI1_BASE_ADDR, SPI2_BASE_ADDR};/* physical address */
#endif
    struct dma_hw_conf spi_hw_conf = {0};

     #ifdef CONFIG_SPI_SUNXI_NDMA
    //write
    if (dma_dir == SW_DMA_WDEV) {
	    spi_hw_conf.drqsrc_type  = N_DRQSRC_SDRAM;               // must be sdram,or sdram
	    spi_hw_conf.drqdst_type  = spi_drq[bus_num];             // spi drq type
	    spi_hw_conf.dir          = SW_DMA_WDEV;                  //transmit data to device
	    spi_hw_conf.address_type = DMAADDRT_D_IO_S_LN;           //dest io, src linear
        spi_hw_conf.to           = spi_phyaddr[bus_num] + SPI_TXDATA_REG; // physical address, destination address
	}//read
	else if (dma_dir == SW_DMA_RDEV) {
	    spi_hw_conf.drqsrc_type  = spi_drq[bus_num];    // spi drq type
	    spi_hw_conf.drqdst_type  = N_DRQDST_SDRAM;      // must be sdram ?? what about sram ?
	    spi_hw_conf.dir          = SW_DMA_RDEV;         // receive data from device
	    spi_hw_conf.address_type = DMAADDRT_D_LN_S_IO; // dest linear, src io
	    spi_hw_conf.from         = spi_phyaddr[bus_num] + SPI_RXDATA_REG; // physical address, source address
	}
	else {
	    spi_wrn("[spi-%d]: unknow dma direction = %d \n", bus_num, dma_dir);
	    return -1;
	}
    #else
    //write
    if (dma_dir == SW_DMA_WDEV) {
	    spi_hw_conf.drqsrc_type  = D_DRQSRC_SDRAM;               // must be sdram,or sdram
	    spi_hw_conf.drqdst_type  = spi_drq[bus_num];             // spi drq type
	    spi_hw_conf.dir          = SW_DMA_WDEV;                  //transmit data to device
	    spi_hw_conf.address_type = DMAADDRT_D_IO_S_LN;           //dest io, src linear
        spi_hw_conf.to           = spi_phyaddr[bus_num] + SPI_TXDATA_REG; // physical address, destination address
	}//read
	else if (dma_dir == SW_DMA_RDEV) {
	    spi_hw_conf.drqsrc_type  = spi_drq[bus_num];    // spi drq type
	    spi_hw_conf.drqdst_type  = D_DRQDST_SDRAM;      // must be sdram ?? what about sram ?
	    spi_hw_conf.dir          = SW_DMA_RDEV;         // receive data from device
	    spi_hw_conf.address_type = DMAADDRT_D_LN_S_IO; // dest linear, src io
	    spi_hw_conf.from         = spi_phyaddr[bus_num] + SPI_RXDATA_REG; // physical address, source address
	}
	else {
	    spi_wrn("[spi-%d]: unknow dma direction = %d \n", bus_num, dma_dir);
	    return -1;
	}
    spi_hw_conf.cmbk        = 0x07070707;
    #endif

    spi_hw_conf.xfer_type   = DMAXFER_D_SWORD_S_SWORD;
    spi_hw_conf.hf_irq      = SW_DMA_IRQ_FULL;

     #ifdef CONFIG_SPI_SUNXI_NDMA
    /* set src,dst, drq type,configuration */
    ret = sw_dma_config(aw_spi->dma_id, &spi_hw_conf);
    /* flush the transfer queue */
    ret += sw_dma_ctrl(aw_spi->dma_id, SW_DMAOP_FLUSH);
	#else
    /* set src,dst, drq type,configuration */
    ret = sw_dma_config((dma_dir == SW_DMA_WDEV) ? aw_spi->dma_tx_id : aw_spi->dma_rx_id, &spi_hw_conf);
    /* flush the transfer queue */
    ret += sw_dma_ctrl((dma_dir == SW_DMA_WDEV) ? aw_spi->dma_tx_id : aw_spi->dma_rx_id, SW_DMAOP_FLUSH);
	#endif

    /* 1. flush d-cache */
    spi_sunxi_cleanflush_dcache_region((void *)buf, len);
     #ifdef CONFIG_SPI_SUNXI_NDMA
    /* 2. enqueue dma transfer, --FIXME--: buf: virtual address, not physical address. */
    ret += sw_dma_enqueue(aw_spi->dma_id, (void *)aw_spi, (dma_addr_t)buf, len);
	#else
    /* 2. enqueue dma transfer, --FIXME--: buf: virtual address, not physical address. */
    ret += sw_dma_enqueue((dma_dir == SW_DMA_WDEV) ? aw_spi->dma_tx_id : aw_spi->dma_rx_id, (void *)aw_spi, (dma_addr_t)buf, len);
	#endif
    return ret;
}

/* set dma start flag, if queue, it will auto restart to transfer next queue */
static int spi_sunxi_start_dma(struct sunxi_spi *aw_spi, unsigned int dma_id)
{
    int ret = 0;
    /* change the state of the dma channel, dma start */
    ret = sw_dma_ctrl(dma_id, SW_DMAOP_START);
    /* set the channel's flags to a given state */
    ret += sw_dma_setflags(dma_id, SW_DMAF_AUTOSTART);
    return ret;
}

/* request dma channel and set callback function */
static int spi_sunxi_prepare_dma(struct sunxi_spi *aw_spi, enum sw_dmadir dma_dir)
{
    int ret = 0;
    int bus_num   = aw_spi->master->bus_num;
    #ifdef CONFIG_SPI_SUNXI_NDMA
    aw_spi->dma_hdle = sw_dma_request(aw_spi->dma_id, &spi_dma_client[bus_num], NULL);
    if(aw_spi->dma_hdle < 0) {
        spi_wrn("[spi-%d]: request dma failed!\n", bus_num);
        return aw_spi->dma_hdle;
    }
	ret = sw_dma_set_buffdone_fn(aw_spi->dma_hdle, spi_sunxi_dma_cb);
    /* make sure the queue safe */
	ret += sw_dma_setflags(aw_spi->dma_id, 0); // set flag ??? dma run status, mainly usd
	#else
	if (dma_dir == SW_DMA_WDEV) {
	    aw_spi->dma_tx_hdle = sw_dma_request(aw_spi->dma_tx_id, &spi_dma_client[bus_num], NULL);
		if(aw_spi->dma_tx_hdle < 0) {
		    spi_wrn("[spi-%d]: request dma tx failed!\n", bus_num);
		    return aw_spi->dma_tx_hdle;
		}
		ret = sw_dma_set_buffdone_fn(aw_spi->dma_tx_hdle, spi_sunxi_dma_tx_cb);
	    /* make sure the queue safe */
		ret += sw_dma_setflags(aw_spi->dma_tx_id, 0); // set flag ??? dma run status, mainly usd
    }
	else if (dma_dir == SW_DMA_RDEV) {
	    aw_spi->dma_rx_hdle = sw_dma_request(aw_spi->dma_rx_id, &spi_dma_client[bus_num], NULL);
		if(aw_spi->dma_rx_hdle < 0) {
		    spi_wrn("[spi-%d]: request dma rx failed!\n", bus_num);
		    return aw_spi->dma_rx_hdle;
		}
		ret = sw_dma_set_buffdone_fn(aw_spi->dma_rx_hdle, spi_sunxi_dma_rx_cb);
	    /* make sure the queue safe */
		ret += sw_dma_setflags(aw_spi->dma_rx_id, 0); // set flag ??? dma run status, mainly usd
	}
	else {
		spi_wrn("[spi-%d]: unknow dma direction = %d\n", bus_num, dma_dir);
	    return -1;
	}
	#endif
    //spi_msg("[spi-%d] request dma handle = %d, dmaid %d\n", bus_num, aw_spi->dma_hdle, aw_spi->dma_id);
    return ret;
}

/* release dma channel, and set queue status to idle. */
static int spi_sunxi_release_dma(struct sunxi_spi *aw_spi)
{
    int ret = 0;
    #ifdef CONFIG_SPI_SUNXI_NDMA
    ret  = sw_dma_ctrl(aw_spi->dma_id, SW_DMAOP_STOP); /* first stop */
    ret += sw_dma_setflags(aw_spi->dma_id, 0);
    ret += sw_dma_free(aw_spi->dma_id, &spi_dma_client[aw_spi->master->bus_num]);
    //spi_msg("[spi-%d] release dma,ret = %d \n", aw_spi->master->bus_num, ret);
    aw_spi->dma_hdle = -1;
	#else
    ret  = sw_dma_ctrl(aw_spi->dma_tx_id, SW_DMAOP_STOP); /* first stop */
    ret += sw_dma_ctrl(aw_spi->dma_rx_id, SW_DMAOP_STOP); /* first stop */
    ret += sw_dma_setflags(aw_spi->dma_tx_id, 0);
    ret += sw_dma_setflags(aw_spi->dma_rx_id, 0);
    ret += sw_dma_free(aw_spi->dma_tx_id, &spi_dma_client[aw_spi->master->bus_num]);
    ret += sw_dma_free(aw_spi->dma_rx_id, &spi_dma_client[aw_spi->master->bus_num]);
    //spi_msg("[spi-%d] release dma,ret = %d \n", aw_spi->master->bus_num, ret);
    aw_spi->dma_tx_hdle = -1;
    aw_spi->dma_rx_hdle = -1;
	#endif
    return ret;
}
/* ------------------------------dma operation end----------------------------- */


/* check the valid of cs id */
static int spi_sunxi_check_cs(int cs_id, struct sunxi_spi *aw_spi)
{
    int ret = AW_SPI_FAIL;
    switch(cs_id)
    {
        case 0:
            ret = (aw_spi->cs_bitmap & SPI_CHIP_SELECT_CS0) ? AW_SPI_OK : AW_SPI_FAIL;
            break;
        case 1:
            ret = (aw_spi->cs_bitmap & SPI_CHIP_SELECT_CS1) ? AW_SPI_OK : AW_SPI_FAIL;
            break;
        default:
            spi_wrn("chip select not support! cs = %d \n", cs_id);
            break;
    }
    return ret;
}

/* spi device on or off control */
static void spi_sunxi_cs_control(struct spi_device *spi, bool on)
{
	struct sunxi_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;
		}
		aw_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 spi_sunxi_xfer_setup(struct spi_device *spi, struct spi_transfer *t)
{
	/* get at the setup function, the properties of spi device */
	struct sunxi_spi *aw_spi = spi_master_get_devdata(spi->master);
	struct sunxi_spi_config *config = spi->controller_data; //allocate in the setup,and free in the cleanup
    void *__iomem base_addr = aw_spi->base_addr;
    //spi_msg("spi_sunxi_xfer_setup\n");
	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_wrn("[spi-%d]: just support 8bits per word... \n", spi->master->bus_num);
	    return -EINVAL;
	}
	if(spi->chip_select >= spi->master->num_chipselect) {
	    spi_wrn("[spi-%d]: spi device's chip select = %d exceeds the master supported cs_num[%d] \n",
	                    spi->master->bus_num, spi->chip_select, spi->master->num_chipselect);
	    return -EINVAL;
	}
	/* check again board info */
	if( AW_SPI_OK != spi_sunxi_check_cs(spi->chip_select, aw_spi) ) {
	    spi_wrn("spi_sunxi_check_cs failed! spi_device cs =%d ...\n", spi->master->num_chipselect);
	    return -EINVAL;
	}
	/* set cs */
	aw_spi_set_cs(spi->chip_select, base_addr);
    /*
     *  master: set spi module clock;
     *  set the default frequency	10MHz
     */
    aw_spi_set_master(base_addr);
   	if(config->max_speed_hz > SPI_MAX_FREQUENCY) {
	    return -EINVAL;
	}
    aw_spi_set_clk(config->max_speed_hz, clk_get_rate(aw_spi->mclk), base_addr);
    /*
     *  master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0.
     *  set bit width-default: 8 bits
     */
    aw_spi_config(1, spi->mode, base_addr);
	/*
	 * setup inter-byte delay; used for slow slaves
	 */
	aw_spi_set_waitclk_cnt((t && t->interbyte_usecs) ? t->interbyte_usecs*config->max_speed_hz/1000000 : 0, base_addr);
	return 0;
}


/*
 * > 64 : cpu ;  =< 64 : dma
 * wait for done completion in this function, wakup in the irq hanlder
 */
static int spi_sunxi_xfer(struct spi_device *spi, struct spi_transfer *t)
{
	struct sunxi_spi *aw_spi = spi_master_get_devdata(spi->master);
	void __iomem* base_addr = aw_spi->base_addr;
	unsigned long flags = 0;
	unsigned tx_len = 0;	/* number of bytes receieved */
	unsigned rx_len = 0;	/* number of bytes sent */
	unsigned char *rx_buf = (unsigned char *)t->rx_buf;
	unsigned char *tx_buf = (unsigned char *)t->tx_buf;
	int ret = 0;

    //spi_msg("Begin transfer, txbuf %p, rxbuf %p, len %d\n", t->tx_buf, t->rx_buf, t->len);
	if (!t->tx_buf && !t->rx_buf && t->len)
		return -EINVAL;
	if (t->tx_buf)
		tx_len = t->len;
	if (t->rx_buf)
		rx_len = t->len;
    //hex_dump("spi_regs:", base_addr, 0x60, 2);

    /* write 1 to clear 0 */
    aw_spi_clr_irq_pending(SPI_STAT_MASK, base_addr);
    /* disable all DRQ */
    aw_spi_disable_dma_irq(SPI_DRQEN_MASK, base_addr);
    aw_spi_sel_dma_type(0, base_addr);
    /* reset tx/rx fifo */
    aw_spi_reset_fifo(base_addr);

    aw_spi_set_bc_wtc(t->len, tx_len, base_addr);

    /*
     * 1. Tx/Rx error irq,process in IRQ;
     * 2. Transfer Complete Interrupt Enable
     */
    aw_spi_enable_irq(SPI_INTEN_TC|SPI_INTEN_ERR, base_addr);

	if (t->len > BULK_DATA_BOUNDARY) { /* dma */
        #ifdef CONFIG_SPI_SUNXI_NDMA
		if (tx_len && rx_len) /* dma full duplex not possible in normal dma mode */
		{
		    aw_spi_disable_irq(SPI_INTEN_TC|SPI_INTEN_ERR, base_addr);
		    spi_wrn("Full duplex not supported for normal dma!\n");
			return EINVAL;
		}
        aw_spi_sel_dma_type(0, base_addr);
        #else
        aw_spi_sel_dma_type(1, base_addr);
        #endif

		if(tx_len) {
			/* txFIFO 1/4 empty dma request enable,when 16 or less than 16 bytes. */
            aw_spi_enable_dma_irq(SPI_DRQEN_THE, base_addr);
		}

		if(rx_len) {
    		/* rxFIFO 1/4 full dma request enable,when 16 or more than 16 bytes */
            aw_spi_enable_dma_irq(SPI_DRQEN_RHF, base_addr);
		}

		if (rx_len) {
			ret = spi_sunxi_prepare_dma(aw_spi, SW_DMA_RDEV);
    		if(ret < 0) {
    			aw_spi_disable_irq(SPI_INTEN_TC|SPI_INTEN_ERR, base_addr);
				aw_spi_disable_dma_irq(SPI_DRQEN_RHF, base_addr);
				if (tx_len)
					aw_spi_disable_dma_irq(SPI_DRQEN_THE, base_addr);
    			return -EINVAL;
    		}
			spi_sunxi_config_dma(aw_spi, SW_DMA_RDEV, (void *)rx_buf, rx_len);
	        #ifdef CONFIG_SPI_SUNXI_NDMA
			spi_sunxi_start_dma(aw_spi, aw_spi->dma_id);
			#else
			spi_sunxi_start_dma(aw_spi, aw_spi->dma_rx_id);
			#endif
		}

		if (tx_len) {
			ret = spi_sunxi_prepare_dma(aw_spi, SW_DMA_WDEV);
			if(ret < 0) {
			    aw_spi_disable_irq(SPI_INTEN_TC|SPI_INTEN_ERR, base_addr);
			    aw_spi_disable_dma_irq(SPI_DRQEN_THE, base_addr);
				if (rx_len)
					aw_spi_disable_dma_irq(SPI_DRQEN_RHF, base_addr);
				return -EINVAL;
			}
			spi_sunxi_config_dma(aw_spi, SW_DMA_WDEV, (void *)tx_buf, tx_len);
	        #ifdef CONFIG_SPI_SUNXI_NDMA
			spi_sunxi_start_dma(aw_spi, aw_spi->dma_id);
			#else
			spi_sunxi_start_dma(aw_spi, aw_spi->dma_tx_id);
			#endif
		}

	    aw_spi_start_xfer(base_addr);
	} else { /* no dma */
		unsigned int poll_time = 0xfffff;
		//spi_msg(" tx -> by ahb\n");
		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);
	    aw_spi_start_xfer(base_addr);
		if (rx_len) {
			while(rx_len && (--poll_time >0)) {
				/* rxFIFO counter */
			    if(aw_spi_query_rxfifo(base_addr)){
			        *rx_buf++ =  readb(base_addr + SPI_RXDATA_REG);//fetch data
			        --rx_len;
			        poll_time = 0xffff;
			    }
			}
		} else {
            while(aw_spi_query_txfifo(base_addr)&&(--poll_time > 0) );/* txFIFO counter */
		}
		if(poll_time <= 0) {
		    spi_wrn("cpu tx data time out!\n");
		}
	}

	/* wait for xfer complete in the isr. */
	wait_for_completion(&aw_spi->done);
    /* get the isr return code */
    if(aw_spi->result != 0) {
        spi_wrn("[spi-%d]: xfer failed... \n", aw_spi->master->bus_num);
        ret = -1;
    }
    /* release dma resource */
	if (t->len > BULK_DATA_BOUNDARY)
		spi_sunxi_release_dma(aw_spi);
	return ret;
}

/* spi core xfer process */
static void spi_sunxi_work(struct work_struct *work)
{
	struct sunxi_spi *aw_spi = container_of(work, struct sunxi_spi, work);
	spin_lock_irq(&aw_spi->lock);
	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;
		unsigned int cs_change = 0;
		int status;
		/* get message from message queue in sunxi_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_irq(&aw_spi->lock);
		/* 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 to force xfer_setup on first transfer. */
		status = -1;
		/* 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 || t->speed_hz || t->interbyte_usecs) { /* xfer_setup if first transfer or overrides provided. */
				status = spi_sunxi_xfer_setup(spi, t);/* set the value every spi transfer */
//				spi_msg(" xfer setup \n");
				if (status < 0)
					break;/* fail, quit */
			}
			/* first active the cs */
			if (cs_change) {
				aw_spi->cs_control(spi, 1);
			}
			/* update the new cs value */
			cs_change = t->cs_change;
			/*
             * do transfer
			 * > 64 : cpu ;  =< 64 : dma
			 * wait for done completion in this function, wakup in the irq hanlder
			 */
			status = spi_sunxi_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 */
			if (t->delay_usecs)
				udelay(t->delay_usecs);
			/* if zero ,keep active,otherwise deactived. */
			if (cs_change) {
				aw_spi->cs_control(spi, 0);
			}
		}
		/*
		 * 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);
		}
		spin_lock_irq(&aw_spi->lock);
	}
	/* set spi to free */
	aw_spi->busy = SPI_FREE;
	spin_unlock_irq(&aw_spi->lock);
	return;
}

/* wake up the sleep thread, and give the result code */
static irqreturn_t spi_sunxi_isr(int irq, void *dev_id)
{
	struct sunxi_spi *aw_spi = (struct sunxi_spi *)dev_id;
	void *base_addr = aw_spi->base_addr;
    unsigned int status = aw_spi_qry_irq_pending(base_addr);
    aw_spi_clr_irq_pending(status, base_addr);//write 1 to clear 0.
    //spi_msg("status = %x \n", status);

    aw_spi->result = 0; /* assume succeed */
    /* master mode, Transfer Complete Interrupt */
    if( status & SPI_STAT_TC ) {
        aw_spi_disable_irq(SPI_STAT_TC|SPI_STAT_ERR, base_addr);
        //spi_msg("SPI TC comes\n");
        /*wakup uplayer, by the sem */
        complete(&aw_spi->done);
        return IRQ_HANDLED;
    }/* master mode:err */
    else if( status & SPI_STAT_ERR ) {
        /* error process, release dma in the workqueue,should not be here */
        aw_spi_disable_irq(SPI_STAT_TC|SPI_STAT_ERR, base_addr);
        aw_spi_restore_state(1, base_addr);
        aw_spi->result = -1;
        complete(&aw_spi->done);
        //spi_msg("SPI ERR comes\n");
        spi_wrn("master mode error: txFIFO overflow/rxFIFO underrun or overflow\n");
        return IRQ_HANDLED;
    }
    //spi_msg("SPI NONE comes\n");
	return IRQ_NONE;
}

/* interface 1 */
static int spi_sunxi_transfer(struct spi_device *spi, struct spi_message *msg)
{
	struct sunxi_spi *aw_spi = spi_master_get_devdata(spi->master);
	unsigned long flags;
	msg->actual_length = 0;
	msg->status = -EINPROGRESS;

	spin_lock_irqsave(&aw_spi->lock, flags);
    /* add msg to the sunxi_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 spi_sunxi_setup(struct spi_device *spi)
{
	struct sunxi_spi *aw_spi = spi_master_get_devdata(spi->master);
	struct sunxi_spi_config *config = spi->controller_data;/* general is null. */
	unsigned long flags;

    /* 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(AW_SPI_FAIL == spi_sunxi_check_cs(spi->chip_select, aw_spi)) {
        spi_wrn("[spi-%d]: not support cs-%d \n", aw_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)
			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 */
	    aw_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 spi_sunxi_cleanup(struct spi_device *spi)
{
    if(spi->controller_data) {
        kfree(spi->controller_data);
        spi->controller_data = NULL;
    }
}

static int spi_sunxi_set_gpio(struct sunxi_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_wrn("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_wrn("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_wrn("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 spi_sunxi_set_mclk(struct sunxi_spi *aw_spi, u32 mod_clk)
{
    struct clk *source_clock = NULL;
    char* name = NULL;
    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_wrn("Unable to get spi source clock resource\n");
		return -1;
	}

    if (clk_set_parent(aw_spi->mclk, source_clock))
    {
        spi_wrn("clk_set_parent failed\n");
        ret = -1;
        goto out;
    }

    if (clk_set_rate(aw_spi->mclk, mod_clk))
    {
        spi_wrn("clk_set_rate failed\n");
        ret = -1;
        goto out;
    }

    spi_msg("source = %s, src_clk = %u, mclk %u\n", name, (unsigned)clk_get_rate(source_clock), (unsigned)clk_get_rate(aw_spi->mclk));

	if (clk_enable(aw_spi->mclk)) {
		spi_wrn("Couldn't enable module clock 'spi'\n");
		ret = -EBUSY;
		goto out;
	}
	ret = 0;
out:
    clk_put(source_clock);
    return ret;
}

static int spi_sunxi_hw_init(struct sunxi_spi *aw_spi)
{
	void *base_addr = aw_spi->base_addr;
	unsigned long sclk_freq = 0;
#if defined CONFIG_ARCH_SUN4I
	char* mclk_name[] = {"spi0","spi1","spi2","spi3"};
#elif defined CONFIG_ARCH_SUN5I
    char* mclk_name[] = {"spi0","spi1","spi2"};
#endif
    aw_spi->mclk = clk_get(&aw_spi->pdev->dev, mclk_name[aw_spi->pdev->id]);
	if (IS_ERR(aw_spi->mclk)) {
		spi_wrn("Unable to acquire module clock 'spi'\n");
		return -1;
	}
    if (spi_sunxi_set_mclk(aw_spi, 100000000))
    {
        spi_wrn("spi_sunxi_set_mclk 'spi'\n");
		clk_put(aw_spi->mclk);
		return -1;
    }
    //hex_dump("ccmu regs:", (void __iomem*)SW_VA_CCM_IO_BASE, 0x200, 2);

	/* 1. enable the spi module */
	aw_spi_enable_bus(base_addr);
	/* 2. set the default chip select */
	if(AW_SPI_OK == spi_sunxi_check_cs(0, aw_spi)) {
	    aw_spi_set_cs(0, base_addr);
	}
	else{
        aw_spi_set_cs(1, base_addr);
	}
	/* 3. master */
    aw_spi_set_master(base_addr);
    sclk_freq  = clk_get_rate(aw_spi->mclk);
	/* 4. manual control the chip select */
	aw_spi_ss_ctrl(base_addr, 1);
	return 0;
}

static int spi_sunxi_hw_exit(struct sunxi_spi *aw_spi)
{
	/* disable the spi controller */
    aw_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 spi_sunxi_probe(struct platform_device *pdev)
{
	#ifdef CONFIG_SPI_SUNXI_NDMA
	struct resource	*dma_res;
	#else
	struct resource	*dma_tx_res, *dma_rx_res;
	#endif
	struct resource	*mem_res;
	struct sunxi_spi *aw_spi;
	struct sunxi_spi_platform_data *pdata;
	struct spi_master *master;
	int ret = 0, err = 0, irq;
	int cs_bitmap = 0;

	if (pdev->id < 0) {
		spi_wrn("Invalid platform device id-%d\n", pdev->id);
		return -ENODEV;
	}

	if (pdev->dev.platform_data == NULL) {
		spi_wrn("platform_data missing!\n");
		return -ENODEV;
	}

	pdata = pdev->dev.platform_data;
	if (!pdata->clk_name) {
		spi_wrn("platform data must initial! \n");
		return -EINVAL;
	}

	/* Check for availability of necessary resource */
    #ifdef CONFIG_SPI_SUNXI_NDMA
    dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
    if (dma_res == NULL) {
        spi_wrn("Unable to get spi DMA resource\n");
        return -ENXIO;
    }
    #else
    dma_tx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
    if (dma_tx_res == NULL) {
        spi_wrn("Unable to get spi DMA TX resource\n");
        return -ENXIO;
    }
    dma_rx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
    if (dma_rx_res == NULL) {
        spi_wrn("Unable to get spi DMA RX resource\n");
        return -ENXIO;
    }
    #endif

	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (mem_res == NULL) {
        	spi_wrn("Unable to get spi MEM resource\n");
        	return -ENXIO;
        }

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		spi_wrn("No spi IRQ specified\n");
		return -ENXIO;
	}

    /* create spi master */
	master = spi_alloc_master(&pdev->dev, sizeof(struct sunxi_spi));
	if (master == NULL) {
		spi_wrn("Unable to allocate SPI Master\n");
		return -ENOMEM;
	}

	platform_set_drvdata(pdev, master);
	aw_spi = spi_master_get_devdata(master);
    memset(aw_spi, 0, sizeof(struct sunxi_spi));

	aw_spi->master     = master;
	aw_spi->irq        = irq;
    #ifdef CONFIG_SPI_SUNXI_NDMA
	aw_spi->dma_id     = dma_res->start;
    aw_spi->dma_hdle = -1;
	#else
	aw_spi->dma_tx_id     = dma_tx_res->start;
	aw_spi->dma_rx_id     = dma_rx_res->start;
    aw_spi->dma_tx_hdle = -1;
    aw_spi->dma_rx_hdle = -1;
	#endif
	aw_spi->cs_control = spi_sunxi_cs_control;
	aw_spi->cs_bitmap  = pdata->cs_bitmap; /* cs0-0x1; cs1-0x2; cs0&cs1-0x3. */
	aw_spi->busy       = SPI_FREE;

	master->bus_num         = pdev->id;
	master->setup           = spi_sunxi_setup;
	master->cleanup         = spi_sunxi_cleanup;
	master->transfer        = spi_sunxi_transfer;
	master->num_chipselect  = pdata->num_cs;
    //master->dma_alignment   = 8; //  should be set to 32  ??
    //master->flags           = SPI_MASTER_HALF_DUPLEX; // temporay not support duplex

	/* the spi->mode bits understood by this driver: */
	master->mode_bits       = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH| SPI_LSB_FIRST | SPI_LOOP;
    /* update the cs bitmap */
    cs_bitmap = spi_sunxi_get_cfg_csbitmap(pdev->id);
	if(cs_bitmap & 0x3){
	    aw_spi->cs_bitmap  = cs_bitmap&0x3;
    //spi_msg("cs_bitmap = 0x%x \n", cs_bitmap);
	}

	err = request_irq(aw_spi->irq, spi_sunxi_isr, IRQF_DISABLED, pdev->name, aw_spi);
	if (err) {
		spi_wrn("Cannot claim IRQ\n");
		goto err0;
	}

	if (request_mem_region(mem_res->start,
			resource_size(mem_res), pdev->name) == NULL) {
		spi_wrn("Req mem region failed\n");
		ret = -ENXIO;
		goto err1;
	}

	aw_spi->base_addr = ioremap(mem_res->start, resource_size(mem_res));
	if (aw_spi->base_addr == NULL) {
		spi_wrn("Unable to remap IO\n");
		ret = -ENXIO;
		goto err2;
	}

	/* Setup clocks */
	aw_spi->hclk = clk_get(&pdev->dev, pdata->clk_name);
	if (IS_ERR(aw_spi->hclk)) {
		spi_wrn("Unable to acquire clock 'spi'\n");
		ret = PTR_ERR(aw_spi->hclk);
		goto err3;
	}

	if (clk_enable(aw_spi->hclk)) {
		spi_wrn("Couldn't enable clock 'spi'\n");
		ret = -EBUSY;
		goto err4;
	}

	aw_spi->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent));
	if (aw_spi->workqueue == NULL) {
		spi_wrn("Unable to create workqueue\n");
		ret = -ENOMEM;
		goto err5;
	}

    aw_spi->pdev = pdev;

	/* Setup Deufult Mode */
	spi_sunxi_hw_init(aw_spi);
#ifndef SYS_SPI_PIN
	/* set gpio */
	gpio_addr = ioremap(_PIO_BASE_ADDRESS, 0x1000);
#endif
	spi_sunxi_set_gpio(aw_spi, 1);

	spin_lock_init(&aw_spi->lock);
	init_completion(&aw_spi->done);
	INIT_WORK(&aw_spi->work, spi_sunxi_work);/* banding the process handler */
	INIT_LIST_HEAD(&aw_spi->queue);

	if (spi_register_master(master)) {
		spi_wrn("cannot register SPI master\n");
		ret = -EBUSY;
		goto err6;
	}

	spi_msg("allwinners SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", pdev->id, master->num_chipselect);
	//spi_msg("\tIOmem=[0x%x-0x%x]\tDMA=[%d]\n", mem_res->end, mem_res->start, aw_spi->dma_id);
	#ifdef CONFIG_SPI_SUNXI_NDMA
	spi_msg("[spi-%d]: driver probe succeed, base %p, irq %d, dma_id %d!\n", master->bus_num, aw_spi->base_addr, aw_spi->irq, aw_spi->dma_id);
	#else
	spi_msg("[spi-%d]: driver probe succeed, base %p, irq %d, dma_tx_id %d, dma_rx_id %d!\n", master->bus_num, aw_spi->base_addr, aw_spi->irq, aw_spi->dma_tx_id, aw_spi->dma_rx_id);
	#endif
	return 0;
err6:
	destroy_workqueue(aw_spi->workqueue);
err5:
	clk_disable(aw_spi->hclk);
err4:
	clk_put(aw_spi->hclk);
err3:
	iounmap((void *)aw_spi->base_addr);
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 spi_sunxi_remove(struct platform_device *pdev)
{
	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
	struct sunxi_spi *aw_spi = spi_master_get_devdata(master);
	struct resource	*mem_res;
	unsigned long flags;

	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)
		msleep(10);

	spi_sunxi_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));

	platform_set_drvdata(pdev, NULL);
	spi_master_put(master);
	return 0;
}

#ifdef CONFIG_PM
static int spi_sunxi_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
	struct sunxi_spi *aw_spi = spi_master_get_devdata(master);
	unsigned long flags;
	printk("[spi-%d]: suspend okay.. \n", master->bus_num);

	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)
		msleep(10);

	/* Disable the clock */
	clk_disable(aw_spi->hclk);
	return 0;
}

static int spi_sunxi_resume(struct platform_device *pdev)
{
	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
	struct sunxi_spi  *aw_spi = spi_master_get_devdata(master);
	unsigned long flags;
    printk("[spi-%d]: resume okay.. \n", master->bus_num);

	/* Enable the clock */
	clk_enable(aw_spi->hclk);
	spi_sunxi_hw_init(aw_spi);

	spin_lock_irqsave(&aw_spi->lock, flags);
	aw_spi->busy = SPI_FREE;
	spin_unlock_irqrestore(&aw_spi->lock, flags);
	return 0;
}
#else
#define spi_sunxi_suspend	NULL
#define spi_sunxi_resume	NULL
#endif /* CONFIG_PM */

static struct platform_driver __refdata spi_sunxi_driver = {
	.driver = {
#if defined CONFIG_ARCH_SUN4I
		.name	= "sun4i-spi",
#elif defined CONFIG_ARCH_SUN5I
        .name	= "sun5i-spi",
#endif
		.owner = THIS_MODULE,
	},
	.probe   = spi_sunxi_probe,
	.remove  = spi_sunxi_remove,
	.suspend = spi_sunxi_suspend,
	.resume  = spi_sunxi_resume,
};

/* ---------------- spi resouce and platform data start ---------------------- */
struct sunxi_spi_platform_data sunxi_spi0_pdata = {
#if defined CONFIG_ARCH_SUN4I
	.cs_bitmap	= 0x3,
	.num_cs		= 2,
#elif defined CONFIG_ARCH_SUN5I
	.cs_bitmap  = 0x1,
	.num_cs		= 1,
#endif
	.clk_name = "ahb_spi0",
};
static struct resource sunxi_spi0_resources[] = {
	[0] = {
		.start	= SPI0_BASE_ADDR,
		.end	= SPI0_BASE_ADDR + 1024,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= SW_INT_IRQNO_SPI00,
		.end	= SW_INT_IRQNO_SPI00,
		.flags	= IORESOURCE_IRQ,
	},
	#ifdef CONFIG_SPI_SUNXI_NDMA
	[2] = {
		.start	= DMACH_NSPI0,
		.end	= DMACH_NSPI0,
		.flags	= IORESOURCE_DMA,
	},
	#else
	[2] = {
		.start	= DMACH_DSPI0_TX,
		.end	= DMACH_DSPI0_TX,
		.flags	= IORESOURCE_DMA,
	},
	[3] = {
		.start	= DMACH_DSPI0_RX,
		.end	= DMACH_DSPI0_RX,
		.flags	= IORESOURCE_DMA,
	},
	#endif
};
static struct platform_device sunxi_spi0_device = {
#if defined CONFIG_ARCH_SUN4I
	.name		= "sun4i-spi",
#elif defined CONFIG_ARCH_SUN5I
    .name		= "sun5i-spi",
#endif
	.id			= 0,
	.num_resources	= ARRAY_SIZE(sunxi_spi0_resources),
	.resource	= sunxi_spi0_resources,
	.dev		= {
		.platform_data = &sunxi_spi0_pdata,
	},
};

struct sunxi_spi_platform_data sunxi_spi1_pdata = {
#if defined CONFIG_ARCH_SUN4I
	.cs_bitmap	= 0x3,
	.num_cs		= 2,
#elif defined CONFIG_ARCH_SUN5I
	.cs_bitmap	= 0x1,
	.num_cs		= 1,
#endif
	.clk_name = "ahb_spi1",
};
static struct resource sunxi_spi1_resources[] = {
	[0] = {
		.start	= SPI1_BASE_ADDR,
		.end	= SPI1_BASE_ADDR + 1024,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= SW_INT_IRQNO_SPI01,
		.end	= SW_INT_IRQNO_SPI01,
		.flags	= IORESOURCE_IRQ,
	},
	#ifdef CONFIG_SPI_SUNXI_NDMA
	[2] = {
		.start	= DMACH_NSPI1,
		.end	= DMACH_NSPI1,
		.flags	= IORESOURCE_DMA,
	},
	#else
	[2] = {
		.start	= DMACH_DSPI1_TX,
		.end	= DMACH_DSPI1_TX,
		.flags	= IORESOURCE_DMA,
	},
	[3] = {
		.start	= DMACH_DSPI1_RX,
		.end	= DMACH_DSPI1_RX,
		.flags	= IORESOURCE_DMA,
	},
	#endif
};
static struct platform_device sunxi_spi1_device = {
#if defined CONFIG_ARCH_SUN4I
	.name		= "sun4i-spi",
#elif defined CONFIG_ARCH_SUN5I
    .name		= "sun5i-spi",
#endif
	.id			= 1,
	.num_resources	= ARRAY_SIZE(sunxi_spi1_resources),
	.resource	= sunxi_spi1_resources,
	.dev		= {
		.platform_data = &sunxi_spi1_pdata,
	},
};

struct sunxi_spi_platform_data sunxi_spi2_pdata = {
	.cs_bitmap	= 0x3,
	.num_cs		= 2,
	.clk_name	= "ahb_spi2",
};
static struct resource sunxi_spi2_resources[] = {
	[0] = {
		.start	= SPI2_BASE_ADDR,
		.end	= SPI2_BASE_ADDR + 1024,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= SW_INT_IRQNO_SPI02,
		.end	= SW_INT_IRQNO_SPI02,
		.flags	= IORESOURCE_IRQ,
	},
	#ifdef CONFIG_SPI_SUNXI_NDMA
	[2] = {
		.start	= DMACH_NSPI2,
		.end	= DMACH_NSPI2,
		.flags	= IORESOURCE_DMA,
	},
	#else
	[2] = {
		.start	= DMACH_DSPI2_TX,
		.end	= DMACH_DSPI2_TX,
		.flags	= IORESOURCE_DMA,
	},
	[3] = {
		.start	= DMACH_DSPI2_RX,
		.end	= DMACH_DSPI2_RX,
		.flags	= IORESOURCE_DMA,
	},
	#endif
};
static struct platform_device sunxi_spi2_device = {
#if defined CONFIG_ARCH_SUN4I
	.name		= "sun4i-spi",
#elif defined CONFIG_ARCH_SUN5I
    .name		= "sun5i-spi",
#endif
	.id			= 2,
	.num_resources	= ARRAY_SIZE(sunxi_spi2_resources),
	.resource	= sunxi_spi2_resources,
	.dev		= {
		.platform_data = &sunxi_spi2_pdata,
	},
};

#ifdef CONFIG_ARCH_SUN4I
struct sunxi_spi_platform_data sunxi_spi3_pdata = {
	.cs_bitmap	= 0x3,
	.num_cs		= 2,
	.clk_name	= "ahb_spi3",
};
static struct resource sunxi_spi3_resources[] = {
	[0] = {
		.start	= SPI3_BASE_ADDR,
		.end	= SPI3_BASE_ADDR + 1024,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= SW_INT_IRQNO_SPI3,
		.end	= SW_INT_IRQNO_SPI3,
		.flags	= IORESOURCE_IRQ,
	},
	#ifdef CONFIG_SPI_SUNXI_NDMA
	[2] = {
		.start	= DMACH_NSPI3,
		.end	= DMACH_NSPI3,
		.flags	= IORESOURCE_DMA,
	},
	#else
	[2] = {
		.start	= DMACH_DSPI3_TX,
		.end	= DMACH_DSPI3_TX,
		.flags	= IORESOURCE_DMA,
	},
	[3] = {
		.start	= DMACH_DSPI3_RX,
		.end	= DMACH_DSPI3_RX,
		.flags	= IORESOURCE_DMA,
	},
	#endif
};
static struct platform_device sunxi_spi3_device = {
	.name		= "sun4i-spi",
	.id			= 3,
	.num_resources	= ARRAY_SIZE(sunxi_spi3_resources),
	.resource	= sunxi_spi3_resources,
	.dev		= {
		.platform_data = &sunxi_spi3_pdata,
	},
};
#endif

/* ---------------- spi resource and platform data end ----------------------- */
static struct spi_board_info *spi_boards = NULL;
int __devinit spi_sunxi_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_msg("Get spi devices number failed\n");
        return -1;
    }
    spi_msg("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_msg("Alloc spi board information failed \n");
        return -1;
    }

    spi_msg("%-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_msg("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_msg("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_msg("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_msg("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_msg("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_msg("%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_msg("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_msg("Get spi devices manual_cs failed\n");
            goto fail;
        }
        */
        spi_msg("%-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_msg("Register board information failed\n");
        goto fail;
    }
    return 0;
fail:
    if (spi_boards)
    {
        kfree(spi_boards);
        spi_boards = NULL;
    }
    return -1;
}

static int spi_sunxi_get_cfg_csbitmap(int bus_num)
{
    int value = 0;
    int ret   = 0;
#if defined CONFIG_ARCH_SUN4I
    char *main_name[] = {"spi0_para", "spi1_para", "spi2_para", "spi3_para"};
#elif defined CONFIG_ARCH_SUN5I
    char *main_name[] = {"spi0_para", "spi1_para", "spi2_para"};
#endif
    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_wrn("get spi %d para failed, err code = %d \n", bus_num, ret);
        return 0;
    }
    spi_msg("bus num = %d, spi used = %d \n", bus_num, value);
    return value;
}

/* get configuration in the script */
#define SPI0_USED_MASK 0x1
#define SPI1_USED_MASK 0x2
#define SPI2_USED_MASK 0x4
#ifdef CONFIG_ARCH_SUN4I
    #define SPI3_USED_MASK 0x8
#endif
static int spi_used = 0;

static int __init spi_sunxi_init(void)
{
    int used = 0;
    int i = 0;
    int ret = 0;
    char spi_para[16] = {0};

    spi_msg("sw spi init !!\n");
    spi_used = 0;
#if defined CONFIG_ARCH_SUN4I
    for (i=0; i<4; i++)
#elif defined  CONFIG_ARCH_SUN5I
    for (i=0; i<3; i++)
#endif
    {
        used = 0;
        sprintf(spi_para, "spi%d_para", i);
        ret = script_parser_fetch(spi_para, "spi_used", &used, sizeof(int));
        if (ret)
        {
            spi_msg("sw spi init fetch spi%d uning configuration failed\n", i);
            continue;
        }
        if (used)
            spi_used |= 1 << i;
    }

    ret = spi_sunxi_register_spidev();
    if (ret)
    {
        spi_msg("register spi devices board info failed \n");
    }

    if (spi_used & SPI0_USED_MASK)
        platform_device_register(&sunxi_spi0_device);
    if (spi_used & SPI1_USED_MASK)
        platform_device_register(&sunxi_spi1_device);
    if (spi_used & SPI2_USED_MASK)
        platform_device_register(&sunxi_spi2_device);
#ifdef CONFIG_ARCH_SUN4I
    if (spi_used & SPI3_USED_MASK)
        platform_device_register(&sunxi_spi3_device);
#endif

    if (spi_used)
    {
        return platform_driver_register(&spi_sunxi_driver);
    }
    else
    {
        pr_warning("spi: cannot find any using configuration for \
                    all 4 spi controllers, return directly!\n");
        return 0;
    }
}
module_init(spi_sunxi_init);

static void __exit spi_sunxi_exit(void)
{
    if (spi_used)
	    platform_driver_unregister(&spi_sunxi_driver);
}
module_exit(spi_sunxi_exit);

MODULE_AUTHOR("Victor.Wei @allwinner");
MODULE_DESCRIPTION("SUNXI SPI BUS Driver");
MODULE_ALIAS("platform:sunxi-spi");
MODULE_LICENSE("GPL");

Reply via email to