| /* | 
 |  * zx297520v2v3 spi controller driver | 
 |  * Author: zhou.tianbao@sanechips.com.cn | 
 |  * from original zx297520v2v3 driver | 
 |  * | 
 |  * Copyright (C) 2016 Sanechips Corporation | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License as published by | 
 |  * the Free Software Foundation; either version 2 of the License, or | 
 |  * (at your option) any later version. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
 |  * | 
 |  */ | 
 | #include <linux/kernel.h> | 
 | #include <linux/init.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/module.h> | 
 | #include <linux/device.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/err.h> | 
 | #include <linux/clk.h> | 
 | #include <linux/io.h> | 
 | #include <linux/gpio.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/dmaengine.h> | 
 | #include <linux/dma-mapping.h> | 
 | #include <linux/scatterlist.h> | 
 | #include <linux/pm_runtime.h> | 
 | #include <linux/semaphore.h> | 
 | #include <linux/wakelock.h> | 
 |  | 
 | #include <linux/debugfs.h> | 
 |  | 
 | #include <linux/spi/spi.h> | 
 | #include <linux/soc/zte/pm/drv_idle.h> | 
 |  | 
 | #include <mach/clk.h> | 
 | #include <mach/spi.h> | 
 | #include <mach/gpio.h> | 
 | #include <mach/dma.h> | 
 | #include <mach/iomap.h> | 
 |  | 
 | #include <linux/ramdump/ramdump.h> | 
 |  | 
 | #define CONFIG_SPI_DMA_ENGINE | 
 | #define SPI_PSM_CONTROL         (1) | 
 |  | 
 | /* | 
 |  * This macro is used to define some register default values. | 
 |  * reg is masked with mask, the OR:ed with an (again masked) | 
 |  * val shifted sb steps to the left. | 
 |  */ | 
 | #define SPI_WRITE_BITS(reg, val, mask, sb) \ | 
 |  ((reg) = (((reg) & ~(mask)) | (((val)<<(sb)) & (mask)))) | 
 |  | 
 | /* | 
 |  * This macro is also used to define some default values. | 
 |  * It will just shift val by sb steps to the left and mask | 
 |  * the result with mask. | 
 |  */ | 
 | #define GEN_MASK_BITS(val, mask, sb) \ | 
 |  (((val)<<(sb)) & (mask)) | 
 |  | 
 |  | 
 | #define SPI_GPIO_HIGH 		1 | 
 | #define SPI_GPIO_LOW 		0 | 
 |  | 
 | #define	ZX29_CS_ACTIVE		1	/* normally nCS, active low */ | 
 | #define	ZX29_CS_INACTIVE	0 | 
 |  | 
 | #define DRIVE_TX				0 | 
 | #define DO_NOT_DRIVE_TX	1 | 
 |  | 
 | #define DO_NOT_QUEUE_DMA	0 | 
 | #define QUEUE_DMA			1 | 
 |  | 
 | #define RX_TRANSFER			BIT(0) | 
 | #define TX_TRANSFER			BIT(1) | 
 |  | 
 | /* registers */ | 
 | /* | 
 | #define SPI_VER_REG(r)		(r + 0x00) | 
 | #define SPI_COM_CTRL(r)		(r + 0x04) | 
 | #define SPI_FMT_CTRL(r)		(r + 0x08) | 
 | #define SPI_DR(r)			 	(r + 0x0C) | 
 | #define SPI_FIFO_CTRL(r)	 	(r + 0x10) | 
 | #define SPI_FIFO_SR(r)		(r + 0x14) | 
 | #define SPI_INTR_EN(r)		(r + 0x18) | 
 | #define SPI_INTR_SR_SCLR(r)	 (r + 0x1C) | 
 | #define SPI_TIMING_SCLR(r)	 (r + 0x20) | 
 | */ | 
 | #define SPI_VER_REG_OFFSET		(0x00) | 
 | #define SPI_COM_CTRL_OFFSET	(0x04) | 
 | #define SPI_FMT_CTRL_OFFSET	(0x08) | 
 | #define SPI_DR_OFFSET			(0x0C) | 
 | #define SPI_FIFO_CTRL_OFFSET	(0x10) | 
 | #define SPI_FIFO_SR_OFFSET		(0x14) | 
 | #define SPI_INTR_EN_OFFSET		(0x18) | 
 | #define SPI_INTR_SR_OFFSET		(0x1C) | 
 | #define SPI_TIMING_OFFSET		(0x20) | 
 |  | 
 | /* | 
 |  * SPI Version Register - SPI_VER_REG | 
 |  */ | 
 | #define SPI_VER_REG_MASK_Y		(0xFFUL << 16) | 
 | #define SPI_VER_REG_MASK_X		(0xFFUL << 24) | 
 |  | 
 | /* | 
 |  * SPI Common Control Register - SPI_COM_CTRL | 
 |  */ | 
 | #define SPI_COM_CTRL_MASK_LBM	    	(0x1UL << 0) | 
 | #define SPI_COM_CTRL_MASK_SSPE 			(0x1UL << 1) | 
 | #define SPI_COM_CTRL_MASK_MS	    	(0x1UL << 2) | 
 | #define SPI_COM_CTRL_MASK_SOD	    	(0x1UL << 3) | 
 | #define SPI_COM_CTRL_MASK_SSPE_BACK		(0x1UL << 4) | 
 |  | 
 | /* | 
 |  * SPI Format Control Register - SPI_FMT_CTRL | 
 |  */ | 
 | #define SPI_FMT_CTRL_MASK_FRF		(0x3UL << 0) | 
 | #define SPI_FMT_CTRL_MASK_POL		(0x1UL << 2) | 
 | #define SPI_FMT_CTRL_MASK_PHA		(0x1UL << 3) | 
 | #define SPI_FMT_CTRL_MASK_DSS		(0x1FUL << 4) | 
 |  | 
 | /* | 
 |  * SPI FIFO Control Register - SPI_FIFO_CTRL | 
 |  */ | 
 | #define SPI_FIFO_CTRL_MASK_RX_DMA_EN		(0x1UL << 2) | 
 | #define SPI_FIFO_CTRL_MASK_TX_DMA_EN		(0x1UL << 3) | 
 | #define SPI_FIFO_CTRL_MASK_RX_FIFO_THRES   	(0xFUL << 4) | 
 | #define SPI_FIFO_CTRL_MASK_TX_FIFO_THRES   	(0xFUL << 8) | 
 | /* | 
 |  * SPI FIFO Status Register - SPI_FIFO_SR | 
 |  */ | 
 |  | 
 | #define SPI_FIFO_SR_MASK_RX_BEYOND_THRES	(0x1UL << 0) | 
 | #define SPI_FIFO_SR_MASK_TX_BEYOND_THRES	(0x1UL << 1) | 
 | #define SPI_FIFO_SR_MASK_RX_FIFO_FULL	    (0x1UL << 2) | 
 | #define SPI_FIFO_SR_MASK_TX_FIFO_EMPTY	    (0x1UL << 3) | 
 | #define SPI_FIFO_SR_MASK_BUSY               (0x1UL << 4) | 
 | #define SPI_FIFO_SR_SHIFT_RX_CNT			5 | 
 | #define SPI0_FIFO_SR_MASK_RX_FIFO_CNTR	    (0x1fUL << SPI_FIFO_SR_SHIFT_RX_CNT) | 
 | #define SPI1_FIFO_SR_MASK_RX_FIFO_CNTR	    (0x7fUL << SPI_FIFO_SR_SHIFT_RX_CNT) | 
 | #define SPI0_FIFO_SR_SHIFT_TX_CNT			10 | 
 | #define SPI0_FIFO_SR_MASK_TX_FIFO_CNTR	    (0x1fUL << SPI0_FIFO_SR_SHIFT_TX_CNT) | 
 | #define SPI1_FIFO_SR_SHIFT_TX_CNT			12 | 
 | #define SPI1_FIFO_SR_MASK_TX_FIFO_CNTR	    (0x1fUL << SPI1_FIFO_SR_SHIFT_TX_CNT) | 
 |  | 
 | /* | 
 |  * SPI Interrupt Enable Register - SPI_INTR_EN | 
 |  */ | 
 | #define SPI_INTR_EN_MASK_RX_OVERRUN_IE    	(0x1UL << 0) | 
 | #define SPI_INTR_EN_MASK_TX_UNDERRUN_IE  	(0x1UL << 1) | 
 | #define SPI_INTR_EN_MASK_RX_FULL_IE     	(0x1UL << 2) | 
 | #define SPI_INTR_EN_MASK_TX_EMPTY_IE  		(0x1UL << 3) | 
 | #define SPI_INTR_EN_MASK_RX_THRES_IE  		(0x1UL << 4) | 
 | #define SPI_INTR_EN_MASK_TX_THRES_IE  		(0x1UL << 5) | 
 |  | 
 | /* | 
 |  * SPI Interrupt Status Register OR Interrupt Clear Register - SPI_INTR_SR_SCLR | 
 |  */ | 
 |  | 
 | #define SPI_INTR_SR_SCLR_MASK_RX_OVERRUN_INTR     		(0x1UL << 0) | 
 | #define SPI_INTR_SR_SCLR_MASK_TX_UNDERRUN_INTR  		(0x1UL << 1) | 
 | #define SPI_INTR_SR_SCLR_MASK_RX_FULL_INTR            	(0x1UL << 2) | 
 | #define SPI_INTR_SR_SCLR_MASK_TX_EMPTY_INTR         	(0x1UL << 3) | 
 | #define SPI_INTR_SR_SCLR_MASK_RX_THRES_INTR         	(0x1UL << 4) | 
 | #define SPI_INTR_SR_SCLR_MASK_TX_THRES_INTR       		(0x1UL << 5) | 
 |  | 
 | /* SPI WCLK Freqency */ | 
 | #define SPI_SPICLK_FREQ_26M		(26*1000*1000) | 
 | #define SPI_SPICLK_FREQ_104M	(104*1000*1000) | 
 | #define SPI_SPICLK_FREQ_156M	(156*1000*1000) | 
 |  | 
 | #define CLEAR_ALL_INTERRUPTS    0x3FUL | 
 | #define ENABLE_ALL_INTERRUPTS   0x3FUL | 
 | #define ENABLE_INTERRUPTS   	0x03UL | 
 | #define DISABLE_ALL_INTERRUPTS  0x0UL | 
 | /* | 
 |  * Message State | 
 |  * we use the spi_message.state (void *) pointer to | 
 |  * hold a single state value, that's why all this | 
 |  * (void *) casting is done here. | 
 |  */ | 
 |  | 
 | enum zx29_spi_state { | 
 | 	STATE_START, | 
 | 	STATE_RUNNING, | 
 | 	STATE_DONE, | 
 | 	STATE_ERROR | 
 | }; | 
 |  | 
 | /* | 
 |  * SPI State - Whether Enabled or Disabled | 
 |  */ | 
 | #define SPI_DISABLED		(0) | 
 | #define SPI_ENABLED			(1) | 
 |  | 
 | /* | 
 |  * SPI DMA State - Whether DMA Enabled or Disabled | 
 |  */ | 
 | #define SPI_DMA_DISABLED		(0) | 
 | #define SPI_DMA_ENABLED		(1) | 
 |  | 
 | /* | 
 |  * SPI SOD State - Whether SOD Enabled or Disabled | 
 |  */ | 
 | #define SPI_SOD_DISABLED		(1) | 
 | #define SPI_SOD_ENABLED		(0) | 
 |  | 
 | #if 0 | 
 | #define GPIO_AP_SPI0_CS   30 | 
 | #define GPIO_AP_SPI0_CLK  31 | 
 | #define GPIO_AP_SPI0_RXD  32 | 
 | #define GPIO_AP_SPI0_TXD  33 | 
 | #define GPIO_AP_SPI1_CS   7 | 
 | #define GPIO_AP_SPI1_CLK  8 | 
 | #define GPIO_AP_SPI1_RXD  13 | 
 | #define GPIO_AP_SPI1_TXD  14 | 
 | //#else | 
 | #define GPIO_AP_SPI0_CS   ZX29_GPIO_25 | 
 | #define GPIO_AP_SPI0_CLK  ZX29_GPIO_26 | 
 | #define GPIO_AP_SPI0_RXD  ZX29_GPIO_27 | 
 | #define GPIO_AP_SPI0_TXD  ZX29_GPIO_28 | 
 |  | 
 |  | 
 | #define GPIO_AP_SPI0_CS_FUN 	GPIO25_SSP0_CS | 
 | #define GPIO_AP_SPI0_CLK_FUN 	GPIO26_SSP0_CLK | 
 | #define GPIO_AP_SPI0_RXD_FUN 	GPIO27_SSP0_RXD | 
 | #define GPIO_AP_SPI0_TXD_FUN 	GPIO28_SSP0_TXD | 
 | #endif | 
 | enum spi_fifo_threshold_level { | 
 | 	SPI_FIFO_THRES_1, | 
 | 	SPI_FIFO_THRES_2, | 
 | 	SPI_FIFO_THRES_3, | 
 | 	SPI_FIFO_THRES_4, | 
 | 	SPI_FIFO_THRES_5, | 
 | 	SPI_FIFO_THRES_6, | 
 | 	SPI_FIFO_THRES_7, | 
 | 	SPI_FIFO_THRES_8, | 
 | 	SPI_FIFO_THRES_9, | 
 | 	SPI_FIFO_THRES_10, | 
 | 	SPI_FIFO_THRES_11, | 
 | 	SPI_FIFO_THRES_12, | 
 | 	SPI_FIFO_THRES_13, | 
 | 	SPI_FIFO_THRES_14, | 
 | 	SPI_FIFO_THRES_15, | 
 | 	SPI_FIFO_THRES_16 | 
 |  | 
 | }; | 
 |  | 
 | /* | 
 |  * SPI Clock Parameter ranges | 
 |  */ | 
 | #define DIV_MIN     0x00 | 
 | #define DIV_MAX     0x0F | 
 |  | 
 | #define SPI_POLLING_TIMEOUT 1000 | 
 |  | 
 | /* | 
 |  * The type of reading going on on this chip | 
 |  */ | 
 | enum spi_reading { | 
 | 	READING_NULL, | 
 | 	READING_U8, | 
 | 	READING_U16, | 
 | 	READING_U32 | 
 | }; | 
 |  | 
 | enum spi_writing { | 
 | 	WRITING_NULL, | 
 | 	WRITING_U8, | 
 | 	WRITING_U16, | 
 | 	WRITING_U32 | 
 | }; | 
 |  | 
 |  | 
 | struct vendor_data { | 
 | 	int fifodepth; | 
 | 	int max_bpw; | 
 | 	bool loopback; | 
 | }; | 
 |  | 
 |  | 
 | struct zx29_spi { | 
 | 	char name[16]; | 
 | 	struct platform_device		*pdev; | 
 | 	struct vendor_data		*vendor; | 
 | 	resource_size_t			phybase; | 
 | 	void __iomem		*virtbase; | 
 | 	unsigned int 	irq; | 
 | 	struct clk		*pclk;/* spi controller work clock */ | 
 | 	struct clk		*spi_clk;/* spi clk line clock */ | 
 | 	u32                 	clkfreq; | 
 | 	struct spi_master			*master; | 
 | 	struct zx29_spi_controller	*master_info; | 
 | 	/* Message per-transfer pump */ | 
 | 	struct tasklet_struct		pump_transfers; | 
 | 	struct spi_message		*cur_msg; | 
 | 	struct spi_transfer			*cur_transfer; | 
 | 	struct chip_data			*cur_chip; | 
 | 	bool				next_msg_cs_active; | 
 | 	void				*tx; | 
 | 	void				*tx_end; | 
 | 	void				*rx; | 
 | 	void				*rx_end; | 
 | 	enum spi_reading		read; | 
 | 	enum spi_writing		write; | 
 | 	u32					exp_fifo_level; | 
 | 	enum spi_rx_level_trig		rx_lev_trig; | 
 | 	enum spi_tx_level_trig		tx_lev_trig; | 
 | 	/* DMA settings */ | 
 | #ifdef CONFIG_SPI_DMA_ENGINE | 
 | 	struct dma_chan			*dma_rx_channel; | 
 | 	struct dma_chan			*dma_tx_channel; | 
 | 	struct sg_table			sgt_rx; | 
 | 	struct sg_table			sgt_tx; | 
 | 	char					*dummypage; | 
 | 	unsigned int			dma_running; | 
 | //	struct mutex		spi_lock; | 
 | #endif | 
 | #if defined(CONFIG_DEBUG_FS) | 
 | 	struct dentry *			spi_root; | 
 | 	struct debugfs_regset32 	spi_regset; | 
 | 	u32 spi_poll_cnt; | 
 | 	u32 spi_dma_cnt; | 
 | #endif | 
 | #if SPI_PSM_CONTROL | 
 |     struct wake_lock        psm_lock; | 
 | #endif | 
 | 	struct semaphore 	sema_dma; | 
 | }; | 
 |  | 
 |  | 
 | struct chip_data { | 
 | 	u32 ver_reg; | 
 | 	u32 com_ctrl; | 
 | 	u32 fmt_ctrl; | 
 | 	u32 fifo_ctrl; | 
 | //	u32 intr_en; | 
 | 	u8 n_bytes; | 
 | 	u8 clk_div;/* spi clk divider */ | 
 | 	bool enable_dma; | 
 | 	enum spi_reading read; | 
 | 	enum spi_writing write; | 
 | 	void (*cs_control) (u32 command); | 
 | 	int xfer_type; | 
 | }; | 
 | //struct semaphore g_SpiTransferSemaphore; | 
 |  | 
 | static struct zx29_spi *g_zx29_spi[2]; | 
 |  | 
 | #if SPI_PSM_CONTROL | 
 | static volatile unsigned int spi_active_count = 0; | 
 |  | 
 | static void zx29_spi_set_active(struct wake_lock *lock) | 
 | { | 
 | 	unsigned long flags; | 
 |  | 
 | 	local_irq_save(flags); | 
 |  | 
 |     if(spi_active_count == 0) | 
 |     { | 
 |         zx_cpuidle_set_busy(IDLE_FLAG_SPI); | 
 |     } | 
 |     spi_active_count++; | 
 |  | 
 | 	local_irq_restore(flags); | 
 |  | 
 |     wake_lock(lock); | 
 | } | 
 |  | 
 | static void zx29_spi_set_idle(struct wake_lock *lock) | 
 | { | 
 | 	unsigned long flags; | 
 |  | 
 | 	local_irq_save(flags); | 
 |  | 
 |     spi_active_count--; | 
 |     if(spi_active_count == 0) | 
 |     { | 
 |         zx_cpuidle_set_free(IDLE_FLAG_SPI); | 
 |     } | 
 |  | 
 | 	local_irq_restore(flags); | 
 |  | 
 |     wake_unlock(lock); | 
 | } | 
 | #endif | 
 |  | 
 | static int zx29_do_interrupt_dma_transfer(struct zx29_spi *zx29spi); | 
 | /** | 
 |  * default_cs_control - Dummy chip select function | 
 |  * @command: select/delect the chip | 
 |  * | 
 |  * If no chip select function is provided by client this is used as dummy | 
 |  * chip select | 
 |  */ | 
 | static void default_cs0_control(u32 command) | 
 | { | 
 | 	gpio_set_value(GPIO_AP_SPI0_CS, !command); | 
 | } | 
 |  | 
 | #ifdef CONFIG_ARCH_ZX297520V3 | 
 | static void default_cs1_control(u32 command) | 
 | { | 
 | 	gpio_set_value(GPIO_AP_SPI1_CS, !command); | 
 | } | 
 | #endif | 
 |  | 
 |  | 
 | static int flush(struct zx29_spi *zx29spi) | 
 | { | 
 | 	unsigned long limit = loops_per_jiffy << 1; | 
 | 	uint32_t rx_fifo_cnt_msk = (zx29spi->pdev->id == 1) ? | 
 | 								SPI1_FIFO_SR_MASK_RX_FIFO_CNTR : | 
 | 								SPI0_FIFO_SR_MASK_RX_FIFO_CNTR; | 
 |  | 
 | 	dev_dbg(&zx29spi->pdev->dev, "flush\n"); | 
 | 	/* Flushing FIFO by software cannot clear RX DMA Request. */ | 
 | 	do { | 
 | 		while (readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase)) & rx_fifo_cnt_msk) | 
 | 			readl((SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 	} while ((readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase)) & SPI_FIFO_SR_MASK_BUSY) && limit--); | 
 |  | 
 | 	zx29spi->exp_fifo_level = 0; | 
 |  | 
 | 	return limit; | 
 | } | 
 |  | 
 |  | 
 | static void restore_state(struct zx29_spi *zx29spi) | 
 | { | 
 | 	struct chip_data *chip = zx29spi->cur_chip; | 
 |  | 
 | 	/* disable all interrupts */ | 
 | 	writel(ENABLE_INTERRUPTS, (SPI_INTR_EN_OFFSET+zx29spi->virtbase)); | 
 | 	writel(CLEAR_ALL_INTERRUPTS, (SPI_INTR_SR_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | 	writel(chip->fmt_ctrl, (SPI_FMT_CTRL_OFFSET+zx29spi->virtbase)); | 
 | 	writel(chip->fifo_ctrl, (SPI_FIFO_CTRL_OFFSET+zx29spi->virtbase)); | 
 | //	writel(chip->intr_en, SPI_INTR_EN(zx297520v2spi->virtbase)); | 
 | 	writel(chip->com_ctrl, (zx29spi->virtbase + SPI_COM_CTRL_OFFSET)); | 
 | } | 
 |  | 
 | /* | 
 |  * Default spi Register Values | 
 |  */ | 
 | #define DEFAULT_SPI_COM_CTRL ( \ | 
 | 	GEN_MASK_BITS(LOOPBACK_DISABLED, SPI_COM_CTRL_MASK_LBM, 0) | \ | 
 | 	GEN_MASK_BITS(SPI_DISABLED, SPI_COM_CTRL_MASK_SSPE, 1) | \ | 
 | 	GEN_MASK_BITS(SPI_MASTER, SPI_COM_CTRL_MASK_MS, 2) \ | 
 | ) | 
 |  | 
 | #define DEFAULT_SPI_FMT_CTRL ( \ | 
 | 	GEN_MASK_BITS(SPI_INTERFACE_MOTOROLA_SPI, SPI_FMT_CTRL_MASK_FRF, 0) | \ | 
 | 	GEN_MASK_BITS(SPI_CLK_POL_IDLE_LOW, SPI_FMT_CTRL_MASK_POL, 2) | \ | 
 | 	GEN_MASK_BITS(SPI_CLK_FIRST_EDGE, SPI_FMT_CTRL_MASK_PHA, 3) | \ | 
 | 	GEN_MASK_BITS(SPI_DATA_BITS_8, SPI_FMT_CTRL_MASK_DSS, 4) \ | 
 | ) | 
 |  | 
 | #define DEFAULT_SPI_FIFO_CTRL ( \ | 
 | 	GEN_MASK_BITS(SPI_DMA_DISABLED, SPI_FIFO_CTRL_MASK_RX_DMA_EN, 2) | \ | 
 | 	GEN_MASK_BITS(SPI_DMA_DISABLED, SPI_FIFO_CTRL_MASK_TX_DMA_EN, 3) | \ | 
 | 	GEN_MASK_BITS(SPI_FIFO_THRES_8, SPI_FIFO_CTRL_MASK_RX_FIFO_THRES, 4) | \ | 
 | 	GEN_MASK_BITS(SPI_FIFO_THRES_8, SPI_FIFO_CTRL_MASK_TX_FIFO_THRES, 8) \ | 
 | ) | 
 |  | 
 |  | 
 |  | 
 | static void load_spi_default_config(struct zx29_spi *zx29spi) | 
 | { | 
 | 	writel(CLEAR_ALL_INTERRUPTS, (SPI_INTR_SR_OFFSET+zx29spi->virtbase)); | 
 | 	writel(ENABLE_INTERRUPTS, (SPI_INTR_EN_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | 	writel(DEFAULT_SPI_FMT_CTRL, (SPI_FMT_CTRL_OFFSET+zx29spi->virtbase)); | 
 | 	writel(DEFAULT_SPI_FIFO_CTRL, (SPI_FIFO_CTRL_OFFSET+zx29spi->virtbase)); | 
 | 	writel(DEFAULT_SPI_COM_CTRL, (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static void readwriter(struct zx29_spi *zx29spi) | 
 | { | 
 | 	uint32_t fifo_sr; | 
 | 	uint32_t rd_max, wr_max; | 
 | 	uint32_t rx_fifo_cnt_msk; | 
 | 	uint32_t tx_fifo_cnt_msk; | 
 | 	uint32_t tx_fifo_cnt_pos; | 
 |  | 
 | 	if (zx29spi->pdev->id == 1) { | 
 | 		rx_fifo_cnt_msk = SPI1_FIFO_SR_MASK_RX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_msk = SPI1_FIFO_SR_MASK_TX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_pos = SPI1_FIFO_SR_SHIFT_TX_CNT; | 
 | 	} else { | 
 | 		rx_fifo_cnt_msk = SPI0_FIFO_SR_MASK_RX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_msk = SPI0_FIFO_SR_MASK_TX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_pos = SPI0_FIFO_SR_SHIFT_TX_CNT; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * The FIFO depth is different between primecell variants. | 
 | 	 * I believe filling in too much in the FIFO might cause | 
 | 	 * errons in 8bit wide transfers on ARM variants (just 8 words | 
 | 	 * FIFO, means only 8x8 = 64 bits in FIFO) at least. | 
 | 	 * | 
 | 	 * To prevent this issue, the TX FIFO is only filled to the | 
 | 	 * unused RX FIFO fill length, regardless of what the TX | 
 | 	 * FIFO status flag indicates. | 
 | 	 */ | 
 | 	//printk("[yuwei]%s, rx: %p, rxend: %p, tx: %p, txend: %p\n", | 
 | 	//	__func__, zx29spi->rx, zx29spi->rx_end, zx29spi->tx, zx29spi->tx_end); | 
 |  | 
 | 	fifo_sr = readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase)); | 
 | 	rd_max = (fifo_sr & rx_fifo_cnt_msk) >> SPI_FIFO_SR_SHIFT_RX_CNT; | 
 | 	wr_max = (fifo_sr & tx_fifo_cnt_msk) >> tx_fifo_cnt_pos; | 
 | 	if ((fifo_sr & SPI_FIFO_SR_MASK_BUSY) && wr_max) { | 
 | 		wr_max--; | 
 | 	} | 
 |  | 
 | 	//read rx fifo to empty first | 
 | 	while ((zx29spi->rx < zx29spi->rx_end) && rd_max--) { | 
 | 		switch (zx29spi->read) { | 
 | 		case READING_NULL: | 
 | 			readw((SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			break; | 
 | 		case READING_U8: | 
 | 			*(u8 *) (zx29spi->rx) = | 
 | 				readw((SPI_DR_OFFSET+zx29spi->virtbase)) & 0xFFU; | 
 | 			break; | 
 | 		case READING_U16: | 
 | 			*(u16 *) (zx29spi->rx) = | 
 | 				(u16) readw((SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			break; | 
 | 		case READING_U32: | 
 | 			*(u32 *) (zx29spi->rx) = | 
 | 				readl((SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			break; | 
 | 		} | 
 | 		zx29spi->rx += (zx29spi->cur_chip->n_bytes); | 
 | 		zx29spi->exp_fifo_level--; | 
 | 	} | 
 |  | 
 | 	//write | 
 | 	while ((zx29spi->tx < zx29spi->tx_end) && wr_max--) { | 
 | 		switch (zx29spi->write) { | 
 | 		case WRITING_NULL: | 
 | 			writew(0x0, (SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			break; | 
 | 		case WRITING_U8: | 
 | 			writew(*(u8 *) (zx29spi->tx), (SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			break; | 
 | 		case WRITING_U16: | 
 | 			writew((*(u16 *) (zx29spi->tx)), (SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			break; | 
 | 		case WRITING_U32: | 
 | 			writel(*(u32 *) (zx29spi->tx), (SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			break; | 
 | 		} | 
 | 		zx29spi->tx += (zx29spi->cur_chip->n_bytes); | 
 | 		zx29spi->exp_fifo_level++; | 
 | 	} | 
 |  | 
 | 	cpu_relax(); | 
 |  | 
 | 	/* | 
 | 	 * When we exit here the TX FIFO should be full and the RX FIFO | 
 | 	 * should be empty | 
 | 	 */ | 
 | } | 
 |  | 
 | /* | 
 |  * This DMA functionality is only compiled in if we have | 
 |  * access to the generic DMA devices/DMA engine. | 
 |  */ | 
 | #ifdef CONFIG_SPI_DMA_ENGINE | 
 |  | 
 | static void zx29_fill_txfifo(struct zx29_spi *zx29spi) | 
 | { | 
 | 	uint32_t fifo_sr; | 
 | 	int32_t rd_max, wr_max; | 
 | 	uint32_t rx_fifo_cnt_msk; | 
 | 	uint32_t tx_fifo_cnt_msk; | 
 | 	uint32_t tx_fifo_cnt_pos; | 
 |  | 
 | 	if (zx29spi->pdev->id == 1) { | 
 | 		rx_fifo_cnt_msk = SPI1_FIFO_SR_MASK_RX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_msk = SPI1_FIFO_SR_MASK_TX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_pos = SPI1_FIFO_SR_SHIFT_TX_CNT; | 
 | 	} else { | 
 | 		rx_fifo_cnt_msk = SPI0_FIFO_SR_MASK_RX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_msk = SPI0_FIFO_SR_MASK_TX_FIFO_CNTR; | 
 | 		tx_fifo_cnt_pos = SPI0_FIFO_SR_SHIFT_TX_CNT; | 
 | 	} | 
 |  | 
 | 	while (zx29spi->tx < zx29spi->tx_end) { | 
 | 		fifo_sr = readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase)); | 
 | 		rd_max = (fifo_sr & rx_fifo_cnt_msk) >> SPI_FIFO_SR_SHIFT_RX_CNT; | 
 | 		wr_max = (fifo_sr & tx_fifo_cnt_msk) >> tx_fifo_cnt_pos; | 
 | 		if (fifo_sr & SPI_FIFO_SR_MASK_BUSY) { | 
 | 			wr_max--; | 
 | 		} | 
 | 		wr_max -= rd_max; | 
 | 		wr_max = (wr_max > 0) ? wr_max : 0; | 
 |  | 
 | 		//write | 
 | 		while ((zx29spi->tx < zx29spi->tx_end) && wr_max--) { | 
 | 			writew(0x0, (SPI_DR_OFFSET+zx29spi->virtbase)); | 
 | 			zx29spi->tx += (zx29spi->cur_chip->n_bytes); | 
 | 		} | 
 |  | 
 | 		cpu_relax(); | 
 | 	} | 
 | } | 
 |  | 
 | static void dma_callback(void *data) | 
 | { | 
 | 	struct zx29_spi *zx29spi = (struct zx29_spi *)data; | 
 |     //printk(KERN_INFO "spi:dma transfer complete\n");//YXY | 
 |     up(&zx29spi->sema_dma); | 
 | } | 
 |  | 
 | /* | 
 | static void dma_callback_tx(void *data) | 
 | { | 
 | 	struct zx29_spi *zx29spi = (struct zx29_spi *)data; | 
 |    // printk(KERN_INFO "spi:dma transfer complete tx\n");//YXY | 
 |     printk("[yuwei]%s",__func__); | 
 |    printk("COM=0x%x,FMT=0x%x,FIFO_CTL=0x%x,FIFO_SR=0x%x\n",readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)),readl((SPI_FMT_CTRL_OFFSET+zx29spi->virtbase)),readl((SPI_FIFO_CTRL_OFFSET+zx29spi->virtbase)),readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase))); | 
 |  | 
 |     //up(&g_SpiTransferSemaphore); | 
 | } | 
 | */ | 
 |  | 
 | /** | 
 |  * configure_dma - configures the channels for the next transfer | 
 |  */ | 
 | static int configure_dma(struct zx29_spi *zx29spi) | 
 | { | 
 | //	unsigned int pages; | 
 | //	int ret; | 
 | //	int rx_sglen, tx_sglen; | 
 | 	dma_channel_def rx_conf; | 
 | 	dma_channel_def tx_conf; | 
 | 	struct dma_chan *rxchan = zx29spi->dma_rx_channel; | 
 | 	struct dma_chan *txchan = zx29spi->dma_tx_channel; | 
 | 	struct dma_async_tx_descriptor *rxdesc; | 
 | 	struct dma_async_tx_descriptor *txdesc; | 
 | 	struct spi_transfer *transfer = zx29spi->cur_transfer; | 
 |  | 
 |  | 
 | 	rx_conf.src_addr  = (SPI_DR_OFFSET+zx29spi->phybase); | 
 | 	rx_conf.dest_addr = (unsigned int)zx29spi->rx; | 
 | 	rx_conf.dma_control.tran_mode = TRAN_PERI_TO_MEM; | 
 | 	rx_conf.dma_control.irq_mode  = DMA_ALL_IRQ_ENABLE; | 
 | 	rx_conf.link_addr = 0; | 
 |  | 
 | 	tx_conf.src_addr  = (unsigned int)zx29spi->tx; | 
 | 	tx_conf.dest_addr =  (SPI_DR_OFFSET+zx29spi->phybase); | 
 | 	tx_conf.dma_control.tran_mode  = TRAN_MEM_TO_PERI; | 
 | 	tx_conf.dma_control.irq_mode  = DMA_ALL_IRQ_ENABLE; | 
 | 	tx_conf.link_addr = 0; | 
 |  | 
 |  | 
 | 	/* Check that the channels are available */ | 
 | 	if (!rxchan || !txchan) | 
 | 		return -ENODEV; | 
 |  | 
 | 	/* | 
 | 	 * If supplied, the DMA burstsize should equal the FIFO trigger level. | 
 | 	 * Notice that the DMA engine uses one-to-one mapping. Since we can | 
 | 	 * not trigger on 2 elements this needs explicit mapping rather than | 
 | 	 * calculation. | 
 | 	 */ | 
 | 	switch (zx29spi->rx_lev_trig) { | 
 |     case SPI_RX_1_OR_MORE_ELEM: | 
 |         rx_conf.dma_control.src_burst_len = DMA_BURST_LEN_1; | 
 |         rx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_1; | 
 |         break; | 
 |     case SPI_RX_4_OR_MORE_ELEM: | 
 |         rx_conf.dma_control.src_burst_len = DMA_BURST_LEN_4; | 
 |         rx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_4; | 
 |         break; | 
 |     case SPI_RX_8_OR_MORE_ELEM: | 
 |         rx_conf.dma_control.src_burst_len = DMA_BURST_LEN_8; | 
 |         rx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_8; | 
 |         break; | 
 |     case SPI_RX_16_OR_MORE_ELEM: | 
 |         rx_conf.dma_control.src_burst_len = DMA_BURST_LEN_16; | 
 |         rx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_16; | 
 |         break; | 
 |     case SPI_RX_32_OR_MORE_ELEM: | 
 |         rx_conf.dma_control.src_burst_len = DMA_BURST_LEN_ALL; | 
 |         rx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_ALL; | 
 |         break; | 
 |     default: | 
 |         rx_conf.dma_control.src_burst_len = zx29spi->vendor->fifodepth >> 1; | 
 |         rx_conf.dma_control.dest_burst_len = zx29spi->vendor->fifodepth >> 1; | 
 |         break; | 
 |     } | 
 |  | 
 | 	switch (zx29spi->tx_lev_trig) { | 
 | 	case SPI_TX_1_OR_MORE_EMPTY_LOC: | 
 |         tx_conf.dma_control.src_burst_len = DMA_BURST_LEN_1; | 
 |         tx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_1; | 
 |         break; | 
 | 	case SPI_TX_4_OR_MORE_EMPTY_LOC: | 
 |         tx_conf.dma_control.src_burst_len = DMA_BURST_LEN_4; | 
 |         tx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_4; | 
 |         break; | 
 | 	case SPI_TX_8_OR_MORE_EMPTY_LOC: | 
 |         tx_conf.dma_control.src_burst_len = DMA_BURST_LEN_8; | 
 |         tx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_8; | 
 |         break; | 
 | 	case SPI_TX_16_OR_MORE_EMPTY_LOC: | 
 |         tx_conf.dma_control.src_burst_len = DMA_BURST_LEN_16; | 
 |         tx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_16; | 
 |         break; | 
 | 	case SPI_TX_32_OR_MORE_EMPTY_LOC: | 
 |         tx_conf.dma_control.src_burst_len = DMA_BURST_LEN_ALL; | 
 |         tx_conf.dma_control.dest_burst_len = DMA_BURST_LEN_ALL; | 
 |         break; | 
 |     default: | 
 |         tx_conf.dma_control.src_burst_len = zx29spi->vendor->fifodepth >> 1; | 
 |         tx_conf.dma_control.dest_burst_len = zx29spi->vendor->fifodepth >> 1; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	switch (zx29spi->read) { | 
 | 	case READING_NULL: | 
 | 		/* Use the same as for writing */ | 
 |         rx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         rx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         rx_conf.count     = zx29spi->cur_transfer->len; | 
 |         break; | 
 |     case READING_U8: | 
 |         rx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         rx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         rx_conf.count     = zx29spi->cur_transfer->len; | 
 |         break; | 
 |     case READING_U16: | 
 |         rx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_16BIT; | 
 |         rx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_16BIT; | 
 |         rx_conf.count     = zx29spi->cur_transfer->len; | 
 |         break; | 
 |     case READING_U32: | 
 |         rx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_32BIT; | 
 |         rx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_32BIT; | 
 |         rx_conf.count     = zx29spi->cur_transfer->len; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	switch (zx29spi->write) { | 
 | 	case WRITING_NULL: | 
 | 		/* Use the same as for reading */ | 
 |         tx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         tx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         tx_conf.count     = zx29spi->cur_transfer->len; | 
 |         break; | 
 |     case WRITING_U8: | 
 |         tx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         tx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_8BIT; | 
 |         tx_conf.count     = zx29spi->cur_transfer->len; | 
 |         break; | 
 |     case WRITING_U16: | 
 |         tx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_16BIT; | 
 |         tx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_16BIT; | 
 |         tx_conf.count     = zx29spi->cur_transfer->len; | 
 |         break; | 
 |     case WRITING_U32: | 
 |         tx_conf.dma_control.src_burst_size = DMA_BURST_SIZE_32BIT; | 
 |         tx_conf.dma_control.dest_burst_size = DMA_BURST_SIZE_32BIT; | 
 |         tx_conf.count     = zx29spi->cur_transfer->len; | 
 | 	break; | 
 | 	} | 
 |  | 
 |     dmaengine_slave_config(rxchan,(struct dma_slave_config*)&rx_conf); | 
 |     dmaengine_slave_config(txchan,(struct dma_slave_config*)&tx_conf); | 
 |  | 
 |     /* Submit and fire RX and TX with TX last so we're ready to read! */ | 
 |     if (zx29spi->rx) { | 
 | 	//printk("[yuwei]%s,tx=%p,rx=%p,len=%d\n",__func__,zx29spi->tx,zx29spi->rx,zx29spi->cur_transfer->len); | 
 | 	//printk("[yuwei]tx_conf:sb_len=%d,db_len=%d, sb_size=%d,db_size=%d\n",tx_conf.dma_control.src_burst_len, tx_conf.dma_control.dest_burst_len, tx_conf.dma_control.src_burst_size,  tx_conf.dma_control.dest_burst_size); | 
 | 	//printk("[yuwei]rx_conf:sb_len=%d,db_len=%d, sb_size=%d,db_size=%d\n",rx_conf.dma_control.src_burst_len, rx_conf.dma_control.dest_burst_len, rx_conf.dma_control.src_burst_size, rx_conf.dma_control.dest_burst_size); | 
 |  | 
 | 		rxdesc= rxchan->device->device_prep_interleaved_dma(rxchan,NULL,0); | 
 | 		txdesc= txchan->device->device_prep_interleaved_dma(txchan,NULL,0); | 
 |         /* Put the callback on the RX transfer only, that should finish last */ | 
 |         rxdesc->callback = dma_callback; | 
 |         rxdesc->callback_param = zx29spi; | 
 | 	// txdesc->callback = dma_callback_tx; | 
 | 	// txdesc->callback_param = zx29spi; | 
 |  | 
 |         dmaengine_submit(rxdesc); | 
 |         dma_async_issue_pending(rxchan); | 
 | 		if (transfer->tx_dma) { | 
 | 			/* SPI RX buffer may overflow in DMA busy situation. */ | 
 | 	        dmaengine_submit(txdesc); | 
 | 	        dma_async_issue_pending(txchan); | 
 | 			zx29spi->dma_running = TX_TRANSFER | RX_TRANSFER; | 
 | 			enable_irq(zx29spi->irq);	/* detect overflow through interrupt */ | 
 | 		} else { | 
 | 			zx29_fill_txfifo(zx29spi); | 
 | 			zx29spi->dma_running = RX_TRANSFER; | 
 | 		} | 
 |     } | 
 |     else if (zx29spi->tx){ | 
 |         txdesc = txchan->device->device_prep_interleaved_dma(txchan,NULL,0); | 
 |         txdesc->callback = dma_callback; | 
 |         txdesc->callback_param = zx29spi; | 
 |         dmaengine_submit(txdesc); | 
 |         dma_async_issue_pending(txchan); | 
 | 		zx29spi->dma_running = TX_TRANSFER; | 
 |     } | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | extern bool zx29_dma_filter_fn(struct dma_chan *chan, void *param); | 
 | static int __devinit zx29_dma_probe(struct zx29_spi *zx29spi) | 
 | { | 
 | 	dma_cap_mask_t mask; | 
 |  | 
 | 	/* Try to acquire a generic DMA engine slave channel */ | 
 | 	dma_cap_zero(mask); | 
 | 	dma_cap_set(DMA_SLAVE, mask); | 
 | 	/* | 
 | 	 * We need both RX and TX channels to do DMA, else do none | 
 | 	 * of them. | 
 | 	 */ | 
 | 	zx29spi->dma_rx_channel = dma_request_channel(mask, | 
 |                         zx29_dma_filter_fn, | 
 | 					    zx29spi->master_info->dma_rx_param); | 
 | 	if (!zx29spi->dma_rx_channel) { | 
 | 		dev_dbg(&zx29spi->pdev->dev, "no RX DMA channel!\n"); | 
 | 		goto err_no_rxchan; | 
 | 	} | 
 |  | 
 | 	zx29spi->dma_tx_channel = dma_request_channel(mask, | 
 |                         zx29_dma_filter_fn, | 
 | 					    zx29spi->master_info->dma_tx_param); | 
 | 	if (!zx29spi->dma_tx_channel) { | 
 | 		dev_dbg(&zx29spi->pdev->dev, "no TX DMA channel!\n"); | 
 | 		goto err_no_txchan; | 
 | 	} | 
 |  | 
 | 	zx29spi->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL); | 
 | 	if (!zx29spi->dummypage) { | 
 | 		dev_dbg(&zx29spi->pdev->dev, "no DMA dummypage!\n"); | 
 | 		goto err_no_dummypage; | 
 | 	} | 
 |  | 
 | 	dev_info(&zx29spi->pdev->dev, "setup for DMA on RX %s, TX %s\n", | 
 | 		 dma_chan_name(zx29spi->dma_rx_channel), | 
 | 		 dma_chan_name(zx29spi->dma_tx_channel)); | 
 |  | 
 | 	return 0; | 
 |  | 
 | err_no_dummypage: | 
 | 	dma_release_channel(zx29spi->dma_tx_channel); | 
 | err_no_txchan: | 
 | 	dma_release_channel(zx29spi->dma_rx_channel); | 
 | 	zx29spi->dma_rx_channel = NULL; | 
 | err_no_rxchan: | 
 | 	dev_err(&zx29spi->pdev->dev, | 
 | 			"Failed to work in dma mode, work without dma!\n"); | 
 | 	return -ENODEV; | 
 | } | 
 |  | 
 | static void terminate_dma(struct zx29_spi *zx29spi) | 
 | { | 
 | 	struct dma_chan *rxchan = zx29spi->dma_rx_channel; | 
 | 	struct dma_chan *txchan = zx29spi->dma_tx_channel; | 
 |  | 
 | 	dmaengine_terminate_all(rxchan); | 
 | 	dmaengine_terminate_all(txchan); | 
 | //	unmap_free_dma_scatter(zx29spi); | 
 | 	zx29spi->dma_running = 0; | 
 | } | 
 |  | 
 | static void zx29_dma_remove(struct zx29_spi *zx29spi) | 
 | { | 
 | 	if (zx29spi->dma_running) | 
 | 		terminate_dma(zx29spi); | 
 | 	if (zx29spi->dma_tx_channel) | 
 | 		dma_release_channel(zx29spi->dma_tx_channel); | 
 | 	if (zx29spi->dma_rx_channel) | 
 | 		dma_release_channel(zx29spi->dma_rx_channel); | 
 | 	kfree(zx29spi->dummypage); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | static irqreturn_t zx29_spi_irq(int irqno, void *dev_id) | 
 | { | 
 | 	struct zx29_spi *zx29spi = dev_id; | 
 |  | 
 | 	disable_irq_nosync(zx29spi->irq); | 
 | 	up(&zx29spi->sema_dma); | 
 |  | 
 | 	//pr_info("spi_irq %X-%X\n", zx29spi->dma_running, readl((SPI_INTR_SR_OFFSET+zx29spi->virtbase))); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static int zx29_do_interrupt_dma_transfer(struct zx29_spi *zx29spi) | 
 | { | 
 | 	u32 irqflags = ENABLE_ALL_INTERRUPTS; | 
 | 	struct spi_transfer *transfer = zx29spi->cur_transfer; | 
 | 	int ret = 0; | 
 |  | 
 | 	if((void *)transfer->tx_dma != NULL){ | 
 | 		zx29spi->tx = (void *)transfer->tx_dma; | 
 | 		zx29spi->tx_end = zx29spi->tx + zx29spi->cur_transfer->len; | 
 | 	} | 
 | 	if((void *)transfer->rx_dma != NULL){ | 
 | 		zx29spi->rx = (void *)transfer->rx_dma; | 
 | 		zx29spi->rx_end = zx29spi->rx + zx29spi->cur_transfer->len; | 
 |  | 
 | 		/*if tx is null, use rx buffer as a dummy tx buffer.*/ | 
 | 		if((void *)transfer->tx_dma == NULL){ | 
 | 			zx29spi->tx = (void *)transfer->rx_dma; | 
 | 			zx29spi->tx_end = zx29spi->tx + zx29spi->cur_transfer->len; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	zx29spi->write = zx29spi->tx ? zx29spi->cur_chip->write : WRITING_NULL; | 
 | 	zx29spi->read = zx29spi->rx ? zx29spi->cur_chip->read : READING_NULL; | 
 |  | 
 | 	/* If we're using DMA, set up DMA here */ | 
 | 	if (zx29spi->cur_chip->enable_dma) { | 
 | 		/* Configure DMA transfer */ | 
 | 		ret = configure_dma(zx29spi); | 
 | 		if (ret) { | 
 | 			dev_err(&zx29spi->pdev->dev, "configuration of DMA failed, fall back to interrupt mode\n"); | 
 | 			goto err_config_dma; | 
 | 		} | 
 | 		/* Disable interrupts in DMA mode, IRQ from DMA controller */ | 
 | 		irqflags = DISABLE_ALL_INTERRUPTS; | 
 | 	} | 
 |  | 
 | 	/* config interrupts */ | 
 | 	/* writel(irqflags, (SPI_INTR_EN_OFFSET+zx29spi->virtbase));	//spi interrupt mode is not supported. */ | 
 |  | 
 | 	/* Enable SSP, turn on interrupts */ | 
 | //	writel(readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) | SPI_COM_CTRL_MASK_SSPE,  (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | 	if (zx29spi->cur_chip->enable_dma) | 
 | 	{ | 
 | 		ret = down_timeout(&zx29spi->sema_dma, msecs_to_jiffies(1500)); | 
 | 		//printk("COM=0x%x,FMT=0x%x,FIFO_CTL=0x%x,FIFO_SR=0x%x\n",readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)),readl((SPI_FMT_CTRL_OFFSET+zx29spi->virtbase)),readl((SPI_FIFO_CTRL_OFFSET+zx29spi->virtbase)),readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase))); | 
 | 		if (ret < 0) { | 
 | 			panic("spi transfer timeout\n"); | 
 | 		} | 
 |  | 
 | 		while (readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase)) & SPI_FIFO_SR_MASK_BUSY) | 
 | 		{ | 
 | 			cpu_relax(); | 
 | 		} | 
 |  | 
 | 		if (zx29spi->dma_running == (TX_TRANSFER | RX_TRANSFER)) { | 
 | 			u32 intr_status; | 
 | 			intr_status = readl((SPI_INTR_SR_OFFSET+zx29spi->virtbase)); | 
 | 			if (intr_status & SPI_INTR_SR_SCLR_MASK_RX_OVERRUN_INTR) { | 
 | 				terminate_dma(zx29spi); | 
 | 				dev_err(&zx29spi->cur_msg->spi->dev, "spi rx fifo overflow status = %X!!\n", intr_status); | 
 | 				ret = -EIO; | 
 | 			} else | 
 | 				disable_irq_nosync(zx29spi->irq); | 
 | 		} | 
 | 		zx29spi->dma_running = 0; | 
 | 	} | 
 |  | 
 | err_config_dma: | 
 | 	if(ret) | 
 | 	{ | 
 | 		dev_err(&zx29spi->pdev->dev, "down_interruptible, ret=%d\n",ret); | 
 | 	} | 
 | //	writel(readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & ~ SPI_COM_CTRL_MASK_SSPE, (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | static int zx29_do_polling_transfer(struct zx29_spi *zx29spi) | 
 | { | 
 | 	struct spi_transfer *transfer = zx29spi->cur_transfer; | 
 | /*	unsigned long time, timeout;	*/ | 
 | 	int ret = 0; | 
 |  | 
 | 	//printk("[yuwei]%s,tx=%p,tx_dma=%p,rx=%p,rx_dma=%p,len=%d\n",__func__,transfer->tx_buf,transfer->tx_dma,transfer->rx_buf,transfer->rx_dma,transfer->len); | 
 |  | 
 |  | 
 | 	/* Flush FIFOs and enable SSP */ | 
 | 	//flush(zx29spi); | 
 | 	//writel((readl(SSP_CR1(zx297520v27502ssp->virtbase)) | SSP_CR1_MASK_SSE), | 
 | 	//       SSP_CR1(zx297520v27502ssp->virtbase)); | 
 | /* | 
 | 	while (readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & SPI_COM_CTRL_MASK_SSPE_BACK); | 
 | 	writel(readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) | SPI_COM_CTRL_MASK_SSPE,  (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 | */ | 
 | 	dev_dbg(&zx29spi->pdev->dev, "polling transfer ongoing ...\n"); | 
 |  | 
 | 	/* timeout = jiffies + msecs_to_jiffies(SPI_POLLING_TIMEOUT); */ | 
 |  | 
 | 	if (!zx29spi->tx && !zx29spi->rx) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/*read and write*/ | 
 | 	while ((zx29spi->tx < zx29spi->tx_end) || (zx29spi->rx < zx29spi->rx_end)) { | 
 | 		readwriter(zx29spi); | 
 | #if 0 | 
 | 		time = jiffies; | 
 | 		if (time_after(time, timeout)) { | 
 | 			dev_warn(&zx29spi->pdev->dev, "%s: read write timeout!\n", __func__); | 
 | 			ret = -EIO; | 
 | 			goto out; | 
 | 		} | 
 | #endif | 
 | 	} | 
 | 	while (readl((SPI_FIFO_SR_OFFSET+zx29spi->virtbase)) & SPI_FIFO_SR_MASK_BUSY) { | 
 | 		cpu_relax(); | 
 | #if 0 | 
 | 		time = jiffies; | 
 | 		if (time_after(time, timeout)) { | 
 | 			dev_warn(&zx29spi->pdev->dev, "%s: wait busy timeout!\n", __func__); | 
 | 			ret = -EIO; | 
 | 			goto out; | 
 | 		} | 
 | #endif | 
 | 	} | 
 |  | 
 | out: | 
 | /* | 
 | 	while (~ readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & SPI_COM_CTRL_MASK_SSPE_BACK); | 
 | 	writel(readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & ~ SPI_COM_CTRL_MASK_SSPE, (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 | */ | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int zx29_spi_map_mssg(struct zx29_spi *zx29spi, | 
 | 						struct spi_message *msg) | 
 | { | 
 | 	struct device *dev = &msg->spi->dev; | 
 | 	struct spi_transfer *transfer; | 
 | 	int ret = 0; | 
 |  | 
 | 	if (msg->is_dma_mapped) | 
 | 		return 0; | 
 |  | 
 | 	/* Map until end or first fail */ | 
 | 	list_for_each_entry(transfer, &msg->transfers, transfer_list) { | 
 |  | 
 | 		if(!virt_addr_valid(transfer->tx_buf)){ | 
 | 			transfer->tx_dma = (dma_addr_t)transfer->tx_buf; | 
 | 			transfer->tx_buf = 0; | 
 | 		} | 
 |  | 
 | 		if(!virt_addr_valid(transfer->rx_buf)){ | 
 | 			transfer->rx_dma = (dma_addr_t)transfer->rx_buf; | 
 | 			transfer->rx_buf = 0; | 
 | 		} | 
 |  | 
 | 		if (transfer->len <= zx29spi->vendor->fifodepth || transfer->tx_dma || transfer->rx_dma ) | 
 | 			continue; | 
 |  | 
 | 		if (transfer->tx_buf != NULL) { | 
 | 			transfer->tx_dma = dma_map_single(dev,(void *)transfer->tx_buf, transfer->len, DMA_TO_DEVICE); | 
 | 			if (dma_mapping_error(dev, transfer->tx_dma)) { | 
 | 				dev_err(dev, "dma_map_single spi Tx failed\n"); | 
 | 				transfer->tx_dma = 0; | 
 | 				ret |= -ENOMEM; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (transfer->rx_buf != NULL) { | 
 | 			transfer->rx_dma = dma_map_single(dev, transfer->rx_buf, transfer->len, DMA_FROM_DEVICE); | 
 | 			if (dma_mapping_error(dev, transfer->rx_dma)) { | 
 | 				dev_err(dev, "dma_map_single spi Rx failed\n"); | 
 | 				transfer->rx_dma = 0; | 
 | 				ret |= -ENOMEM; | 
 | 			} | 
 |  | 
 | 			if (!transfer->rx_dma && transfer->tx_dma && transfer->tx_buf) { | 
 | 				dma_unmap_single(dev, transfer->tx_dma,	transfer->len, DMA_TO_DEVICE); | 
 | 				transfer->tx_dma = 0; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void zx29_spi_unmap_mssg(struct zx29_spi *zx29spi, | 
 | 						struct spi_message *msg) | 
 | { | 
 | 	struct device *dev = &msg->spi->dev; | 
 | 	struct spi_transfer *transfer; | 
 |  | 
 | 	if (msg->is_dma_mapped) | 
 | 		return; | 
 |  | 
 | 	list_for_each_entry(transfer, &msg->transfers, transfer_list) { | 
 |  | 
 | 		if ( (!transfer->tx_buf && transfer->tx_dma) || (! transfer->rx_buf &&  transfer->rx_dma) ) | 
 | 			continue; | 
 |  | 
 | 		if (transfer->rx_buf != NULL && transfer->rx_dma) | 
 | 			dma_unmap_single(dev, transfer->rx_dma,	transfer->len, DMA_FROM_DEVICE); | 
 |  | 
 | 		if (transfer->tx_buf != NULL && transfer->tx_dma) | 
 | 			dma_unmap_single(dev, transfer->tx_dma,	transfer->len, DMA_TO_DEVICE); | 
 | 	} | 
 | } | 
 |  | 
 | static int zx29_transfer_one_message(struct spi_master *master, | 
 | 				      struct spi_message *msg) | 
 | { | 
 | 	struct zx29_spi *zx29spi = spi_master_get_devdata(master); | 
 | 	struct spi_device *spi = msg->spi; | 
 | 	struct spi_transfer *transfer; | 
 | 	unsigned	cs_change = 1; | 
 | 	const int	nsecs = 100; | 
 | 	int	ret = 0; | 
 | 	 | 
 | 	if(!zx29spi) | 
 | 		return -EINVAL; | 
 | 	//printk(KERN_INFO "ssp:in function  %s \n", __FUNCTION__); | 
 | #if SPI_PSM_CONTROL | 
 |     zx29_spi_set_active(&zx29spi->psm_lock); | 
 | #endif | 
 | 	//mutex_lock(&zx29spi->spi_lock); | 
 | 	//printk(KERN_INFO "ssp:lock \n"); | 
 | 	/* Initial message state */ | 
 | 	zx29spi->cur_msg = msg; | 
 | 	/* Setup the SPI using the per chip configuration */ | 
 | 	zx29spi->cur_chip = spi_get_ctldata(msg->spi); | 
 |  | 
 | 	if ((clk_get_rate(zx29spi->spi_clk) / 2) != spi->max_speed_hz) { | 
 | 		clk_set_rate(zx29spi->spi_clk, spi->max_speed_hz * 2); | 
 | 	} | 
 |  | 
 | 	restore_state(zx29spi); | 
 |  | 
 | 	ret = zx29_spi_map_mssg(zx29spi, msg); | 
 | 	/* continue with polling mode | 
 | 	if(ret){ | 
 | 		goto out; | 
 | 	} | 
 | 	*/ | 
 |  | 
 | 	while (readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & SPI_COM_CTRL_MASK_SSPE_BACK); | 
 | 	writel(readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) | SPI_COM_CTRL_MASK_SSPE,  (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | 	list_for_each_entry(transfer, &msg->transfers, transfer_list) { | 
 |  | 
 | 		zx29spi->cur_transfer = transfer; | 
 | 		if (transfer->bits_per_word || transfer->speed_hz) | 
 | 			dev_warn(&msg->spi->dev, "ignore bits & speed setting in transfer."); | 
 |  | 
 | 		if((void *)transfer->tx_buf != NULL){ | 
 | 			zx29spi->tx = (void *)transfer->tx_buf; | 
 | 			zx29spi->tx_end = zx29spi->tx + zx29spi->cur_transfer->len; | 
 | 		} | 
 | 		else | 
 | 			zx29spi->tx = zx29spi->tx_end =  NULL; | 
 |  | 
 | 		if((void *)transfer->rx_buf != NULL){ | 
 | 			zx29spi->rx = (void *)transfer->rx_buf; | 
 | 			zx29spi->rx_end = zx29spi->rx + zx29spi->cur_transfer->len; | 
 |  | 
 | 			/*if tx is null, use rx buffer as a dummy tx buffer.*/ | 
 | 			if((void *)transfer->tx_buf == NULL){ | 
 | 				zx29spi->tx = (void *)transfer->rx_buf; | 
 | 				zx29spi->tx_end = zx29spi->tx + zx29spi->cur_transfer->len; | 
 | 			} | 
 | 		} | 
 | 		else | 
 | 			zx29spi->rx = zx29spi->rx_end =  NULL; | 
 |  | 
 | 		zx29spi->write = zx29spi->tx ? zx29spi->cur_chip->write : WRITING_NULL; | 
 | 		zx29spi->read = zx29spi->rx ? zx29spi->cur_chip->read : READING_NULL; | 
 |  | 
 | 		if (transfer->rx_buf || transfer->rx_dma) | 
 | 			flush(zx29spi); | 
 | 		writel(CLEAR_ALL_INTERRUPTS, (SPI_INTR_SR_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | 		if (cs_change) { | 
 | 			zx29spi->cur_chip->cs_control(ZX29_CS_ACTIVE); | 
 | 		} | 
 | 		cs_change = transfer->cs_change; | 
 |  | 
 | 		if (zx29spi->cur_chip->xfer_type == POLLING_TRANSFER || (!transfer->tx_dma && !transfer->rx_dma)) { | 
 | 			ret = zx29_do_polling_transfer(zx29spi); | 
 | 			#if defined(CONFIG_DEBUG_FS) | 
 | 				zx29spi->spi_poll_cnt ++; | 
 | 			#endif | 
 | 		} else { | 
 | 			struct chip_data *chip = zx29spi->cur_chip; | 
 |  | 
 | 			if (transfer->rx_buf || transfer->rx_dma) { | 
 | 				writel((chip->fifo_ctrl | (SPI_FIFO_CTRL_MASK_RX_DMA_EN | SPI_FIFO_CTRL_MASK_TX_DMA_EN)), | 
 | 						(SPI_FIFO_CTRL_OFFSET+zx29spi->virtbase)); | 
 | 			} else { | 
 | 				writel((chip->fifo_ctrl | SPI_FIFO_CTRL_MASK_TX_DMA_EN), (SPI_FIFO_CTRL_OFFSET+zx29spi->virtbase)); | 
 | 			} | 
 |  | 
 | 			ret = zx29_do_interrupt_dma_transfer(zx29spi); | 
 | 			#if defined(CONFIG_DEBUG_FS) | 
 | 				zx29spi->spi_dma_cnt ++; | 
 | 			#endif | 
 |  | 
 | 			/* clear TX/RX DMA Enable */ | 
 | 			writel(chip->fifo_ctrl, (SPI_FIFO_CTRL_OFFSET+zx29spi->virtbase)); | 
 | 		} | 
 |  | 
 | 		if (ret) { | 
 | 			pr_info("ssp:transfer error,transfer=%p\n", transfer); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		/* Update total byte transferred */ | 
 | 		msg->actual_length += zx29spi->cur_transfer->len; | 
 |  | 
 | 		if (transfer->delay_usecs) | 
 | 			udelay(transfer->delay_usecs); | 
 |  | 
 | 		if (cs_change) { | 
 | 			zx29spi->cur_chip->cs_control(ZX29_CS_INACTIVE); | 
 | 			ndelay(nsecs); | 
 | 		} | 
 | 	} | 
 | 	if (ret || !cs_change) { | 
 | 		zx29spi->cur_chip->cs_control(ZX29_CS_INACTIVE); | 
 | 	} | 
 | 	while (~ readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & SPI_COM_CTRL_MASK_SSPE_BACK); | 
 | 	writel(readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & ~ SPI_COM_CTRL_MASK_SSPE, (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | out: | 
 | 	zx29_spi_unmap_mssg(zx29spi, msg); | 
 | 	//mutex_unlock(&zx29spi->spi_lock); | 
 | 	//printk(KERN_INFO "ssp:unlock \n"); | 
 |  | 
 | 	msg->status = ret; | 
 | 	spi_finalize_current_message(master); | 
 |  | 
 | #if SPI_PSM_CONTROL | 
 |     zx29_spi_set_idle(&zx29spi->psm_lock); | 
 | #endif | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static int zx29_prepare_transfer_hardware(struct spi_master *master) | 
 | { | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int zx29_unprepare_transfer_hardware(struct spi_master *master) | 
 | { | 
 | 	//struct zx29_spi *zx29spi = spi_master_get_devdata(master); | 
 |  | 
 | 	//dev_warn(&zx29spi->pdev->dev,"in function %s\n", __FUNCTION__); | 
 |  | 
 | 	/* nothing more to do - disable spi/ssp and power off */ | 
 | 	//writel(readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & ~ SPI_COM_CTRL_MASK_SSPE, (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int verify_controller_parameters(struct zx29_spi *zx29spi, | 
 | 				struct spi_config_chip const *chip_info) | 
 | { | 
 | 	if ((chip_info->iface < SPI_INTERFACE_MOTOROLA_SPI) | 
 | 	    || (chip_info->iface > SPI_INTERFACE_TI_SYNC_SERIAL)) { | 
 | 		dev_err(&zx29spi->pdev->dev, | 
 | 			"interface is configured incorrectly\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if ((chip_info->hierarchy != SPI_MASTER) | 
 | 	    && (chip_info->hierarchy != SPI_SLAVE)) { | 
 | 		dev_err(&zx29spi->pdev->dev, | 
 | 			"hierarchy is configured incorrectly\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 | 	if ((chip_info->com_mode != INTERRUPT_TRANSFER) | 
 | 	    && (chip_info->com_mode != DMA_TRANSFER) | 
 | 	    && (chip_info->com_mode != POLLING_TRANSFER)) { | 
 | 		dev_err(&zx29spi->pdev->dev, | 
 | 			"Communication mode is configured incorrectly\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 | 	switch (chip_info->rx_lev_trig) { | 
 | 	case SPI_RX_1_OR_MORE_ELEM: | 
 | 	case SPI_RX_4_OR_MORE_ELEM: | 
 | 	case SPI_RX_8_OR_MORE_ELEM: | 
 | 		/* These are always OK, all variants can handle this */ | 
 | 		break; | 
 | 	case SPI_RX_16_OR_MORE_ELEM: | 
 | 		if (zx29spi->vendor->fifodepth < 16) { | 
 | 			dev_err(&zx29spi->pdev->dev, | 
 | 			"RX FIFO Trigger Level is configured incorrectly\n"); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		break; | 
 | 	case SPI_RX_32_OR_MORE_ELEM: | 
 | 		if (zx29spi->vendor->fifodepth < 32) { | 
 | 			dev_err(&zx29spi->pdev->dev, | 
 | 			"RX FIFO Trigger Level is configured incorrectly\n"); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 		dev_err(&zx29spi->pdev->dev, | 
 | 			"RX FIFO Trigger Level is configured incorrectly\n"); | 
 | 		return -EINVAL; | 
 | 		break; | 
 | 	} | 
 | 	switch (chip_info->tx_lev_trig) { | 
 | 	case SPI_TX_1_OR_MORE_EMPTY_LOC: | 
 | 	case SPI_TX_4_OR_MORE_EMPTY_LOC: | 
 | 	case SPI_TX_8_OR_MORE_EMPTY_LOC: | 
 | 		/* These are always OK, all variants can handle this */ | 
 | 		break; | 
 | 	case SPI_TX_16_OR_MORE_EMPTY_LOC: | 
 | 		if (zx29spi->vendor->fifodepth < 16) { | 
 | 			dev_err(&zx29spi->pdev->dev, | 
 | 			"TX FIFO Trigger Level is configured incorrectly\n"); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		break; | 
 | 	case SPI_TX_32_OR_MORE_EMPTY_LOC: | 
 | 		if (zx29spi->vendor->fifodepth < 32) { | 
 | 			dev_err(&zx29spi->pdev->dev, | 
 | 			"TX FIFO Trigger Level is configured incorrectly\n"); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 		dev_err(&zx29spi->pdev->dev, | 
 | 			"TX FIFO Trigger Level is configured incorrectly\n"); | 
 | 		return -EINVAL; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct vendor_data vendor_arm = { | 
 | 	.fifodepth = 16, | 
 | 	.max_bpw = 32, | 
 | 	.loopback = true, | 
 | }; | 
 |  | 
 | static struct resource spi0_gpio_resources[] ={ | 
 | 	[0]={ | 
 | 		.start	= GPIO_AP_SPI0_TXD, | 
 | 		.end	= GPIO_AP_SPI0_TXD_FUN, | 
 | 		.name	= "txd", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	}, | 
 | 	[1]={ | 
 | 		.start	= GPIO_AP_SPI0_CLK, | 
 | 		.end	= GPIO_AP_SPI0_CLK_FUN, | 
 | 		.name	= "clk", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	}, | 
 | 	[2]={ | 
 | 		.start	= GPIO_AP_SPI0_CS, | 
 | 		.end	= GPIO_AP_CS_GPIO_FUN, | 
 | 		.name	= "cs", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	}, | 
 | #if 1 | 
 | 	[3]={ | 
 | 		.start	= GPIO_AP_SPI0_RXD, | 
 | 		.end	= GPIO_AP_SPI0_RXD_FUN, | 
 | 		.name	= "rxd", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	} | 
 | #endif | 
 | }; | 
 | #ifdef CONFIG_ARCH_ZX297520V3 | 
 | static struct resource spi1_gpio_resources[] ={ | 
 | 	[0]={ | 
 | 		.start	= GPIO_AP_SPI1_TXD, | 
 | 		.end	= GPIO_AP_SPI1_TXD_FUN, | 
 | 		.name	= "txd", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	}, | 
 | 	[1]={ | 
 | 		.start	= GPIO_AP_SPI1_CLK, | 
 | 		.end	= GPIO_AP_SPI1_CLK_FUN, | 
 | 		.name	= "clk", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	}, | 
 | 	[2]={ | 
 | 		.start	= GPIO_AP_SPI1_CS, | 
 | 		.end	= GPIO_AP_CS1_GPIO_FUN, | 
 | 		.name	= "cs", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	}, | 
 | #if 1 | 
 | 	[3]={ | 
 | 		.start	= GPIO_AP_SPI1_RXD, | 
 | 		.end	= GPIO_AP_SPI1_RXD_FUN, | 
 | 		.name	= "rxd", | 
 | 		.flags	= IORESOURCE_IO, | 
 | 	} | 
 | #endif | 
 | }; | 
 | #endif | 
 | /* | 
 |  * A piece of default chip info unless the platform | 
 |  * supplies it. | 
 |  */ | 
 | static const struct spi_config_chip spi_default_chip_info = { | 
 | 	.com_mode = POLLING_TRANSFER, | 
 | 	.iface = SPI_INTERFACE_MOTOROLA_SPI, | 
 | 	.hierarchy = SPI_MASTER, | 
 | 	.slave_tx_disable = DO_NOT_DRIVE_TX, | 
 | 	.rx_lev_trig = SPI_RX_4_OR_MORE_ELEM, | 
 | 	.tx_lev_trig = SPI_TX_4_OR_MORE_EMPTY_LOC, | 
 | //	.ctrl_len = SSP_BITS_8, | 
 | //	.wait_state = SSP_MWIRE_WAIT_ZERO, | 
 | //	.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX, | 
 | //	.cs_control = default_cs_control, | 
 | }; | 
 |  | 
 | /* | 
 | *    spi ʹÓÃGPIOģʽ¶ÁÈ¡LCD µÄID Begin | 
 | */ | 
 | static void spi_set_gpio_function(int id) | 
 | { | 
 |     //TODO:ÉèÖÃGPIOΪ¹¦ÄܽŠ| 
 | 	if(id == 0){ | 
 | 		/* zx29_gpio_function_sel(GPIO_AP_SPI0_CS, 	 GPIO_AP_SPI0_CS_FUN); */ | 
 | 		zx29_gpio_function_sel(GPIO_AP_SPI0_CLK, GPIO_AP_SPI0_CLK_FUN); | 
 | 		zx29_gpio_function_sel(GPIO_AP_SPI0_RXD, GPIO_AP_SPI0_RXD_FUN); | 
 | 		zx29_gpio_function_sel(GPIO_AP_SPI0_TXD, GPIO_AP_SPI0_TXD_FUN); | 
 | 	} | 
 | 	else if(id==1){ | 
 |  | 
 | 	} | 
 | } | 
 | static void spi_set_gpio_gpio(int id) | 
 | { | 
 |     //TODO:ÉèÖÃGPIOΪGPIO½Å | 
 | 	if(id == 0){ | 
 | 	    /* zx29_gpio_function_sel(GPIO_AP_SPI0_CS, 	GPIO_AP_CS_GPIO_FUN); */ | 
 | 	    zx29_gpio_function_sel(GPIO_AP_SPI0_CLK,	GPIO_AP_CLK_GPIO_FUN); | 
 | 	    zx29_gpio_function_sel(GPIO_AP_SPI0_RXD,	GPIO_AP_RXD_GPIO_FUN); | 
 | 	    zx29_gpio_function_sel(GPIO_AP_SPI0_TXD,	GPIO_AP_TXD_GPIO_FUN); | 
 | 	} | 
 | 	else if(id==1){ | 
 |  | 
 | 	} | 
 | } | 
 | static void spi_set_gpio_val(int gpio_num, int val) | 
 | { | 
 |     zx29_gpio_output_data(gpio_num, val); | 
 | } | 
 |  | 
 | static int spi_get_gpio_val(int gpio_num) | 
 | { | 
 |     //zx29_gpio_set_direction(gpio,GPIO_IN); | 
 |  | 
 |     return zx29_gpio_input_data(gpio_num); | 
 | } | 
 |  | 
 | static void spi_time_delay(int delay/*us*/) | 
 | { | 
 |     udelay(delay); | 
 | } | 
 |  | 
 | void spi_gpio_mode_start(void) | 
 | { | 
 | 	//mutex_lock(&g_zx29_spi->spi_lock); //spi control function mutex. | 
 |     /* set clk tx rx cs to gpio */ | 
 | 	spi_set_gpio_gpio(0); | 
 |  | 
 | 	zx29_gpio_set_direction(GPIO_AP_SPI0_CS, GPIO_OUT); | 
 | 	zx29_gpio_set_direction(GPIO_AP_SPI0_CLK, GPIO_OUT); | 
 | 	zx29_gpio_set_direction(GPIO_AP_SPI0_TXD, GPIO_OUT); | 
 | 	zx29_gpio_set_direction(GPIO_AP_SPI0_RXD, GPIO_IN); | 
 |  | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CS,  SPI_GPIO_HIGH);/* CSµÍÓÐЧ */ | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_LOW);/* clk¿ÕÏÐʱΪµÍ */ | 
 | #if 0 | 
 |     spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_LOW); | 
 |     spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_LOW); | 
 | #endif | 
 | } | 
 | EXPORT_SYMBOL(spi_gpio_mode_start); | 
 | void spi_gpio_mode_stop(void) | 
 | { | 
 | 	/* set clk tx rx cs to function */ | 
 | 	spi_set_gpio_function(0); | 
 | 	//mutex_unlock(&g_zx29_spi->spi_lock); //spi control function mutex. | 
 | } | 
 | EXPORT_SYMBOL(spi_gpio_mode_stop); | 
 |  | 
 | void spi_gpio_write_single8(unsigned char data) | 
 | { | 
 |     int i; | 
 |  | 
 | 	//printk("howard spi_gpio_write_single8 %x\n", data); | 
 |  | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_HIGH); | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CS,  SPI_GPIO_LOW);/* CSµÍÓÐЧ */ | 
 |  | 
 | 	for( i=7; i>=0; i-- ) | 
 | 	{ | 
 | 		spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_LOW); | 
 | 		if ((data >> i) & 0x1) | 
 | 		{ | 
 | 			spi_set_gpio_val(GPIO_AP_SPI0_TXD, SPI_GPIO_HIGH); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			spi_set_gpio_val(GPIO_AP_SPI0_TXD, SPI_GPIO_LOW); | 
 | 		} | 
 | 		spi_time_delay(1); | 
 | 		spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_HIGH); | 
 | 		spi_time_delay(1); | 
 | 	} | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CS,  SPI_GPIO_HIGH); | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_TXD, SPI_GPIO_LOW); | 
 |  | 
 | } | 
 | EXPORT_SYMBOL(spi_gpio_write_single8); | 
 | /******************************************************************************* | 
 |  * Function: | 
 |  * Description: | 
 |  * Parameters: | 
 |  *   Input: | 
 |  * | 
 |  *   Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | unsigned char spi_gpio_read_single8(void) | 
 | { | 
 | 	int i; | 
 | 	unsigned char readData = 0; | 
 |  | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_HIGH); | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CS,  SPI_GPIO_LOW);/* CSµÍÓÐЧ */ | 
 |  | 
 | 	for( i=7; i>=0; i-- ) | 
 | 	{ | 
 | 		spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_LOW); | 
 | 		spi_time_delay(1); | 
 | 		spi_set_gpio_val(GPIO_AP_SPI0_CLK, SPI_GPIO_HIGH); | 
 | 		if( spi_get_gpio_val(GPIO_AP_SPI0_RXD) )/* lcd ¸´ÓÃtx rx */ | 
 | 		{ | 
 | 		    readData |= (1 << i); | 
 | 		} | 
 | 		spi_time_delay(1); | 
 | 	} | 
 | 	spi_set_gpio_val(GPIO_AP_SPI0_CS,  SPI_GPIO_HIGH); | 
 |  | 
 | 	//printk("howard spi_gpio_read_single8 %x\n", readData); | 
 | 	return readData; | 
 | } | 
 | EXPORT_SYMBOL(spi_gpio_read_single8); | 
 |  | 
 | /** | 
 |  * @brief spi gpio mode, cs control | 
 |  * | 
 |  * This function used for lcd 3-wires spi mode. | 
 |  * before cs pull down, spi pads will change to gpio mode. | 
 |  * after cs pull high, spi pads gpio mode recovery to spi mode. | 
 |  * | 
 |  * @param level 0: cs line pull down, no-zero: cs line pull up. | 
 |  * | 
 |  * @retval none | 
 |  */ | 
 | void spi_gpio_3wire_cs(unsigned char level) | 
 | { | 
 | 	if(level){ | 
 | 		spi_set_gpio_val(GPIO_AP_SPI0_CS,  		SPI_GPIO_HIGH); | 
 | 		gpio_direction_input(GPIO_AP_SPI0_TXD); | 
 |  | 
 | 		/* zx29_gpio_function_sel(GPIO_AP_SPI0_CS, 	GPIO_AP_SPI0_CS_FUN); */ | 
 | 		zx29_gpio_function_sel(GPIO_AP_SPI0_CLK, 	GPIO_AP_SPI0_CLK_FUN); | 
 | 		zx29_gpio_function_sel(GPIO_AP_SPI0_TXD, 	GPIO_AP_SPI0_TXD_FUN); | 
 |  | 
 |     		//mutex_unlock(&g_zx29_spi->spi_lock); //spi control function mutex. | 
 | 	} | 
 | 	else{ | 
 | 		//mutex_lock(&g_zx29_spi->spi_lock); | 
 |  | 
 | 		/* zx29_gpio_function_sel(GPIO_AP_SPI0_CS, 	GPIO_AP_CS_GPIO_FUN); */ | 
 | 		zx29_gpio_function_sel(GPIO_AP_SPI0_CLK, 	GPIO_AP_CLK_GPIO_FUN); | 
 | 		zx29_gpio_function_sel(GPIO_AP_SPI0_TXD, 	GPIO_AP_TXD_GPIO_FUN); | 
 |  | 
 | 		gpio_direction_output(GPIO_AP_SPI0_CS, 	SPI_GPIO_LOW); | 
 | 		gpio_direction_output(GPIO_AP_SPI0_CLK, 	SPI_GPIO_LOW); | 
 | 		gpio_direction_output(GPIO_AP_SPI0_TXD, 	SPI_GPIO_LOW); | 
 |  | 
 | 		spi_set_gpio_val(GPIO_AP_SPI0_CLK,  		SPI_GPIO_LOW);/* CSµÍÓÐЧ */ | 
 | 		spi_set_gpio_val(GPIO_AP_SPI0_CS,  		SPI_GPIO_LOW); | 
 | 	} | 
 | } | 
 | EXPORT_SYMBOL(spi_gpio_3wire_cs); | 
 |  | 
 | /** | 
 |  * @brief spi gpio mode, one byte write. | 
 |  * | 
 |  * This function used for lcd 3-wires spi mode. | 
 |  * txd line used tx function and rx function at different time. | 
 |  * | 
 |  * @param reg one byte write data. | 
 |  * | 
 |  * @retval none | 
 |  */ | 
 | void spi_gpio_3wire_write8(unsigned char reg) | 
 | { | 
 | 	int i; | 
 | 	//unsigned char readData = 0; | 
 |  | 
 | 	//write | 
 | 	spi_time_delay(50); | 
 | 	for (i = 0; i < 8; i++) | 
 | 	{ | 
 | 	    gpio_set_value(GPIO_AP_SPI0_CLK, SPI_GPIO_LOW); | 
 | 	    spi_time_delay(50); | 
 |  | 
 | 	    if ((reg & 0x80)==0x80) | 
 | 	    { | 
 | 	        gpio_set_value(GPIO_AP_SPI0_TXD, SPI_GPIO_HIGH); | 
 | 	    } | 
 | 	    else | 
 | 	    { | 
 | 	        gpio_set_value(GPIO_AP_SPI0_TXD, SPI_GPIO_LOW); | 
 | 	    } | 
 | 	    spi_time_delay(50); | 
 |  | 
 | 	    gpio_set_value(GPIO_AP_SPI0_CLK, SPI_GPIO_HIGH); | 
 | 	    spi_time_delay(50); | 
 |  | 
 | 	    reg <<= 1; | 
 | 	} | 
 | 	//spi_time_delay(50); | 
 | } | 
 | EXPORT_SYMBOL(spi_gpio_3wire_write8); | 
 |  | 
 | /** | 
 |  * @brief spi gpio mode, one byte read. | 
 |  * | 
 |  * This function used for lcd 3-wires spi mode. | 
 |  * txd line used tx function and rx function at different time. | 
 |  * | 
 |  * @param none. | 
 |  * | 
 |  * @retval one byte readed data. | 
 |  */ | 
 | unsigned char spi_gpio_3wire_read8(void) | 
 | { | 
 | 	int i; | 
 | 	unsigned char readData = 0; | 
 |   	//read | 
 | 	gpio_direction_input(GPIO_AP_SPI0_TXD); | 
 | 	spi_time_delay(50); | 
 |  | 
 | 	readData = 0; | 
 | 	for (i = 0; i < 8; i++) | 
 | 	{ | 
 | 		readData <<= 1; | 
 | 		gpio_set_value(GPIO_AP_SPI0_CLK, SPI_GPIO_LOW); | 
 | 		spi_time_delay(50); | 
 |  | 
 | 		if (GPIO_HIGH == gpio_get_value(GPIO_AP_SPI0_TXD)) | 
 | 		{ | 
 | 			readData |= 0x01; | 
 | 		} | 
 |  | 
 | 		gpio_set_value(GPIO_AP_SPI0_CLK, SPI_GPIO_HIGH); | 
 | 		spi_time_delay(50); | 
 | 	} | 
 | 	//spi_time_delay(50); | 
 |  | 
 | 	//printk("howard spi_gpio_read_single8 %x\n", readData); | 
 | 	return readData; | 
 | } | 
 | EXPORT_SYMBOL(spi_gpio_3wire_read8); | 
 |  | 
 |  | 
 |  | 
 | static int zx29_setup(struct spi_device *spi) | 
 | { | 
 | 	struct spi_config_chip const *chip_info; | 
 | 	struct chip_data *chip; | 
 | 	unsigned speed_hz; | 
 | 	int status = 0; | 
 | 	struct zx29_spi *zx29spi = spi_master_get_devdata(spi->master); | 
 | 	unsigned int bits = spi->bits_per_word; | 
 | 	u32 tmp; | 
 |  | 
 | 	if ((!spi->max_speed_hz)||(!zx29spi)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	chip = spi_get_ctldata(spi); | 
 |  | 
 | 	if (chip == NULL) { | 
 | 		chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); | 
 | 		if (!chip) { | 
 | 			dev_err(&spi->dev, "cannot alloc chip_data\n"); | 
 | 			return -ENOMEM; | 
 | 		} | 
 | 		dev_dbg(&spi->dev, "allocated memory for controller's runtime state\n"); | 
 | 	} | 
 |  | 
 | 	chip_info = spi->controller_data; | 
 |  | 
 | 	if (chip_info == NULL) { | 
 | 		chip_info = &spi_default_chip_info; | 
 | 		/* spi_board_info.controller_data not is supplied */ | 
 | 		dev_dbg(&spi->dev, "using default chip_info_data settings\n"); | 
 | 	} else | 
 | 		dev_dbg(&spi->dev, "using user suppliedchip_info_data settings\n"); | 
 |  | 
 | 	 /* set spi  clock source at 104MHz/1 */ | 
 | 	// zx297520v2spi->spi_clk->ops->set_division(zx297520v2spi->spi_clk,chip ->clk_div-1); | 
 | 	//writel(chip ->clk_div-1, M0_SSP_CLKDIV_REG_VA); | 
 | 	speed_hz = spi->max_speed_hz; | 
 | //	clk_set_rate(zx29spi->spi_clk, speed_hz * 2); /* f(ssp_clk) = 2*f(ssp_sclk_out) */ | 
 | 	spi->max_speed_hz = clk_round_rate(zx29spi->spi_clk, speed_hz * 2) / 2; | 
 | 	if (spi->max_speed_hz != speed_hz) | 
 | 		dev_warn(&spi->dev, "round speed %dHz differs from requested %dHz.", spi->max_speed_hz, speed_hz); | 
 |  | 
 | 	status = verify_controller_parameters(zx29spi, chip_info); | 
 | 	if (status) { | 
 | 		dev_err(&spi->dev, "controller data is incorrect"); | 
 | 		goto err_config_params; | 
 | 	} | 
 |  | 
 | 	zx29spi->rx_lev_trig = chip_info->rx_lev_trig; | 
 | 	zx29spi->tx_lev_trig = chip_info->tx_lev_trig; | 
 |  | 
 | 	chip->xfer_type = chip_info->com_mode; | 
 |  | 
 | 	if (!chip_info->cs_control) { | 
 | 		if (spi->master->bus_num == 0) | 
 | 			chip->cs_control = default_cs0_control; | 
 | #ifdef CONFIG_ARCH_ZX297520V3 | 
 | 		if (spi->master->bus_num == 1) | 
 | 			chip->cs_control = default_cs1_control; | 
 | #endif | 
 | 		if (spi->master->num_chipselect != 1) | 
 | 			dev_err(&spi->dev, "chip select function is NULL!\n"); | 
 | 	} else | 
 | 		chip->cs_control = chip_info->cs_control; | 
 |  | 
 | 	/* Check bits per word with vendor specific range */ | 
 | 	if ((bits <= 3) || (bits > zx29spi->vendor->max_bpw)) { | 
 | 		status = -ENOTSUPP; | 
 | 		dev_err(&spi->dev, "illegal data size for this controller!\n"); | 
 | 		dev_err(&spi->dev, "This controller can only handle 4 <= n <= %d bit words\n", | 
 | 				zx29spi->vendor->max_bpw); | 
 | 		goto err_config_params; | 
 | 	} else if (bits <= 8) { | 
 | 		dev_dbg(&spi->dev, "4 <= n <=8 bits per word\n"); | 
 | 		chip->n_bytes = 1; | 
 | 		chip->read = READING_U8; | 
 | 		chip->write = WRITING_U8; | 
 | 	} else if (bits <= 16) { | 
 | 		dev_dbg(&spi->dev, "9 <= n <= 16 bits per word\n"); | 
 | 		chip->n_bytes = 2; | 
 | 		chip->read = READING_U16; | 
 | 		chip->write = WRITING_U16; | 
 | 	} else { | 
 | 		dev_dbg(&spi->dev, "17 <= n <= 32 bits per word\n"); | 
 | 		chip->n_bytes = 4; | 
 | 		chip->read = READING_U32; | 
 | 		chip->write = WRITING_U32; | 
 | 	} | 
 |  | 
 | 	/* Now Initialize all register settings required for this chip */ | 
 | 	chip->com_ctrl  = 0; | 
 | 	chip->fmt_ctrl = 0; | 
 | 	chip->fifo_ctrl = 0; | 
 |  | 
 | 	if ((chip_info->com_mode == DMA_TRANSFER) | 
 | 	    && ((zx29spi->master_info)->enable_dma)) { | 
 | 		chip->enable_dma = true; | 
 | 		dev_dbg(&spi->dev, "DMA mode set in controller state\n"); | 
 | 	} else { | 
 | 		chip->enable_dma = false; | 
 | 		dev_dbg(&spi->dev, "DMA mode NOT set in controller state\n"); | 
 | 	} | 
 | 	SPI_WRITE_BITS(chip->fifo_ctrl, SPI_DMA_DISABLED, SPI_FIFO_CTRL_MASK_RX_DMA_EN, 2); | 
 | 	SPI_WRITE_BITS(chip->fifo_ctrl, SPI_DMA_DISABLED, SPI_FIFO_CTRL_MASK_TX_DMA_EN, 3); | 
 |  | 
 | 	if (zx29spi->rx_lev_trig == SPI_RX_8_OR_MORE_ELEM) | 
 | 		SPI_WRITE_BITS(chip->fifo_ctrl, SPI_FIFO_THRES_8, SPI_FIFO_CTRL_MASK_RX_FIFO_THRES, 4); | 
 | 	else | 
 | 		SPI_WRITE_BITS(chip->fifo_ctrl, SPI_FIFO_THRES_4, SPI_FIFO_CTRL_MASK_RX_FIFO_THRES, 4); | 
 | 	if (zx29spi->tx_lev_trig == SPI_TX_8_OR_MORE_EMPTY_LOC) | 
 | 		SPI_WRITE_BITS(chip->fifo_ctrl, SPI_FIFO_THRES_8, SPI_FIFO_CTRL_MASK_TX_FIFO_THRES, 8); | 
 | 	else | 
 | 		SPI_WRITE_BITS(chip->fifo_ctrl, SPI_FIFO_THRES_4, SPI_FIFO_CTRL_MASK_TX_FIFO_THRES, 8); | 
 |  | 
 | 	SPI_WRITE_BITS(chip->fmt_ctrl, bits - 1, 			SPI_FMT_CTRL_MASK_DSS, 4); | 
 | 	SPI_WRITE_BITS(chip->fmt_ctrl, chip_info->iface,	SPI_FMT_CTRL_MASK_FRF, 0); | 
 |  | 
 | 	/* Stuff that is common for all versions */ | 
 | 	if (spi->mode & SPI_CPOL) | 
 | 		tmp = SPI_CLK_POL_IDLE_HIGH; | 
 | 	else | 
 | 		tmp = SPI_CLK_POL_IDLE_LOW; | 
 | 	SPI_WRITE_BITS(chip->fmt_ctrl, tmp, SPI_FMT_CTRL_MASK_POL, 2); | 
 |  | 
 | 	if (spi->mode & SPI_CPHA) | 
 | 		tmp = SPI_CLK_SECOND_EDGE; | 
 | 	else | 
 | 		tmp = SPI_CLK_FIRST_EDGE; | 
 |  | 
 | 	SPI_WRITE_BITS(chip->fmt_ctrl, tmp, SPI_FMT_CTRL_MASK_PHA, 3); | 
 |  | 
 | 	if (zx29spi->vendor->loopback) { | 
 | 		if (spi->mode & SPI_LOOP) | 
 | 			tmp = LOOPBACK_ENABLED; | 
 | 		else | 
 | 			tmp = LOOPBACK_DISABLED; | 
 | 		SPI_WRITE_BITS(chip->com_ctrl, tmp, SPI_COM_CTRL_MASK_LBM, 0); | 
 | 	} | 
 | //	SPI_WRITE_BITS(chip->com_ctrl, SPI_ENABLED, SPI_COM_CTRL_MASK_SSPE, 1); | 
 | 	SPI_WRITE_BITS(chip->com_ctrl, chip_info->hierarchy, SPI_COM_CTRL_MASK_MS, 2); | 
 | //	SPI_WRITE_BITS(chip->com_ctrl, chip_info->slave_tx_disable, SPI_COM_CTRL_MASK_SOD, 3); | 
 |  | 
 | 	/* Save controller_state */ | 
 | 	spi_set_ctldata(spi, chip); | 
 | 	return status; | 
 |  err_config_params: | 
 | 	spi_set_ctldata(spi, NULL); | 
 | 	kfree(chip); | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static void zx29_cleanup(struct spi_device *spi) | 
 | { | 
 | 	struct chip_data *chip = spi_get_ctldata(spi); | 
 |  | 
 | 	spi_set_ctldata(spi, NULL); | 
 | 	kfree(chip); | 
 | } | 
 |  | 
 | static int zx29_spi_clock_init(struct zx29_spi *zx29spi) | 
 | { | 
 | 	int status=0; | 
 | 	struct platform_device *pdev = zx29spi->pdev; | 
 | 	/* work clock */ | 
 | 	zx29spi->spi_clk = clk_get(&pdev->dev, "work_clk");//clk_get_sys("zx297520v2_ssp.0", const char * con_id);// | 
 | 	if (IS_ERR(zx29spi->spi_clk)) { | 
 | 		status = PTR_ERR(zx29spi->spi_clk); | 
 | 		dev_err(&pdev->dev, "could not retrieve SPI work clock\n"); | 
 | 		return status; | 
 | 	} | 
 | 	 /* enable ssp clock source */ | 
 | 	clk_enable(zx29spi->spi_clk); | 
 |  | 
 | 	 /* enable spiclk at function zx297520v2_setup  */ | 
 | 	zx29spi->clkfreq = SPI_SPICLK_FREQ_104M; | 
 |  | 
 | 	/* apb clock */ | 
 | 	zx29spi->pclk = clk_get(&pdev->dev, "apb_clk"); | 
 | 	if (IS_ERR(zx29spi->pclk)) { | 
 | 		status = PTR_ERR(zx29spi->pclk); | 
 | 		dev_err(&pdev->dev, "could not retrieve SPI work clock\n"); | 
 | 		return status; | 
 | 	} | 
 | 	clk_enable(zx29spi->pclk); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | #if defined(CONFIG_DEBUG_FS) | 
 | #define dump_register(reg)		\ | 
 | {							\ | 
 | 	.name	= __stringify(reg),	\ | 
 | 	.offset	= SPI_ ##reg##_OFFSET,	\ | 
 | } | 
 |  | 
 |  | 
 | static const struct debugfs_reg32 spi_regs[] = { | 
 | 	dump_register(VER_REG), | 
 | 	dump_register(COM_CTRL), | 
 | 	dump_register(FMT_CTRL), | 
 | 	dump_register(DR), | 
 | 	dump_register(FIFO_CTRL), | 
 | 	dump_register(FIFO_SR), | 
 | 	dump_register(INTR_EN), | 
 | 	dump_register(INTR_SR), | 
 | 	dump_register(TIMING), | 
 | }; | 
 |  | 
 | //#define Strcat(x, fmt, ...) sprintf(x, "%s" #fmt, x, __VA_ARGS__) | 
 | #ifdef CONFIG_DEBUG_FS | 
 | static void debugfs_spi_init(struct zx29_spi *zx29spi) | 
 | { | 
 | 	struct dentry *root; | 
 | 	struct dentry *node; | 
 | 	char tmp[32]; | 
 |  | 
 | 	if(!zx29spi) | 
 | 		return; | 
 |  | 
 | 	//create root | 
 | 	sprintf(tmp,"spi%d_zx29", zx29spi->pdev->id); | 
 | 	root = debugfs_create_dir(tmp, NULL); | 
 | 	if (!root)	{ | 
 | 		dev_err(&zx29spi->pdev->dev, "debugfs_create_dir %s err\n", tmp); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	//create regs | 
 | 	zx29spi->spi_regset.regs = (struct debugfs_reg32 *)spi_regs; | 
 | 	zx29spi->spi_regset.nregs = sizeof(spi_regs)/sizeof(struct debugfs_reg32); | 
 | 	zx29spi->spi_regset.base = zx29spi->virtbase; | 
 | 	node = debugfs_create_regset32("spi_regs", S_IRUGO, root, &zx29spi->spi_regset); | 
 | 	if (!node){ | 
 | 		dev_err(&zx29spi->pdev->dev, "debugfs_create_regset32 err, base=%p, num=%d\n", zx29spi->spi_regset.base, zx29spi->spi_regset.nregs); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	//create info | 
 | 	node = debugfs_create_u32("poll_cnt", S_IRUGO, root, &zx29spi->spi_poll_cnt); | 
 | 	if (!node ){ | 
 | 		dev_err(&zx29spi->pdev->dev, "debugfs_create_u32 poll_cnt err\n"); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	node = debugfs_create_u32("dma_cnt", S_IRUGO, root, &zx29spi->spi_dma_cnt); | 
 | 	if (!node ){ | 
 | 		dev_err(&zx29spi->pdev->dev, "debugfs_create_u32 dma_cnt err\n"); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	zx29spi->spi_root = (void *)root; | 
 | 	return; | 
 | err: | 
 | 	dev_err(&zx29spi->pdev->dev, "debugfs_spi_init err\n"); | 
 | } | 
 | #endif | 
 | #endif | 
 |  | 
 | static int __devinit zx29_spi_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct device *dev = &pdev->dev; | 
 | 	struct zx29_spi_controller *platform_info = pdev->dev.platform_data; | 
 | 	struct spi_master *master; | 
 | 	struct zx29_spi *zx29spi = NULL;	/*Data for this driver */ | 
 | 	struct resource *regs = NULL; | 
 | 	struct resource *gpio = NULL; | 
 | 	struct resource *irq = NULL; | 
 | 	int status = 0, i; | 
 | 	u32 regval = 0; | 
 |  | 
 | 	if (platform_info == NULL) { | 
 | 		dev_err(&pdev->dev, "probe - no platform data supplied\n"); | 
 | 		status = -ENODEV; | 
 | 		goto err_no_pdata; | 
 | 	} | 
 |  | 
 | 	/* Allocate master with space for data */ | 
 | 	master = spi_alloc_master(dev, sizeof(struct zx29_spi)); | 
 | 	if (master == NULL) { | 
 | 		dev_err(&pdev->dev, "probe - cannot alloc SPI master\n"); | 
 | 		status = -ENOMEM; | 
 | 		goto err_no_master; | 
 | 	} | 
 |  | 
 | 	zx29spi = spi_master_get_devdata(master); | 
 | 	if(!zx29spi)	 | 
 | 		return -EINVAL; | 
 | 	 | 
 | 	snprintf(zx29spi->name, sizeof(zx29spi->name), "zx29-spi%d", pdev->id); | 
 | 	//mutex_init(&zx29spi->spi_lock); | 
 | 	g_zx29_spi[pdev->id] = zx29spi; | 
 | 	zx29spi->master = master; | 
 | 	zx29spi->master_info = platform_info; | 
 | 	zx29spi->pdev = pdev; | 
 | 	zx29spi->vendor = &vendor_arm; | 
 | 	sema_init(&zx29spi->sema_dma, 0); | 
 |  | 
 | 	dev_set_drvdata(&pdev->dev, zx29spi); | 
 | 	/* | 
 | 	 * Bus Number Which has been Assigned to this SSP controller | 
 | 	 * on this board | 
 | 	 */ | 
 | 	master->bus_num = platform_info->bus_id; | 
 | 	master->num_chipselect = platform_info->num_chipselect; | 
 | 	master->cleanup = zx29_cleanup; | 
 | 	master->setup = zx29_setup; | 
 | 	master->prepare_transfer_hardware = zx29_prepare_transfer_hardware; | 
 | 	master->transfer_one_message = zx29_transfer_one_message; | 
 | 	master->unprepare_transfer_hardware = zx29_unprepare_transfer_hardware; | 
 | 	master->rt = platform_info->rt; | 
 |  | 
 | 	/* | 
 | 	 * Supports mode 0-3, loopback, and active low CS.  | 
 | 	 */ | 
 | 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_NO_CS; | 
 |  | 
 | 	dev_dbg(&pdev->dev, "BUSNO: %d\n", master->bus_num); | 
 |  | 
 | 	 /* registers */ | 
 | 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
 | 	if ( regs == NULL ){ | 
 | 		dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); | 
 | 		status = -ENOENT; | 
 | 		goto err_no_registers; | 
 | 	} | 
 | 	if (master->bus_num == 0){ | 
 | 	    zx29spi->phybase = ZX29_SSP0_PHYS; | 
 | 	} | 
 | 	else if (master->bus_num == 1){ | 
 | 	    zx29spi->phybase = ZX29_SSP1_PHYS; | 
 | 	} | 
 | 	zx29spi->virtbase = (void *)regs->start; | 
 |  | 
 | 	if (zx29spi->virtbase == NULL) { | 
 | 		status = -ENOMEM; | 
 | 		goto err_no_ioremap; | 
 | 	} | 
 | 	dev_dbg( &pdev->dev," mapped registers from 0x%08x to 0x%p\n", | 
 | 	        regs->start, zx29spi->virtbase); | 
 | #ifdef CONFIG_DEBUG_FS | 
 | 	debugfs_spi_init(zx29spi);//yuwei | 
 | #endif | 
 |  | 
 | 	if(0 == pdev->id) { | 
 | 		ramdump_ram_conf_table_add("ssp0_reg_0x140A010.bin", zx29spi->phybase + 0x10, 0x3C, (unsigned long)zx29spi->virtbase + 0x10, 0, 0); | 
 | 		/* gpios txd rxd sclk cs */ | 
 | 		for(i = 0; i < ARRAY_SIZE(spi0_gpio_resources); i++){ | 
 | 			//gpio =  platform_get_resource(pdev, IORESOURCE_IO, i); | 
 | 			gpio = &spi0_gpio_resources[i]; | 
 | 			/* | 
 | 			if( gpio == NULL ) | 
 | 			{ | 
 | 				dev_err(&pdev->dev, "Cannot get IORESOURCE_IO\n"); | 
 | 				status = -ENOENT; | 
 | 				goto err_gpios; | 
 | 			} | 
 | 			*/ | 
 | 			dev_dbg(&pdev->dev, "used gpio num  %d as %s \n", gpio->start, gpio ->name); | 
 |  | 
 | 			status = gpio_request(gpio->start,gpio->name); | 
 | 			if( status < 0 ) | 
 | 				goto err_gpios; | 
 | 			//zte_gpio_config(gpio->start, SET_FUNCTION); | 
 | 			//zx29_gpio_function_sel(gpio->start,1); | 
 | 			zx29_gpio_function_sel(gpio->start, gpio->end); | 
 |  | 
 | 		} | 
 | 		gpio_direction_output(GPIO_AP_SPI0_CS, 1); | 
 | 		//spi_set_gpio_function(pdev->id); | 
 | 	} else if(1 == pdev->id) { | 
 | #ifdef CONFIG_ARCH_ZX297520V3 | 
 | 		ramdump_ram_conf_table_add("ssp0_reg_0x1410010.bin", zx29spi->phybase + 0x10, 0x3C, (unsigned long)zx29spi->virtbase + 0x10, 0, 0); | 
 | 		/* gpios txd rxd sclk cs */ | 
 | 		for(i = 0; i < ARRAY_SIZE(spi1_gpio_resources); i++){ | 
 | 			//gpio =  platform_get_resource(pdev, IORESOURCE_IO, i); | 
 | 			gpio = &spi1_gpio_resources[i]; | 
 | 			/* | 
 | 			if( gpio == NULL ) | 
 | 			{ | 
 | 				dev_err(&pdev->dev, "Cannot get IORESOURCE_IO\n"); | 
 | 				status = -ENOENT; | 
 | 				goto err_gpios; | 
 | 			} | 
 | 			*/ | 
 | 			dev_dbg(&pdev->dev, "used gpio num  %d as %s \n", gpio->start, gpio ->name); | 
 |  | 
 | 			status = gpio_request(gpio->start,gpio->name); | 
 | 			if( status < 0 ) | 
 | 				goto err_gpios; | 
 | 			//zte_gpio_config(gpio->start, SET_FUNCTION); | 
 | 			zx29_gpio_function_sel(gpio->start, gpio->end); | 
 | 		} | 
 | 		gpio_direction_output(GPIO_AP_SPI1_CS, 1); | 
 | #else | 
 | 		printk(KERN_WARNING "spi1 unsupported yet.\n"); | 
 | #endif | 
 | 	} | 
 |  | 
 | 	/*clock init*/ | 
 | 	status = zx29_spi_clock_init(zx29spi); | 
 | 	if(status) | 
 | 		goto err_no_clk; | 
 |  | 
 | 	/* Initialize transfer pump */ | 
 | 	//tasklet_init(&zx29spi->pump_transfers, pump_transfers,(unsigned long)zx29spi); | 
 |  | 
 | 	/* Disable SPI */ | 
 | 	regval = readl((SPI_COM_CTRL_OFFSET+zx29spi->virtbase)) & (~SPI_COM_CTRL_MASK_SSPE); | 
 |  | 
 | 	writel(regval, (SPI_COM_CTRL_OFFSET+zx29spi->virtbase)); | 
 |  | 
 | 	load_spi_default_config(zx29spi); | 
 | 	writel(0, (SPI_TIMING_OFFSET + zx29spi->virtbase)); | 
 |  | 
 | 	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 
 | 	if (irq == NULL) { | 
 | 		dev_err(&pdev->dev, "Cannot get IORESOURCE_IRQ\n"); | 
 | 		status = -ENOENT; | 
 | 		goto err_no_irq; | 
 | 	} | 
 | 	zx29spi->irq = irq->start; | 
 |  | 
 | 	dev_dbg(&pdev->dev, "used interrupt num is %d\n",  zx29spi->irq); | 
 |  | 
 | 	status = devm_request_irq(&pdev->dev, zx29spi->irq, zx29_spi_irq, | 
 | 				IRQF_TRIGGER_HIGH | IRQF_NO_THREAD | IRQF_ONESHOT, dev_name(&pdev->dev), zx29spi); | 
 | 	if (status < 0) { | 
 | 		dev_err(&pdev->dev, "probe - cannot get IRQ (%d)\n", status); | 
 | 		goto err_no_irq; | 
 | 	} | 
 | 	disable_irq_nosync(zx29spi->irq); | 
 |  | 
 | 	/* Get DMA channels */ | 
 | 	if (platform_info->enable_dma) { | 
 | 		status = zx29_dma_probe(zx29spi); | 
 | 		if (status != 0) | 
 | 			platform_info->enable_dma = 0; | 
 | 	} | 
 |  | 
 | #if SPI_PSM_CONTROL | 
 |     wake_lock_init(&zx29spi->psm_lock, WAKE_LOCK_SUSPEND, zx29spi->name); | 
 | #endif | 
 |  | 
 | 	status = spi_register_master(master); | 
 | 	if (status != 0) { | 
 | 		dev_err(&pdev->dev,	"probe - problem registering spi master\n"); | 
 | 		goto err_spi_register; | 
 | 	} | 
 | 	dev_dbg(&pdev->dev," probe succeeded\n"); | 
 |  | 
 | 	/* let runtime pm put suspend */ | 
 | 	if (platform_info->autosuspend_delay > 0) { | 
 | 		dev_info(&pdev->dev, "will use autosuspend for runtime pm, delay %dms\n", platform_info->autosuspend_delay); | 
 | 		pm_runtime_set_autosuspend_delay(dev, platform_info->autosuspend_delay); | 
 | 		pm_runtime_use_autosuspend(dev); | 
 | 		pm_runtime_put_autosuspend(dev); | 
 | 	} else { | 
 | 		pm_runtime_put(dev); | 
 | 	} | 
 | 	return 0; | 
 |  | 
 |  err_spi_register: | 
 | #if SPI_PSM_CONTROL | 
 |     wake_lock_destroy(&zx29spi->psm_lock); | 
 | #endif | 
 | 	if (platform_info->enable_dma) | 
 | 		zx29_dma_remove(zx29spi); | 
 |  | 
 |  err_no_irq: | 
 | 	clk_disable(zx29spi->spi_clk); | 
 | // err_no_clk_en: | 
 |  //err_clk_prep: | 
 | 	clk_put(zx29spi->spi_clk); | 
 |  err_no_clk: | 
 | //	iounmap(zx29spi->virtbase); | 
 |  err_gpios: | 
 |     /* add */ | 
 |  err_no_ioremap: | 
 |  err_no_registers: | 
 | 	spi_master_put(master); | 
 |  err_no_master: | 
 |  err_no_pdata: | 
 | 	return status; | 
 | } | 
 |  | 
 | static int __exit zx29_spi_remove(struct platform_device *pdev) | 
 | { | 
 | 	struct zx29_spi *zx29spi = dev_get_drvdata(&pdev->dev); | 
 | 	struct  resource *              gpio = NULL; | 
 | 	//struct  resource *              irq = NULL; | 
 | 	int i; | 
 |  | 
 | 	if (!zx29spi) | 
 | 		return 0; | 
 |  | 
 | 	/* | 
 | 	 * undo pm_runtime_put() in probe.  I assume that we're not | 
 | 	 * accessing the primecell here. | 
 | 	 */ | 
 | 	pm_runtime_get_noresume(&pdev->dev); | 
 |  | 
 | 	spi_unregister_master(zx29spi->master); | 
 |  | 
 | 	load_spi_default_config(zx29spi); | 
 | 	if (zx29spi->master_info->enable_dma) | 
 | 		zx29_dma_remove(zx29spi); | 
 | /* | 
 | 	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 
 | 	if( irq != NULL ) | 
 | 	{ | 
 | 		free_irq(irq->start, zx29spi); | 
 | 	} | 
 | */ | 
 | 	devm_free_irq(&pdev->dev, zx29spi->irq, zx29spi); | 
 |  | 
 | 	clk_disable(zx29spi->spi_clk); | 
 | 	clk_put(zx29spi->spi_clk); | 
 |  | 
 | 	clk_disable(zx29spi->pclk); | 
 | 	clk_put(zx29spi->pclk); | 
 |  | 
 | 	#if defined(CONFIG_DEBUG_FS) | 
 | 	if(zx29spi->spi_root){ | 
 | 		printk(KERN_INFO "spi:debugfs_remove_recursive \n"); | 
 | 		debugfs_remove_recursive(zx29spi->spi_root); | 
 | 	} | 
 | 	#endif | 
 |  | 
 | 	#if 0 | 
 | 	if(0 == pdev->id) { | 
 | 	    /* gpios txd rxd sclk sfr */ | 
 | 		for(i = 0; i < ARRAY_SIZE(spi0_gpio_resources); i++){ | 
 | 			//gpio =  platform_get_resource(pdev, IORESOURCE_IO, i); | 
 | 			gpio = &spi0_gpio_resources[i]; | 
 |  | 
 | 			if( gpio != NULL ) | 
 | 			{ | 
 | 				gpio_free(gpio->start); | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	else if(1 == pdev->id){ | 
 | 		 /* gpios txd rxd sclk sfr */ | 
 | 		for(i = 0; i < ARRAY_SIZE(spi1_gpio_resources); i++){ | 
 | 			//gpio =  platform_get_resource(pdev, IORESOURCE_IO, i); | 
 | 			gpio = &spi1_gpio_resources[i]; | 
 |  | 
 | 			if( gpio != NULL ) | 
 | 			{ | 
 | 				gpio_free(gpio->start); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	#endif | 
 |  | 
 | //	iounmap(zx29spi->virtbase); | 
 | 	//amba_release_regions(adev); | 
 | 	//tasklet_disable(&zx29spi->pump_transfers); | 
 |  | 
 | 	spi_master_put(zx29spi->master); | 
 | 	//amba_set_drvdata(adev, NULL); | 
 | 	dev_set_drvdata(&pdev->dev, NULL); | 
 |  | 
 | #if SPI_PSM_CONTROL | 
 |     wake_lock_destroy(&zx29spi->psm_lock); | 
 | #endif | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct platform_driver zx29_spi_driver = { | 
 | 	.driver = { | 
 | 		//.name		= "zx297520_ssp", | 
 | 		.name		= "zx29_ssp", | 
 | 		.owner		= THIS_MODULE, | 
 | 	}, | 
 | 	.probe      = zx29_spi_probe, | 
 | 	.remove		= __exit_p(zx29_spi_remove), | 
 | }; | 
 |  | 
 | static int __init zx29_spi_init(void) | 
 | { | 
 | 	return platform_driver_register(&zx29_spi_driver); | 
 | } | 
 |  | 
 | static void __exit zx29_spi_exit(void) | 
 | { | 
 | 	platform_driver_unregister(&zx29_spi_driver); | 
 | } | 
 |  | 
 | module_init(zx29_spi_init); | 
 | module_exit(zx29_spi_exit); | 
 |  | 
 | MODULE_DESCRIPTION("zx29 spi controller driver"); | 
 | MODULE_AUTHOR("Sanechips"); | 
 | MODULE_LICENSE("GPL"); | 
 |  |