Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <linux/kernel.h> /* Для printk() и т.д. */
- #include <linux/module.h> /* Эта частичка древней магии, которая оживляет модули */
- #include <linux/init.h> /* Определения макросов */
- #include <linux/fs.h>
- #include <asm/uaccess.h> /* put_user */
- #include <asm/io.h>
- //#include <asm/system.h>
- #include <linux/ioport.h>
- #include <linux/interrupt.h>
- #include <linux/dma-mapping.h>
- #include <asm/dma.h>
- #include <mach/dma.h>
- #include <linux/slab.h>
- #include "mx53.h"
- #include "spislave.h"
- // Ниже мы задаём информацию о модуле, которую можно будет увидеть с помощью Modinfo
- MODULE_LICENSE( "GPL" );
- MODULE_AUTHOR( "A l e x1 e 2e v __ V &a ?l e r i y" );
- MODULE_DESCRIPTION( "Spi slave driver for imx53" );
- MODULE_SUPPORTED_DEVICE( "spi" ); /* /dev/testdevice */
- #define IRQ_NAME "spislave_irq"
- #define IRQ_NUMB 37 // ECSPI-2 irq number
- #define DMA_USE // использовать DMA
- #define SUCCESS 0
- #define DEVICE_NAME "spislave" /* Имя нашего устройства */
- #define INPUT_BUFFER_SIZE (1*1024*1024)
- #define BYTES_IN_FIFO_IRQ_OCCURS 24 // кол-во байт в фифо когда генерится прерывание (макс 32)
- //#define DEBUG_OUTPUT_ENABLED
- struct {
- char data [INPUT_BUFFER_SIZE];
- int start;
- int end;
- } inputBuffer;
- static int maxRxFifoLoad;
- static int maxBufferLoad;
- static int rxoverflowedCount;
- static char locked;
- #if defined(DMA_USE)
- static spi_mxc_port umxcport = {
- .dma_rxbuf_size = SPI_DMA_RXBUFSIZE,
- .dma_rx_id = MXC_DMA_CSPI2_RX, // from /plat-mxc/include/mach/dma.h
- // MXC_DMA_CSPI2_TX
- };
- static dma_info dma_list;//This holds the DMA channel information
- #endif
- // Поддерживаемые нашим устройством операции
- static int device_open( struct inode *, struct file * );
- static int device_release( struct inode *, struct file * );
- static ssize_t device_read( struct file *, char *, size_t, loff_t * );
- static ssize_t device_write( struct file *, const char *, size_t, loff_t * );
- static int region_size = 256;
- // Глобальные переменные, объявлены как static, воизбежание конфликтов имен.
- static int major_number; /* Старший номер устройства нашего драйвера */
- static int is_device_open = 0; /* Используется ли девайс ? */
- static void *spi_base;
- static void *gpio2_base;
- //-------------------------------------------------------------------------------------
- // Прописываем обработчики операций на устройством
- static struct file_operations fops =
- {
- .read = device_read,
- .write = device_write,
- .open = device_open,
- .release = device_release
- };
- #if defined(DMA_USE)
- //-------------------------------------------------------------------------------------
- //--------------------------------DMA STUFF -------------------------------------------
- //-------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------
- /*!
- * Stops DMA and frees the DMA resources
- *
- * @param d_info the structure that holds all the DMA information for a
- * particular MXC SPI
- * @param umxc the MXC SPI port structure, this includes the \b spi_port
- * structure and other members that are specific to MXC SPI
- */
- static void mxcspi_freedma(dma_info *d_info, spi_mxc_port *umxc)
- {
- int i, rxbufs;
- mxc_spi_rxdmamap *rx_buf_elem;
- rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
- for (i = 0; i < rxbufs; i++) {
- rx_buf_elem = (mxc_spi_rxdmamap *) (umxc->rx_dmamap + i);
- dma_free_coherent(NULL, RXDMA_BUFF_SIZE, rx_buf_elem->rx_buf, rx_buf_elem->rx_handle);
- }
- kfree(umxc->rx_dmamap);
- //kfree(umxc->tx_buf);
- mxc_dma_free(d_info->rd_channel);
- //mxc_dma_free(d_info->wr_channel);
- }
- /*!
- * The read DMA callback, this method is called when the DMA buffer has received its
- * data. This functions copies the data to the tty buffer and updates the tty buffer
- * pointers. It also queues the DMA buffer back to the DMA system.
- *
- * @param arg driver private data
- * @param error any DMA error
- * @param cnt amount of data that was transferred
- */
- static void mxcspi_dmaread_callback(void *arg, int error, unsigned int cnt)
- {
- printk(KERN_ERR "\nSPI: DMA READ CALLBACK\n");
- spi_mxc_port *umxc = arg;
- //struct tty_struct *tty = umxc->port.state->port.tty;
- int buff_id, num_bufs, rd_cnt;
- mxc_dma_requestbuf_t readchnl_request;
- mxc_spi_rxdmamap *rx_buf_elem = NULL;
- unsigned int sr1, sr2;
- char flag;
- //rx_buf_elem = NULL; // << DELETE(???!!!)
- //char flag;
- num_bufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
- // Clear the aging timer bit
- //writel(MXC_U A RTUSR1_AGTIM, umxc->port.membase + MXC_ U ARTUSR1);
- buff_id = umxc->dma_rxbuf_id;
- //flag = TTY_NORMAL;
- umxc->dma_rxbuf_id += 1;
- if (umxc->dma_rxbuf_id >= num_bufs)
- {
- umxc->dma_rxbuf_id = 0;
- }
- rx_buf_elem = (mxc_spi_rxdmamap *) (umxc->rx_dmamap + buff_id);
- if (error == MXC_DMA_TRANSFER_ERROR)
- {
- printk(KERN_ERR "SPI: DMA_ERROR: sr1:%x sr2:%x\n", sr1, sr2);
- // By default clearing all error bits in status reg
- // removed
- }
- //rd_cnt = tty_insert_flip_string(tty, rx_buf_elem->rx_buf, cnt);
- //umxc->port.icount.rx += rd_cnt;
- /*
- if (flag != TTY_NORMAL)
- tty_insert_flip_char(tty, 0, flag);
- tty_flip_buffer_push(tty);
- umxc->port.state->port.tty->real_raw = 1;
- */
- char s[256];
- char t[256];
- int i;
- printk (KERN_ERR "\ndata read count=%d:\n ", cnt);
- //printk (KERN_ERR "rxbuf pointer=%08X\n", &rx_buf_elem->rx_buf[0]);
- char *p = rx_buf_elem->rx_buf;
- for (i=0; i<64; i++)
- {
- sprintf (t, "%02X ",rx_buf_elem->rx_buf[i]);
- strcat (s,t);
- }
- printk (KERN_ERR "%s", s);
- drop_data:
- readchnl_request.src_addr = (ECSPI2_BASE+ECSPI2_RXDATA); //umxc->port.mapbase;
- //readchnl_request.src_addr = umxc->mapbase+ ECSPI2_RXDATA;
- readchnl_request.dst_addr = rx_buf_elem->rx_handle;
- readchnl_request.num_of_bytes = RXDMA_BUFF_SIZE;
- mxc_dma_config(dma_list.rd_channel, &readchnl_request, 1, MXC_DMA_MODE_READ);
- mxc_dma_enable(dma_list.rd_channel);
- /*
- */
- }
- /*!
- * Allocates DMA read and write channels, creates DMA read and write buffers and
- * sets the channel specific parameters.
- *
- * @param d_info the structure that holds all the DMA information for a
- * particular MXC SPI
- * @param umxc the MXC SPI port structure, this includes the
- *
- * @return The function returns 0 on success and a non-zero value on failure.
- */
- static int mxcspi_initdma(dma_info *d_info)
- {
- int rxbufs, i, j;
- spi_mxc_port * umxc = &umxcport;
- umxc->mapbase = spi_base; // ioremap(ECSPI2_BASE, 0x80);
- mxc_dma_requestbuf_t *readchnl_reqelem;
- //* mxc_dma_requestbuf_t - an array of physical addresses to the user defined
- //* buffers. The caller must guarantee the dma_buf is
- //* available until the transfer is completed.
- mxc_spi_rxdmamap *rx_buf_elem;
- int ret = 0;
- // Request for the read channels
- printk(KERN_ERR "\nMXC SPI: Start init dma, dma_rx_id=%d\n", umxc->dma_rx_id);
- d_info->rd_channel = mxc_dma_request(umxc->dma_rx_id, "MXC SPI2 Read");
- if (d_info->rd_channel < 0)
- {
- printk(KERN_ERR "\nMXC SPI: Cannot allocate DMA read channel\n");
- return -1;
- }
- printk(KERN_ERR "\nMXC SPI: dma_rxbuf_size = %d\n", umxc->dma_rxbuf_size);
- rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
- /* Allocate the DMA Virtual Receive Buffer */
- umxc->rx_dmamap = kmalloc(rxbufs * sizeof(mxc_spi_rxdmamap), GFP_KERNEL);
- if (umxc->rx_dmamap == NULL)
- {
- ret = -1;
- goto err_dma_rx_buff; //err_dma_rx_buff;
- }
- printk(KERN_ERR "\nMXC SPI: DMA virtual receive buffer allocated");
- /* Allocate the DMA Receive Request structures */
- readchnl_reqelem = kmalloc(rxbufs * sizeof(mxc_dma_requestbuf_t), GFP_KERNEL);
- if (readchnl_reqelem == NULL)
- {
- ret = -1;
- goto err_request;
- }
- printk(KERN_ERR "\nMXC SPI: DMA receive request structures allocated, rxbufs=%d\n", rxbufs);
- for (i = 0; i < rxbufs; i++)
- {
- rx_buf_elem = (mxc_spi_rxdmamap *) (umxc->rx_dmamap + i);
- rx_buf_elem->rx_buf = dma_alloc_coherent(NULL, RXDMA_BUFF_SIZE, &rx_buf_elem->rx_handle, GFP_DMA);
- if (rx_buf_elem->rx_buf == NULL)
- {
- for (j = 0; j < i; j++)
- {
- rx_buf_elem = (mxc_spi_rxdmamap *) (umxc->rx_dmamap + j);
- dma_free_coherent(NULL, RXDMA_BUFF_SIZE, rx_buf_elem->rx_buf, rx_buf_elem->rx_handle);
- }
- ret = -1;
- goto cleanup;
- }
- }
- umxc->dma_rxbuf_id = 0;
- /* Setup the DMA read request structures */
- for (i = 0; i < rxbufs; i++)
- {
- rx_buf_elem = (mxc_spi_rxdmamap *) (umxc->rx_dmamap + i);
- (readchnl_reqelem + i)->src_addr = umxc->mapbase+ECSPI2_RXDATA;
- (readchnl_reqelem + i)->dst_addr = rx_buf_elem->rx_handle;
- (readchnl_reqelem + i)->num_of_bytes = RXDMA_BUFF_SIZE;
- printk(KERN_ERR "\nMXC SPI: buffer:%d read request structures: src_addr=%08X, dst_addr=%08X, num_of_bytes=%d\n",
- i, (readchnl_reqelem + i)->src_addr, (readchnl_reqelem + i)->dst_addr, (readchnl_reqelem + i)->num_of_bytes );
- }
- mxc_dma_config(d_info->rd_channel, readchnl_reqelem, rxbufs, MXC_DMA_MODE_READ);
- mxc_dma_callback_set(d_info->rd_channel, mxcspi_dmaread_callback, umxc);
- /* Start the read channel */
- mxc_dma_enable(d_info->rd_channel);
- kfree(readchnl_reqelem);
- //d_info->dma_txchnl_inuse = 0;
- return ret;
- cleanup:
- printk(KERN_ERR "\nMXC SPI: Cleanup");
- kfree(readchnl_reqelem);
- err_request:
- printk(KERN_ERR "\nMXC SPI: Err request");
- kfree(umxc->rx_dmamap);
- err_dma_rx_buff:
- printk(KERN_ERR "\nMXC SPI: Error DMA");
- mxc_dma_free(d_info->rd_channel);
- return ret;
- }
- #endif
- //-------------------------------------------------------------------------------------
- //---------------------------- SPI STUFF -------------------------------------------
- //-------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------
- static void ResetInputBuffer(void)
- {
- inputBuffer.start=0;
- inputBuffer.end=0;
- }
- //-------------------------------------------------------------------------------------
- static void ConfigureSpi(void)
- {
- // ------- CONREG -----------
- // CONREG: 19-18 : 00=channel 0 select (SS0),
- // 7-4 : CHANNEL MODE 0-slave/1-master, [3]-channel 3... [0]-channel 0
- // 3: SMC - immediately starts SPI burst when data is written to TXFIFO
- *(int*)(spi_base+ECSPI2_CONREG) = 0x00 ; // enable bit OFF, to reset block
- *(int*)(spi_base+ECSPI2_CONREG) = (1<<3) | (0x07<<20); // | (0x08<<20); // channel 0 slave , SMC enabled, 8 bits SPI burst
- // ------- CONFIGREG -----------
- *(int*)(spi_base+ECSPI2_CONFIGREG) = 0x00; // согласно картинке Меньшова нам нужен режим POL=0 PHA=0
- // ------- INTREG ----------- // isr control
- #if defined(DMA_USE)
- *(int*)(spi_base+ECSPI2_INTREG) = 0;//
- #else
- *(int*)(spi_base+ECSPI2_INTREG) = (1<<4) ; // 4: rx fifo data request enable (rxfifo > rx threshold)
- #endif
- // ------- DMAREG -----------
- // DMAREG:
- // 21-16: rx threshold
- // 23-RXDEN-RXFIFO DMA request enable
- // 29-24 DMA burst length, 31 - RX FIFO tail DMA enable
- #if !defined(DMA_USE)
- *(int*)(spi_base+ECSPI2_DMAREG) = (BYTES_IN_FIFO_IRQ_OCCURS<<16); //
- #else // DMA enabled
- *(int*)(spi_base+ECSPI2_DMAREG) = (BYTES_IN_FIFO_IRQ_OCCURS<<16) | (1<<23) ;
- // | (BYTES_IN_FIFO_IRQ_OCCURS<<24) | (1<<31);
- #endif
- // ------- PERIODREG -----------
- // PERIODREG only for master:
- // << PERIODREG SKIPPED >>
- // 15 - CSRC : 0-Spi clock, 1- Low freq ref clk 32.768Khz
- // ------- TESTREG -----------
- // TESTREG:
- // 31- LBC: 1- loopback enabled - receiver is connected to transmitter
- *(int*)(spi_base+ECSPI2_TESTREG) = 0;
- *(int*)(spi_base+ECSPI2_CONREG) |= 0x01; // enable bit ON
- //----------- end of SPI configure ------------------
- }
- //-------------------------------------------------------------------------------------
- // Функция загрузки модуля. Входная точка. Можем считать что это наш main()
- static int __init spislave_init( void )
- {
- struct resource *res;
- printk( KERN_ALERT "spi driver: configuring ECSPI-2...\n" );
- res= request_mem_region(ECSPI2_BASE, region_size, "spi");
- spi_base = ioremap_nocache(ECSPI2_BASE, region_size);
- res= request_mem_region(GPIO2_BASE, region_size, "gpio2");
- gpio2_base= ioremap_nocache(GPIO2_BASE, region_size);
- *(int*)(gpio2_base+GPIO_GDIR) |= (1<<24); // 1 means output
- ConfigureSpi();
- printk( KERN_ALERT "spi driver: spislave driver loaded!\n" );
- // Регистрируем устройсво и получаем старший номер устройства
- major_number = register_chrdev( 0, DEVICE_NAME, &fops );
- if ( major_number < 0 )
- {
- printk( "spi driver: Registering the character device failed with %d\n", major_number );
- return major_number;
- }
- // Сообщаем присвоенный нам старший номер устройства
- printk( "spi driver: spislave module is loaded!\n" );
- printk( "spi driver: Please, create a dev file with 'mknod /dev/spislave c %d 0'.\n", major_number );
- return SUCCESS;
- }
- //-------------------------------------------------------------------------------------
- // Функция выгрузки модуля
- static void __exit spislave_exit( void )
- {
- // Освобождаем устройство
- iounmap(spi_base);
- release_mem_region(ECSPI2_BASE, region_size);
- unregister_chrdev( major_number, DEVICE_NAME );
- printk( KERN_ALERT "spi driver: spislave module is unloaded!\n" );
- }
- //-------------------------------------------------------------------------------------
- // Указываем наши функции загрузки и выгрузки
- module_init( spislave_init );
- module_exit( spislave_exit );
- static void CheckMaxRxFifoLoad(int load)
- {
- if (load> maxRxFifoLoad)
- {
- maxRxFifoLoad=load;
- printk (KERN_ALERT "max rx fifo load=%d\n", maxRxFifoLoad);
- if (maxRxFifoLoad==64)
- {
- printk (KERN_ALERT "rx fifo MAXIMUM REACHED. cleared.\n");
- maxRxFifoLoad=0;
- }
- }
- }
- //-------------------------------------------------------------------------------------
- static irqreturn_t irq_handler(int irq, void *dummy, struct pt_regs * regs)
- { // interrupt service routine (ISR)
- int testReg;
- int rxFifoCounter;
- int rxData;
- int counter;
- *(int*)(gpio2_base+GPIO_DR) |= (1<<24); // дергаем ножкой, для отладки
- // экспериментально выяснил, что i r q_handler может возникать во время операции read, т.е.
- // read не являются блокирующим для прерывания (по идее так и должно быть, однако были в свое время подозрения).
- //printk(KERN_ALERT "spi IRQ: Interrupt handler executed!\n");
- counter = 256; // число байт которые можем схавать за раз в одном прерывании. это по сути защита от зависонов.
- //теоретически может быть
- // больше чем весь FIFO, т.к. пока сидим и хаваем данные тут могут прийти еще данные в FIFO
- //printk(KERN_ALERT "spi IRQ: %d bytes in buffer\n", rxFifoCounter);
- testReg = *(int*)(spi_base+ECSPI2_TESTREG);
- rxFifoCounter = ((testReg>>8) & 0x7F);// only 7 bits used
- #if defined(DEBUG_OUTPUT_ENABLED)
- CheckMaxRxFifoLoad(rxFifoCounter);
- #endif
- while (rxFifoCounter>0)
- {
- rxData = *(int*)(spi_base+ECSPI2_RXDATA);
- locked=1; // lock inputBuffer
- inputBuffer.data[inputBuffer.end] = rxData & 0xFF;
- inputBuffer.end = (inputBuffer.end+1) % INPUT_BUFFER_SIZE;
- locked=0; // unlock inputBuffer
- testReg = *(int*)(spi_base+ECSPI2_TESTREG);
- rxFifoCounter = ((testReg>>8) & 0x7F);// only 7 bits used
- #if defined(DEBUG_OUTPUT_ENABLED)
- CheckMaxRxFifoLoad(rxFifoCounter);
- #endif
- if (!(counter--)) // защита от зависонов
- break;
- }
- *(int*)(gpio2_base+GPIO_DR) &= ~(1<<24);
- return IRQ_HANDLED;
- }
- //-------------------------------------------------------------------------------------
- void ResetMaxRxFifoLoad(void)
- {
- maxRxFifoLoad=0;
- maxBufferLoad=0 ;
- rxoverflowedCount=0;
- #if defined(DEBUG_OUTPUT_ENABLED)
- printk (KERN_ALERT "max rx fifo reset\n");
- #endif
- }
- //-------------------------------------------------------------------------------------
- static int device_open( struct inode *inode, struct file *file )
- {
- int status = 0;
- int retval;
- locked=0;
- if ( is_device_open )
- return -EBUSY;
- ResetInputBuffer();
- #if defined(DMA_USE)
- printk("\nspi driver: DMA ENABLED\n"); // isr handling
- printk("\nInterrupts for SPI disabled\n"); // isr handling
- #else
- printk("\nspi driver: DMA DISABLED\n"); // isr handling
- printk("\nspi driver: Requesting interrupt\n"); // isr handling
- status = request_irq(IRQ_NUMB, irq_handler,0,IRQ_NAME, NULL);
- if (status == 0)
- {
- printk("\nspi driver: spi IRQ request successful!\n");
- }
- else
- {
- printk("\nspi driver: spi IRQ request failed!\n");
- return status;
- }
- #endif
- ResetMaxRxFifoLoad();
- //------ Initialize the DMA if we need SDMA data transfer ----------
- #if defined(DMA_USE)
- retval = mxcspi_initdma(&dma_list);
- if (retval != 0)
- {
- printk (KERN_ERR "MXC SPI: Failed to initialize DMA for SPI\n" );
- // TO DO: add here free interrupts
- return retval;
- }
- // Configure the GPR register to receive SDMA events
- //config_spidma_event(0); // what is it for??
- #endif
- is_device_open++;
- return SUCCESS;
- }
- //-------------------------------------------------------------------------------------
- static int device_release( struct inode *inode, struct file *file )
- {
- /*
- #if defined(DMA_USE)
- mxcspi_freedma(&dma_list, &umxcport);
- #endif
- */
- is_device_open--;
- #if !defined(DMA_USE)
- free_irq(IRQ_NUMB, NULL);
- printk ("spi driver: spi irq freed\n");
- #endif
- ResetMaxRxFifoLoad();
- return SUCCESS;
- }
- //-------------------------------------------------------------------------------------
- static ssize_t
- device_write( struct file *filp, const char *buff, size_t len, loff_t * off )
- {
- printk( "spi driver: Sorry, this operation isn't supported.\n" );
- return -EINVAL;
- }
- //-------------------------------------------------------------------------------------
- static int rxCountPreviousDebug;
- static ssize_t device_read( struct file *filp, /* include/linux/fs.h */
- char *buffer, /* buffer */
- size_t length, /* buffer length */
- loff_t * offset )
- {
- #define MAX_FAILED_OPS 128
- int byte_read = 0;
- char rx;
- int curBufferLoad = (inputBuffer.end>=inputBuffer.start)? (inputBuffer.end-inputBuffer.start) : (INPUT_BUFFER_SIZE-inputBuffer.start + inputBuffer.start);
- int failedOps=0;
- #if defined(DEBUG_OUTPUT_ENABLED)
- int rxoverflowed = *(int*)(spi_base+ECSPI2_STATREG) & (1<<6);
- if (rxoverflowed)
- {
- *(int*)(spi_base+ECSPI2_STATREG) |= (1<<6) ; // writing to 1 to this bit to clear overflow flag
- rxoverflowedCount++;
- //if (rxoverflowedCount % 5 ==0)
- printk (KERN_ERR "overflow count = %d\n", rxoverflowedCount);
- }
- #endif
- while ( length )
- {
- // need to avoid processing during active interrupt handler process
- // check if inputBuffer is unlocked
- if (locked)
- {
- failedOps++;
- if (failedOps>MAX_FAILED_OPS)
- break;
- else
- continue;
- }
- #if defined(DMA_USE) // debug var
- int rxFifoCounter = (((*(int*)(spi_base+ECSPI2_TESTREG)) >> 8) & 0x7F);// only 7 bits used
- if (rxCountPreviousDebug!=rxFifoCounter)
- {
- put_user( (char) (rxFifoCounter & 0xFF), buffer);
- rxCountPreviousDebug=rxFifoCounter;
- return 1;
- }
- else
- return 0;
- #endif
- if (inputBuffer.start==inputBuffer.end)
- break;
- rx = inputBuffer.data[inputBuffer.start];
- inputBuffer.start = (inputBuffer.start+1) % INPUT_BUFFER_SIZE;
- //put_user( rxFifoCounter, buffer++ );
- put_user( rx, buffer++ );
- length--;
- byte_read++;
- }
- #if defined(DEBUG_OUTPUT_ENABLED)
- if (curBufferLoad>maxBufferLoad)
- {
- maxBufferLoad= curBufferLoad;
- printk ("max buffer load = %d\n", maxBufferLoad);
- }
- #endif
- return byte_read;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement