diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/video/sprdfb/spi | |
Diffstat (limited to 'drivers/video/sprdfb/spi')
| -rw-r--r-- | drivers/video/sprdfb/spi/Makefile | 7 | ||||
| -rw-r--r-- | drivers/video/sprdfb/spi/spi_simple_drv.c | 358 | ||||
| -rw-r--r-- | drivers/video/sprdfb/spi/spi_simple_drv.h | 178 |
3 files changed, 543 insertions, 0 deletions
diff --git a/drivers/video/sprdfb/spi/Makefile b/drivers/video/sprdfb/spi/Makefile new file mode 100644 index 00000000..835dd11c --- /dev/null +++ b/drivers/video/sprdfb/spi/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_FB_SC8825) := spi_simple_drv.o +obj-$(CONFIG_FB_SCX35) := spi_simple_drv.o +obj-$(CONFIG_FB_SCX30G) := spi_simple_drv.o +obj-$(CONFIG_FB_SCX15) := spi_simple_drv.o +ifeq ($(CONFIG_FB_SCX35L), y) +obj-$(CONFIG_SPI) := spi_simple_drv.o +endif diff --git a/drivers/video/sprdfb/spi/spi_simple_drv.c b/drivers/video/sprdfb/spi/spi_simple_drv.c new file mode 100644 index 00000000..51c23912 --- /dev/null +++ b/drivers/video/sprdfb/spi/spi_simple_drv.c @@ -0,0 +1,358 @@ +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/clk.h> +#ifdef CONFIG_OF +#include <linux/fb.h> +#endif + +#include <soc/sprd/hardware.h> +#include <soc/sprd/sci.h> +#include <soc/sprd/sci_glb_regs.h> + +#include "spi_simple_drv.h" + +#ifdef CONFIG_OF +#define FB_SPI_CLOCK ("fb_spi_clock") +#define FB_SPI_CLOCK_PARENT ("fb_spi_clock_parent") +#endif + +static u32 used_spi_reg_base; + +#ifdef CONFIG_OF +static void SPI_Enable(struct device *device,bool is_en) +#else +static void SPI_Enable( uint32_t spi_id, bool is_en) +#endif +{ + struct clk *spi_clk; + +#ifndef CONFIG_OF + switch (spi_id) { + case 0: + spi_clk = clk_get(NULL, "clk_spi0"); + break; + case 1: + spi_clk = clk_get(NULL, "clk_spi1"); + break; + case 2: + spi_clk = clk_get(NULL, "clk_spi2"); + break; + default: + BUG_ON(1); + } +#else + spi_clk = of_clk_get_by_name(device->of_node, FB_SPI_CLOCK); + if (IS_ERR(spi_clk)) { + printk(KERN_WARNING "sprdfb: get spi clock fail!\n"); + return ; + } else { + pr_debug(KERN_INFO "sprdfb: get spi clock ok!\n"); + } +#endif + if(is_en) + { + clk_prepare_enable(spi_clk); + } + else + { + clk_disable_unprepare(spi_clk); + } +} + +static void SPI_Reset( uint32_t spi_id) +{ + u32 rst_reg, rst_bit; + + switch (spi_id) { + case 0: + rst_reg = REG_AP_APB_APB_RST; + rst_bit = BIT_SPI0_SOFT_RST; + break; + case 1: + rst_reg = REG_AP_APB_APB_RST; + rst_bit = BIT_SPI1_SOFT_RST; + break; + case 2: + rst_reg = REG_AP_APB_APB_RST; + rst_bit = BIT_SPI2_SOFT_RST; + break; + default: + BUG_ON(1); + } + + sci_glb_set(rst_reg, rst_bit); + msleep(50); + sci_glb_clr(rst_reg, rst_bit); +} + +/*just set the spi src clk = 26Mhz*/ +#ifdef CONFIG_OF +static void SPI_ClkSetting(struct device *device) +#else +static void SPI_ClkSetting(uint32_t spi_id) +#endif +{ + struct clk *spi_clk, *spi_src_clk; +#ifndef CONFIG_OF + switch (spi_id) { + case 0: + spi_clk = clk_get(NULL, "clk_spi0"); + break; + case 1: + spi_clk = clk_get(NULL, "clk_spi1"); + break; + case 2: + spi_clk = clk_get(NULL, "clk_spi2"); + break; + default: + BUG_ON(1); + } + spi_src_clk = clk_get(NULL, "ext_26m"); +#else + spi_clk = of_clk_get_by_name(device->of_node, FB_SPI_CLOCK); + if (IS_ERR(spi_clk)) { + printk(KERN_WARNING "sprdfb: get spi clock fail!\n"); + return ; + } else { + pr_debug(KERN_INFO "sprdfb: get spi clock ok!\n"); + } + spi_src_clk = of_clk_get_by_name(device->of_node, FB_SPI_CLOCK_PARENT); + if (IS_ERR(spi_src_clk)) { + printk(KERN_WARNING "sprdfb: get spi clock parent fail!\n"); + return ; + } else { + pr_debug(KERN_INFO "sprdfb: get spi clock parent ok!\n"); + } +#endif + clk_set_parent(spi_clk, spi_src_clk); + clk_set_rate(spi_clk, SPI_DEF_SRC_CLK); +} + +void SPI_SetCsLow( uint32_t spi_sel_csx , bool is_low) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T*)(used_spi_reg_base); + + if(is_low) { + spi_ctr_ptr->ctl0 &= ~((1<<spi_sel_csx)<<SPI_SEL_CS_SHIFT); + } + else + { + spi_ctr_ptr->ctl0 |= ((1<<spi_sel_csx)<<SPI_SEL_CS_SHIFT); + } +} + +void SPI_SetCd( uint32_t cd) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T*)(used_spi_reg_base); + + if(cd == 0) { + spi_ctr_ptr->ctl8 &= ~(SPI_CD_MASK); + } else { + spi_ctr_ptr->ctl8 |= (SPI_CD_MASK); + } +} + +#if 0 +static void SPI_SetSpiMode(uint32_t spi_mode) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + uint32_t temp = spi_ctr_ptr->ctl7; + + temp &= ~SPI_MODE_MASK; + temp |= (spi_mode<<SPI_MODE_SHIFT); + + spi_ctr_ptr->ctl7 = temp; +} +#endif + +void SPI_SetDatawidth(uint32_t datawidth) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + uint32_t temp = spi_ctr_ptr->ctl0; + + if( 32 == datawidth ) + { + spi_ctr_ptr->ctl0 &= ~0x7C; + return; + } + + temp &= ~0x0000007C; + temp |= (datawidth<<2); + + spi_ctr_ptr->ctl0 = temp; +} + +static void SPI_SetTxLen(uint32_t data_len, uint32_t dummy_bitlen) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + uint32_t ctl8 = spi_ctr_ptr->ctl8; + uint32_t ctl9 = spi_ctr_ptr->ctl9; + + data_len &= TX_MAX_LEN_MASK; + + ctl8 &= ~((TX_DUMY_LEN_MASK<<4) | TX_DATA_LEN_H_MASK); + ctl9 &= ~( TX_DATA_LEN_L_MASK ); + + spi_ctr_ptr->ctl8 = (ctl8 | (dummy_bitlen<<4) | (data_len>>16)); + spi_ctr_ptr->ctl9 = (ctl9 | (data_len&0xFFFF)); +} + +static void SPI_SetRxLen(uint32_t data_len, uint32_t dummy_bitlen) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + uint32_t ctl10 = spi_ctr_ptr->ctl10; + uint32_t ctl11 = spi_ctr_ptr->ctl11; + + data_len &= RX_MAX_LEN_MASK; + + ctl10 &= ~((RX_DUMY_LEN_MASK<<4) | RX_DATA_LEN_H_MASK); + ctl11 &= ~( RX_DATA_LEN_L_MASK ); + + spi_ctr_ptr->ctl10 = (ctl10 | (dummy_bitlen<<4) | (data_len>>16)); + spi_ctr_ptr->ctl11 = (ctl11 | (data_len&0xFFFF)); +} + +static void SPI_TxReq( void ) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + + spi_ctr_ptr->ctl12 |= SW_TX_REQ_MASK; +} + +static void SPI_RxReq( void ) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + + spi_ctr_ptr->ctl12 |= SW_RX_REQ_MASK; +} + +static unsigned long SPRD_SPI0_BASE = 0xffffffff; +static unsigned long SPRD_SPI1_BASE = 0xffffffff; +static unsigned long SPRD_SPI2_BASE = 0xffffffff; + +#ifdef CONFIG_OF +void SPI_Init(struct device *device, u32 spi_id, SPI_INIT_PARM *spi_parm) +#else +void SPI_Init(u32 spi_id, SPI_INIT_PARM *spi_parm) +#endif +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr; + u32 temp; + +#ifdef CONFIG_OF + SPI_ClkSetting(device); + SPI_Enable(device, true); +#else + SPI_ClkSetting(spi_id); + SPI_Enable(spi_id, true); +#endif + + SPI_Reset(spi_id); + + switch (spi_id) { + case 0: + used_spi_reg_base = SPRD_SPI0_BASE; + break; + case 1: + used_spi_reg_base = SPRD_SPI1_BASE; + break; + case 2: + used_spi_reg_base = SPRD_SPI2_BASE; + break; + default: + BUG_ON(1); + } + + spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + + /*fixme, the spi_clk is 1Mhz*/ + spi_ctr_ptr->clkd = (SPI_DEF_SRC_CLK / (SPI_DEF_CLK << 1)) - 1; + + temp = 0; + temp |= (spi_parm->tx_edge << 1) | + (spi_parm->rx_edge << 0) | + (0x1 << 13) | + (spi_parm->msb_lsb_sel<< 7) ; + spi_ctr_ptr->ctl0 = temp; + + spi_ctr_ptr->ctl1 |= BIT(12) | BIT(13); + + /*rx fifo full watermark is 16*/ + spi_ctr_ptr->ctl3 = 0x10; + + /*set SPIMODE_3WIRE_9BIT_SDIO mode*/ + spi_ctr_ptr->ctl7 &= ~(0x7 << 3); + + spi_ctr_ptr->ctl7 |= SPIMODE_3WIRE_9BIT_SDIO << 3; +} + +static void SPI_WaitTxFinish(void) +{ + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + + while( !((spi_ctr_ptr->iraw)&BIT(8)) ) // IS tx finish + { + } + spi_ctr_ptr->iclr |= BIT(8); + + // Wait for spi bus idle + while((spi_ctr_ptr->sts2)&BIT(8)) + { + } + // Wait for tx real empty + while( !((spi_ctr_ptr->sts2)&BIT(7)) ) + { + } +} + +void SPI_WriteData(uint32_t data, uint32_t data_len, uint32_t dummy_bitlen) +{ + // uint32_t command; + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + + // The unit of data_len is identical with buswidth + SPI_SetTxLen(data_len, dummy_bitlen); + SPI_TxReq( ); + + spi_ctr_ptr->data = data; + + SPI_WaitTxFinish(); +} + +uint32_t SPI_ReadData( uint32_t data_len, uint32_t dummy_bitlen ) +{ + uint32_t read_data=0, rxt_cnt=0; + volatile SPI_CTL_REG_T *spi_ctr_ptr = (volatile SPI_CTL_REG_T *)(used_spi_reg_base); + + // The unit of data_len is identical with buswidth + SPI_SetRxLen(data_len, dummy_bitlen); + SPI_RxReq( ); + + //Wait for spi receive finish + while( !((spi_ctr_ptr->iraw)&BIT(9)) ) + { + //wait rxt fifo full + if((spi_ctr_ptr->iraw)&BIT(6)) + { + rxt_cnt = (spi_ctr_ptr->ctl3)&0x1F; + printk("---FIFOFULL:rxt_cnt=0x%x", rxt_cnt); + while(rxt_cnt--) + { + read_data = spi_ctr_ptr->data; + printk("---FIFOFULL: SPI_ReadData =0x%x", read_data); + } + } + } + + while((spi_ctr_ptr->sts2)&BIT(8)) + { + } + + while(data_len--) + { + read_data = spi_ctr_ptr->data; + printk("---Finish: SPI_ReadData =0x%x", read_data); + } + + return (read_data); +} diff --git a/drivers/video/sprdfb/spi/spi_simple_drv.h b/drivers/video/sprdfb/spi/spi_simple_drv.h new file mode 100644 index 00000000..07019387 --- /dev/null +++ b/drivers/video/sprdfb/spi/spi_simple_drv.h @@ -0,0 +1,178 @@ +#ifndef _SPI_TEST_H +#define _SPI_TEST_H + +#include <linux/kernel.h> + +#define SPI0_ID 0 +#define SPI1_ID 1 +#define SPI2_ID 2 +#define SPI_USED_ID SPI2_ID + +#define CHN_SPI_INT 9 +#define DMA_SPI_TX 0x13 + +#define SPI_TX_FIFO_DEPTH 16 +#define SPI_RX_FIFO_DEPTH 16 +#define SPI_INT_DIS_ALL 0 +#define SPI_RX_FULL_INT_EN BIT_6 +#define SPI_RX_FULL_INT_STS BIT_6 +#define SPI_RX_FULL_INT_CLR BIT_0 +#define SPI_RX_FIFO_REAL_EMPTY BIT_5 +#define SPI_S8_MODE_EN BIT_7 +#define SPI_CD_SEL 0x2 //cs1 is sel as cd signal + +/* + Define the SPI interface mode for LCM + */ +#define SPIMODE_DISABLE 0 +#define SPIMODE_3WIRE_9BIT_SDA 1 // 3 wire 9 bit, cd bit, SDI/SDO share one IO +#define SPIMODE_3WIRE_9BIT_SDIO 2 // 3 wire 9 bit, cd bit, SDI, SDO +#define SPIMODE_4WIRE_8BIT_SDA 3 // 4 wire 8 bit, cd pin, SDI/SDO share one IO +#define SPIMODE_4WIRE_8BIT_SDIO 4 // 4 wire 8 bit, cd pin, SDI, SDO + +/* + Define the clk src for SPI mode + */ +#define SPICLK_SEL_192M 0 +#define SPICLK_SEL_154M 1 +#define SPICLK_SEL_96M 2 +#define SPICLK_SEL_26M 3 + +/* + SPI CS sel in master mode + */ +#define SPI_SEL_CS0 0x0E //2'B1110 +#define SPI_SEL_CS1 0x0D //2'B1101 +#define SPI_SEL_CS2 0x0B //2'B1011 +#define SPI_SEL_CS3 0x07 //2'B0111 + +#define TX_MAX_LEN_MASK 0xFFFFF +#define TX_DUMY_LEN_MASK 0x3F //[09:04] +#define TX_DATA_LEN_H_MASK 0x0F //[03:00] +#define TX_DATA_LEN_L_MASK 0xFFFF //[15:00] +#define SPI_MODE_SHIFT 3 +#define SPI_MODE_MASK (0x07<<SPI_MODE_SHIFT) +#define SPI_CD_MASK BIT(15) +#define SPI_SEL_CS_SHIFT 8 + +#define SPI_DEF_SRC_CLK (26 * 1000 *1000) +#define SPI_DEF_CLK (13 * 1000 * 1000) + +typedef enum +{ + TX_POS_EDGE = 0, + TX_NEG_EDGE +}TX_EDGE; + +typedef enum +{ + RX_POS_EDGE = 0, + RX_NEG_EDGE +}RX_EDGE; + +typedef enum +{ + TX_RX_MSB = 0, + TX_RX_LSB +}MSB_LSB_SEL; + +typedef enum +{ + IDLE_MODE = 0, + RX_MODE, + TX_MODE, + RX_TX_MODE +}TRANCIEVE_MODE; + +typedef enum +{ + NO_SWITCH = 0, + BYTE_SWITCH = 1, + HWORD_SWITCH = 2 +}SWT_MODE; + +typedef enum +{ + MASTER_MODE = 0, + SLAVE_MODE = 1 +}SPI_OPERATE_MODE_E; + +typedef enum +{ + DMA_DISABLE = 0, + DMA_ENABLE +}DMA_EN; + +typedef enum +{ + CS_LOW = 0, + CS_HIGH +}CS_SIGNAL; + +#define SW_RX_REQ_MASK BIT(0) +#define SW_TX_REQ_MASK BIT(1) +#define RX_MAX_LEN_MASK 0xFFFFFF +#define RX_DUMY_LEN_MASK 0x3F +#define RX_DATA_LEN_H_MASK 0x0F +#define RX_DATA_LEN_L_MASK 0xFFFF + +typedef struct _init_param +{ + TX_EDGE tx_edge; + RX_EDGE rx_edge; + MSB_LSB_SEL msb_lsb_sel; + TRANCIEVE_MODE tx_rx_mode; + SWT_MODE switch_mode; + SPI_OPERATE_MODE_E op_mode; + uint32_t DMAsrcSize; + uint32_t DMAdesSize; + uint32_t clk_div; + uint8_t data_width; + uint8_t tx_empty_watermark; + uint8_t tx_full_watermark; + uint8_t rx_empty_watermark; + uint8_t rx_full_watermark; +}SPI_INIT_PARM,*SPI_INIT_PARM_P; + +typedef struct +{ + uint32_t data; // Transmit word or Receive word + uint32_t clkd; // clock dividor register + uint32_t ctl0; // control register + uint32_t ctl1; // Receive Data full threshold/Receive Data full threshold + uint32_t ctl2; // 2-wire mode reigster + uint32_t ctl3; // transmit data interval + uint32_t ctl4; // transmit data interval + uint32_t ctl5; // transmit data interval + uint32_t ien; // interrutp enable register + uint32_t iclr; // interrupt clear register + uint32_t iraw; // interrupt clear register + uint32_t ists; // interrupt clear register + uint32_t sts1; // fifo cnt register, bit[5:0] for RX and [13:8] for TX + uint32_t sts2; // masked interrupt status register + uint32_t dsp_wait; // Used for DSP control + uint32_t sts3; // tx_empty_threshold and tx_full_threshold + uint32_t ctl6; + uint32_t sts4; + uint32_t fifo_rst; + uint32_t ctl7; // SPI_RX_HLD_EN : SPI_TX_HLD_EN : SPI_MODE + uint32_t sts5; // CSN_IN_ERR_SYNC2 + uint32_t ctl8; // SPI_CD_BIT : SPI_TX_DUMY_LEN : SPI_TX_DATA_LEN_H + uint32_t ctl9; // SPI_TX_DATA_LEN_L + uint32_t ctl10; // SPI_RX_DATA_LEN_H : SPI_RX_DUMY_LEN + uint32_t ctl11; // SPI_RX_DATA_LEN_L + uint32_t ctl12; // SW_TX_REQ : SW_RX_REQ +} SPI_CTL_REG_T; + +void SPI_SetCsLow( uint32_t spi_sel_csx , bool is_low); +void SPI_SetCd( uint32_t cd); +void SPI_SetDatawidth(uint32_t datawidth); +#ifdef CONFIG_OF +void SPI_Init(struct device *device, u32 spi_id, SPI_INIT_PARM *spi_parm); +#else +void SPI_Init(u32 spi_id, SPI_INIT_PARM *spi_parm); +#endif +void SPI_WriteData(uint32_t data, uint32_t data_len, uint32_t dummy_bitlen); +uint32_t SPI_ReadData( uint32_t data_len, uint32_t dummy_bitlen ); + +#endif /*_SPI_TEST_H*/ |
