summaryrefslogtreecommitdiff
path: root/drivers/video/sprdfb/spi
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/video/sprdfb/spi
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/video/sprdfb/spi')
-rw-r--r--drivers/video/sprdfb/spi/Makefile7
-rw-r--r--drivers/video/sprdfb/spi/spi_simple_drv.c358
-rw-r--r--drivers/video/sprdfb/spi/spi_simple_drv.h178
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*/