📄 spi.c
字号:
#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/sched.h>#include <linux/mm.h>#include <asm/page.h>#include <linux/poll.h>#include <linux/kdev_t.h>#include <asm/semaphore.h>//#include <asm/arch/pxa-regs.h>#include <asm-arm/arch-pxa/pxa-regs.h>#include <linux/slab.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <linux/i2c.h>//#include <asm/arch/gpio.h>//#include <asm/arch/ssp.h>//#include <asm/arch/mfp.h>#include <asm/arch/hardware.h>#include <asm/dma.h>#include <linux/dma-mapping.h>#include "IF101_Operation.h"#include "IF101_Communication.h"#include <linux/interrupt.h>#include <linux/sem.h>/************************************************************************** * * Declaration for SPI part *****************************************************************************/#define SPI_MODE_0 0 //idle low, transmit falling edge, receive rising edge.#define SPI_MODE_1 1 //idle high, transmit rising edge, receive falling edge#define SPI_MODE_2 2 //idle low, transmit rising edge, receive falling edge#define SPI_MODE_3 3 //idle high, transmit falling edge, receive rising edge.#define SSP_PORT 1#define SPI_CLK_PIN 23//MFP_PIN_GPIO85#define SPI_CS_PIN 24//MFP_PIN_GPIO86#define SPI_SDOUT_PIN 25//MFP_PIN_GPIO87#define SPI_SDIN_PIN 26//MFP_PIN_GPIO88#define DMA_BUFFER_SIZE PAGE_SIZE#if(SSP_PORT==1)#define DRCMR_SPI_RX DRCMR13#define DRCMR_SPI_TX DRCMR14#elif(SSP_PORT==2)#define DRCMR_SPI_RX DRCMR15#define DRCMR_SPI_TX DRCMR16#elif(SSP_PORT==3)#define DRCMR_SPI_RX DRCMR66#define DRCMR_SPI_TX DRCMR67#elif(SSP_PORT==4)#define DRCMR_SPI_RX DRCMR2#define DRCMR_SPI_TX DRCMR3#endif#define PXA_SSP_PORTS 1#if 0static struct mhn_pin_config ssp1_pins[] = { MHN_MFP_CFG("CLK", SPI_CLK_PIN, MFP_AF1, MFP_DS03X, 0, MFP_LPM_PULL_HIGH, MFP_EDGE_NONE),// MHN_MFP_CFG("CS", SPI_CS_PIN, MFP_AF1, MFP_DS03X, 0, MFP_LPM_PULL_HIGH, MFP_EDGE_NONE), MHN_MFP_CFG("CS", SPI_CS_PIN, MFP_AF0, MFP_DS03X, 0, MFP_LPM_PULL_HIGH, MFP_EDGE_NONE), // manual control cs MHN_MFP_CFG("SOUT",SPI_SDOUT_PIN, MFP_AF1, MFP_DS03X, 0, MFP_LPM_PULL_HIGH, MFP_EDGE_NONE), MHN_MFP_CFG("SIN", SPI_SDIN_PIN, MFP_AF1, MFP_DS03X, 0, MFP_LPM_PULL_HIGH, MFP_EDGE_NONE),};#endifstruct inno_spi_dma_info{ int txdma; int rxdma; void *txdma_addr; void *rxdma_addr; dma_addr_t txdma_addr_phys; dma_addr_t rxdma_addr_phys;};struct ssp_dev { u32 port; u32 mode; u32 flags; u32 psp_flags; u32 speed;};static int spi_inited = 0;static DECLARE_MUTEX(sem);static int use_count[PXA_SSP_PORTS] = {0, 0, 0};static unsigned short clk_mode = SPI_MODE_0;static struct ssp_dev inno_spi_dev;struct inno_spi_dma_info inno_dma;static DECLARE_MUTEX_LOCKED(dma_sem);static void inno_spi_pin_config(void);static void inno_spi_dma_init(void);static void inno_spi_dma_uninit(void);void inno_spi_write_byte(u8 cmd);void inno_spi_read_bytes(u8* pdata, u32 len);static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct ssp_dev *dev = (struct ssp_dev*) dev_id; unsigned int status = SSSR_P(dev->port); SSSR_P(dev->port) = status; /* clear status bits */ if (status & SSSR_ROR) printk(KERN_WARNING "SSP(%d): receiver overrun\n", dev->port); if (status & SSSR_TUR) printk(KERN_WARNING "SSP(%d): transmitter underrun\n", dev->port); if (status & SSSR_BCE) printk(KERN_WARNING "SSP(%d): bit count error\n", dev->port); return IRQ_HANDLED;}static int ssp_init(struct ssp_dev *dev, u32 port){ int ret, irq; if (port > PXA_SSP_PORTS || port == 0) return -ENODEV; down(&sem); if (use_count[port - 1]) { up(&sem); return -EBUSY; } use_count[port - 1]++; if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) { use_count[port - 1]--; up(&sem); return -EBUSY; } switch (port) { case 1: irq = IRQ_SSP; break;#if defined (CONFIG_PXA27x) case 2: irq = IRQ_SSP2; break; case 3: irq = IRQ_SSP3; break;#else case 2: irq = IRQ_NSSP; break; case 3: irq = IRQ_ASSP; break;#endif default: return -ENODEV; } dev->port = port; ret = request_irq(irq, ssp_interrupt, 0, "SSP", dev); if (ret) goto out_region; /* turn on SSP port clock */ switch (dev->port) {#if defined (CONFIG_PXA27x) case 1: pxa_set_cken(CKEN23_SSP1, 1); break; case 2: pxa_set_cken(CKEN3_SSP2, 1); break; case 3: pxa_set_cken(CKEN4_SSP3, 1); break;#else case 1: pxa_set_cken(CKEN3_SSP, 1); break; case 2: pxa_set_cken(CKEN9_NSSP, 1); break; case 3: pxa_set_cken(CKEN10_ASSP, 1); break;#endif } up(&sem); return 0;out_region: release_mem_region(__PREG(SSCR0_P(port)), 0x2c); use_count[port - 1]--; up(&sem); return ret;}int ssp_write_word(struct ssp_dev *dev, u32 data){ while (!(SSSR_P(dev->port) & SSSR_TNF)) cpu_relax(); SSDR_P(dev->port) = data; return 0;}void ssp_flush(struct ssp_dev *dev){ do { while (SSSR_P(dev->port) & SSSR_RNE) { (void) SSDR_P(dev->port); } } while (SSSR_P(dev->port) & SSSR_BSY);}int ssp_read_word(struct ssp_dev *dev){ int i = 0; for(i = 0; i< 500; i++); while (!(SSSR_P(dev->port) & SSSR_RNE)) { for(i = 0; i < 100; i++); cpu_relax(); } return SSDR_P(dev->port);}void ssp_enable(struct ssp_dev *dev){ SSCR0_P(dev->port) |= SSCR0_SSE;}void ssp_disable(struct ssp_dev *dev){ SSCR0_P(dev->port) &= ~SSCR0_SSE;}int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed){ dev->mode = mode; dev->flags = flags; dev->psp_flags = psp_flags; dev->speed = speed; /* set up port type, speed, port settings */ SSCR0_P(dev->port) = (dev->speed | dev->mode); SSCR1_P(dev->port) = dev->flags; SSPSP_P(dev->port) = dev->psp_flags; return 0;}void ssp_exit(struct ssp_dev *dev){ int irq; down(&sem); SSCR0_P(dev->port) &= ~SSCR0_SSE; /* find irq, save power and turn off SSP port clock */ switch (dev->port) {#if defined (CONFIG_PXA27x) case 1: irq = IRQ_SSP; pxa_set_cken(CKEN23_SSP1, 0); break; case 2: irq = IRQ_SSP2; pxa_set_cken(CKEN3_SSP2, 0); break; case 3: irq = IRQ_SSP3; pxa_set_cken(CKEN4_SSP3, 0); break;#else case 1: irq = IRQ_SSP; pxa_set_cken(CKEN3_SSP, 0); break; case 2: irq = IRQ_NSSP; pxa_set_cken(CKEN9_NSSP, 0); break; case 3: irq = IRQ_ASSP; pxa_set_cken(CKEN10_ASSP, 0); break;#endif default: printk(KERN_WARNING "SSP: tried to close invalid port\n"); return; } free_irq(irq, dev); release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c); use_count[dev->port - 1]--; up(&sem);}static void SPI_StartFrame(void){ GPCR(SPI_CS_PIN) |= GPIO_bit(SPI_CS_PIN);}static void SPI_EndFrame(void){ GPSR(SPI_CS_PIN) |= GPIO_bit(SPI_CS_PIN);}INNO_RETURN_CODE INNO_SPI_Init(int enable){ int ret; int spi_timming_mode; if(spi_inited) return 0; //printk("clk_mode ====== %d\n", clk_mode); switch(clk_mode){ case SPI_MODE_3: //idle high, transmit falling edge, receive rising edge. spi_timming_mode = SSCR1_SPH | SSCR1_SPO; break; case SPI_MODE_1: //idle high, transmit rising edge, receive falling edge spi_timming_mode = SSCR1_SPO; break; case SPI_MODE_2: //idle low,transmit rising edge, receive falling edge spi_timming_mode = SSCR1_SPH; break; case SPI_MODE_0: //idle low, transmit falling edge, receive rising edge. default: spi_timming_mode = 0; break; } printk("SSP_PORT == %d", SSP_PORT); if(SSP_PORT == 1) { pxa_gpio_mode ( GPIO23_SCLK_MD ); pxa_gpio_mode (SPI_CS_PIN | GPIO_OUT); pxa_gpio_mode ( GPIO25_STXD_MD ); pxa_gpio_mode ( GPIO26_SRXD_MD ); } else if(SSP_PORT == 2) { pxa_gpio_mode( SPI_SDIN_PIN | GPIO_ALT_FN_2_IN); pxa_gpio_mode( SPI_CS_PIN | GPIO_OUT); pxa_gpio_mode( SPI_CLK_PIN | GPIO_ALT_FN_1_OUT); pxa_gpio_mode( SPI_SDOUT_PIN | GPIO_ALT_FN_2_OUT); } else if(SSP_PORT == 3) { pxa_gpio_mode( SPI_SDOUT_PIN | GPIO_ALT_FN_1_OUT); pxa_gpio_mode( SPI_SDIN_PIN | GPIO_ALT_FN_1_IN); pxa_gpio_mode( SPI_CS_PIN | GPIO_OUT); pxa_gpio_mode( SPI_CLK_PIN | GPIO_ALT_FN_1_OUT); } //initial SPI_nCS level high GPSR(SPI_CS_PIN) |= GPIO_bit(SPI_CS_PIN); memzero(&inno_spi_dev, sizeof(struct ssp_dev)); ret = ssp_init(&inno_spi_dev, SSP_PORT); if(ret){ printk("Unable Init SPI %d.\r\n", SSP_PORT); }else{ printk("init SPI successfully!!!"); ssp_disable(&inno_spi_dev); ssp_config(&inno_spi_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07)), // SPI, 8bit data (spi_timming_mode), \ 0, \ (1<<8)); //6.5MHz ssp_enable(&inno_spi_dev); ssp_flush(&inno_spi_dev); } spi_inited = 1; return INNO_NO_ERROR;#if 0 int ret; int spi_timming_mode = 0; if(enable == 1){ inno_spi_pin_config(); memzero(&inno_spi_dev, sizeof(struct ssp_dev)); ret = ssp_init(&inno_spi_dev, SSP_PORT, 0); if(ret){ printk("Unable Init SPI %d.\r\n", SSP_PORT); }else{ ssp_disable(&inno_spi_dev); ssp_config(&inno_spi_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07)), // SPI, 8bit data (spi_timming_mode), \ 0, \ SSCR0_SerClkDiv(2)); //6.5MHz inno_spi_dma_init(); ssp_enable(&inno_spi_dev); ssp_flush(&inno_spi_dev); } } else{ inno_spi_dma_uninit(); ssp_exit(&inno_spi_dev); } return INNO_NO_ERROR;#endif}INNO_RETURN_CODE INNO_SPI_Write_One_Byte(unsigned char data){ inno_spi_write_byte(data); return INNO_NO_ERROR;}INNO_RETURN_CODE INNO_SPI_Read_One_Byte(unsigned char *data){ inno_spi_read_bytes(data,1); return INNO_NO_ERROR;}INNO_RETURN_CODE INNO_SPI_Read_Bytes(unsigned char *buffer, int len){ inno_spi_read_bytes(buffer,len); return INNO_NO_ERROR;}/* * inno spi dma functions */static void inno_spi_dma_tx_intr(int dma, void *devid, struct pt_regs *regs){ if (DCSR(inno_dma.txdma) & DCSR_ENDINTR){ DCSR(inno_dma.txdma) |= DCSR_ENDINTR; }}static void inno_spi_dma_rx_intr(int dma, void *devid, struct pt_regs *regs){ if (DCSR(inno_dma.rxdma) & DCSR_ENDINTR){ DCSR(inno_dma.rxdma) |= DCSR_ENDINTR; } up(&dma_sem);}static void inno_spi_dma_init(void){ SSCR1_P(SSP_PORT) |= (SSCR1_TSRE | SSCR1_RSRE); //enable DMA request SSCR1_P(SSP_PORT) |= SSCR1_TxTresh(8)|SSCR1_RxTresh(8); //set DMA request trig threshold// SSCR1_P(SSP_PORT) |= SSCR1_TRAIL; inno_dma.rxdma = pxa_request_dma("Inno Rx SPI DMA", DMA_PRIO_MEDIUM, inno_spi_dma_rx_intr, NULL); inno_dma.txdma = pxa_request_dma("Inno TX SPI DMA", DMA_PRIO_MEDIUM, inno_spi_dma_tx_intr, NULL); inno_dma.txdma_addr = NULL; inno_dma.rxdma_addr = NULL; inno_dma.txdma_addr = dma_alloc_coherent(NULL, DMA_BUFFER_SIZE, &inno_dma.txdma_addr_phys, GFP_KERNEL); inno_dma.rxdma_addr = dma_alloc_coherent(NULL, DMA_BUFFER_SIZE, &inno_dma.rxdma_addr_phys, GFP_KERNEL); if((NULL == inno_dma.txdma_addr) || (NULL == inno_dma.rxdma_addr)) printk("SPI DMA alloc memory error"); memzero(inno_dma.txdma_addr, PAGE_ALIGN(DMA_BUFFER_SIZE)); memzero(inno_dma.rxdma_addr, PAGE_ALIGN(DMA_BUFFER_SIZE)); DRCMR_SPI_TX = inno_dma.txdma | DRCMR_MAPVLD; DRCMR_SPI_RX = inno_dma.rxdma | DRCMR_MAPVLD;}static void inno_spi_dma_uninit(void){ if(inno_dma.txdma != -1){ pxa_free_dma(inno_dma.txdma); inno_dma.txdma = -1; } if(inno_dma.rxdma != -1){ pxa_free_dma(inno_dma.rxdma); inno_dma.rxdma = -1; } if(inno_dma.txdma_addr != NULL){ dma_free_coherent(NULL, DMA_BUFFER_SIZE, inno_dma.txdma_addr, inno_dma.txdma_addr_phys); inno_dma.txdma_addr = NULL; } if(inno_dma.rxdma_addr != NULL){ dma_free_coherent(NULL, DMA_BUFFER_SIZE, inno_dma.rxdma_addr, inno_dma.rxdma_addr_phys); inno_dma.rxdma_addr = NULL; }}/* * start launch DMA for transfer @size bytes */static void inno_spi_rxdma_start(u32 size){ DCSR(inno_dma.rxdma) = DCSR_NODESC; DSADR(inno_dma.rxdma) = __PREG(SSDR_P(SSP_PORT)); DTADR(inno_dma.rxdma) = inno_dma.rxdma_addr_phys; DCMD(inno_dma.rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST8 | (size & DCMD_LENGTH); DCSR(inno_dma.rxdma) |= DCSR_RUN;}static void inno_spi_txdma_start(u32 size){ DCSR(inno_dma.txdma) = DCSR_NODESC; DSADR(inno_dma.txdma) = inno_dma.txdma_addr_phys; DTADR(inno_dma.txdma) = __PREG(SSDR_P(SSP_PORT)); DCMD(inno_dma.txdma) = DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST8 | (size & DCMD_LENGTH); DCSR(inno_dma.txdma) |= DCSR_RUN;}static void inno_spi_dma_start(u32 size){ SSCR0_P(SSP_PORT) &= ~SSCR0_SSE; SSCR0_P(SSP_PORT) |= SSCR0_SSE; inno_spi_rxdma_start(size); inno_spi_txdma_start(size);}/* * configure SPI GPIO Setting */static void inno_spi_pin_config(void){#if 0 if(SSP_PORT == 1){ mhn_mfp_set_configs(ssp1_pins, ARRAY_SIZE(ssp1_pins)); mhn_gpio_set_direction(SPI_CLK_PIN, GPIO_DIR_OUT); //clk mhn_gpio_set_direction(SPI_CS_PIN, GPIO_DIR_OUT); //ncs mhn_gpio_set_direction(SPI_SDOUT_PIN, GPIO_DIR_OUT); //sdout mhn_gpio_set_direction(SPI_SDIN_PIN, GPIO_DIR_IN); //sdin mhn_gpio_set_level(SPI_CS_PIN, GPIO_LEVEL_HIGH); //init ncs high }#endif}static void inno_spi_start_frame(void){// mhn_gpio_set_level(SPI_CS_PIN, GPIO_LEVEL_LOW); GPCR(SPI_CS_PIN) |= GPIO_bit(SPI_CS_PIN);}static void inno_spi_end_frame(void){ //mhn_gpio_set_level(SPI_CS_PIN, GPIO_LEVEL_HIGH); GPSR(SPI_CS_PIN) |= GPIO_bit(SPI_CS_PIN);}/* * inno_spi_write_byte * @cmd - 8-bit length mmis command * return * @null */void inno_spi_write_byte(u8 cmd){ inno_spi_start_frame(); ssp_write_word(&inno_spi_dev, cmd);// /* Read null data back from device to prevent SSP overflow */// ssp_read_word(&inno_spi_dev); ssp_flush(&inno_spi_dev); inno_spi_end_frame();}/* * note: if101 chip need nCS keep low staus when read until datas read end. * if we use spi controller to control nCS, the nCS will pull high after read every byte, * so we should manual control nCS pin */void inno_spi_read_bytes(u8* pdata, u32 len){ u32 i; SPI_StartFrame(); for(i=0; i<len; i++){ ssp_write_word(&inno_spi_dev, 0); // in order to generate clock to receive *(pdata+i) = (u8)(ssp_read_word(&inno_spi_dev)); } SPI_EndFrame();#if 0 u32 count = 0; u32 len_dma = len; u8* pbuf = pdata; inno_spi_start_frame(); if(len > 8){ if(len%8!=0) //filled to multiple of 8bytes len_dma = (len+7)/8*8; while(len_dma > 0){ count = min(len_dma, (u32)DMA_BUFFER_SIZE); inno_spi_dma_start(count); down_interruptible(&dma_sem); len_dma -= count; memcpy(pbuf, inno_dma.rxdma_addr, min(count, len)); len -= min(count, len); pbuf += min(count,len); } goto out; } for(count=0; count<len; count++){ ssp_write_word(&inno_spi_dev, 0); // in order to generate clock to receive *(pdata+count) = ssp_read_word(&inno_spi_dev); }out: inno_spi_end_frame();#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -