build/patch/kernel/sun7i-default/0029-dev-spi-sun7i.patch

2261 lines
71 KiB
Diff

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