📄 dv_spi.c
字号:
/*File: dv_spi.c Author: Joshua HintzeE-Mail: joshh at imsar.com Description: A very simple implementation for using the SPIport on the Davinci 6446 platform. This is my first linux driver everso comments are appreciated. Thanks goes to Sean on Davinci Mailing List Limitations: Currently this is written to only use a single Chip Select/SPI_ENO the reason being that the/SPI_EN1 is multiplexed with the ATA HDDIR lines. Platform Dependencies: Davinci Change History:Date Author Description*/ //-------------------------------------------------------------------------- ///////////////////////////// INCLUDES//////////////////////////#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h> // udelay()#include <linux/fs.h> // everything...#include <asm/uaccess.h> // copy_from/to_user // Definition for SPI Base Address and the local Power or Sleep ControllerLPSoC#include <asm/arch/hardware.h> #include <asm/hardware/clock.h> ///////////////////////////// PROTOTYPES///////////////////////////* Declaration of dv_spi.c functions */int dv_spi_open(struct inode *inode, struct file *filp);int dv_spi_release(struct inode *inode, struct file *filp);ssize_t dv_spi_read(struct file *filp, char *buf, size_t count, loff_t*f_pos);ssize_t dv_spi_write(struct file *filp, const char *buf, size_t count,loff_t *f_pos);static void dv_spi_exit(void);static int dv_spi_init(void); ///////////////////////////// DEFINITIONS & GLOBALS//////////////////////////// Register definitions to control the SPI#define SPIGCR0 0x01c66800#define SPIGCR1 0x01c66804#define SPIINT 0x01c66808#define SPILVL 0x01c6680c#define SPIFLG 0x01C66810// SPI Flag Status Register#define SPIPC0 0x01C66814// SPI Pin Control Register 0#define SPIPC2 0x01C6681C// SPI Pin Control Register 2#define SPIDAT1 0x01C6683C// SPI Shift Register 1#define SPIBUF 0x01C66840// SPI Buffer Register#define SPIEMU 0x01C66844// SPI Emulation Register#define SPIDELAY 0x01C66848// SPI Delay Register#define SPIDEF 0x01C6684C// SPI Default Chip Select Register#define SPIFMT0 0x01C66850// SPI Data Format Register 0#define SPIFMT1 0x01C66854// SPI Data Format Register 1#define SPIFMT2 0x01C66858// SPI Data Format Register 2#define SPIFMT3 0x01C6685C// SPI Data Format Register 3#define INTVEC0 0x01C66860// SPI Interrupt Vector Register 0#define INTVEC1 0x01C66864// SPI Interrupt Vector Register 1 // Definition for GPIO Pin Multiplexing#define PINMUX1 0x01c40004 // SPI format - Polarity and Phase // ***NOTE*** It doesn't seem like the SPI Phase for the davinci follows thestandard// phase as described by the motorola architecture. I.E. phase 0 = sample onrising edge of clock// In the davinci it seems this is opposite.#define SPI_PHASE 1// Set these values to whatever you need#define SPI_POLARITY 0 // Macro for accessing a memory location such as a register#define SPI_REG(reg) (*(int *__iomem) IO_ADDRESS(reg)) // Version numbers#define MAJOR_VERSION 60#define MINOR_VERSION 01 // Global pointer to the clock struct. We use this to start up the LPSoC(local power system on chip)// so our SPI peripheral has power going to it.static struct clk *g_clkptr = 0; // Structure that declares the usual file access functionsstatic struct file_operations dv_spi_fops = { read: dv_spi_read, write: dv_spi_write, open: dv_spi_open, release: dv_spi_release}; // We will use a 1K read buffer to store datastatic unsigned char *g_readbuf = 0;static unsigned int g_readbufcount = 0; static int dv_spi_init(void) { int result; /* Registering device */ result = register_chrdev(MAJOR_VERSION, "spi",&dv_spi_fops); if (result < 0) { printk("<1>dv_spi: cannot obtain majornumber %d\n", MAJOR_VERSION); return result; } // Allocate space for the read buffer g_readbuf = kmalloc(1024, GFP_KERNEL); if (!g_readbuf) { result = -ENOMEM; dv_spi_exit(); return result; } printk("<1>Inserting SPI module\n"); return 0;} static void dv_spi_exit(void) { /* Freeing the major number */ unregister_chrdev(MAJOR_VERSION, "spi"); /* Freeing buffer memory */ if(g_readbuf) kfree(g_readbuf); if (g_clkptr) dv_spi_release(0,0); printk("<1>Removing SPI module\n");} // Called when a userspace program opens the fileint dv_spi_open(struct inode *inode, struct file *filp){ unsigned int control; // Power up the SPI hardware by requesting and enabling theclock g_clkptr = clk_get(NULL, "SPICLK"); if(g_clkptr <= 0) printk("<l>Error could not get theclock\n"); else clk_enable(g_clkptr); // -------------------------------- // Configure GPIO Pins for SPI // -------------------------------- // Enable the SPI pins on the GPIO SPI_REG(PINMUX1) |= 0x100; // -------------------------------- // Reset SPI // -------------------------------- control=0x00000000; SPI_REG(SPIGCR0)=control;// Place SPI peripheral in reset mdelay(1);// Delay for a bit control=0x00000001; SPI_REG(SPIGCR0)=control;// Remove from reset // -------------------------------- // Enable SPI CLK & Master // -------------------------------- control=0x00000003; SPI_REG(SPIGCR1)=control; // -------------------------------- // Enable pins : DI,DO,CLK,EN0 // -------------------------------- control=0x00000E01; SPI_REG(SPIPC0)=control; /* -------------------------------- // Set data format in SPIFMT0 - THIS CAN BE CHANGED BY IOCTLcommands // SHIFTDIR in bit 20 set to 1 : MSB first // POLARITY and PHASE in bit 17, 16 set to 0, 0 // PRESCALE in bit 15-8 set to whatever you need, SPI_CLK =SYSCLK5 / (Prescale + 1) // CHARLEN in bit 4-0 set to 08 : 8 bit characters -------------------------------- */ control=0x00000000 | (SPI_POLARITY << 17) | (SPI_PHASE <<16) | (0x7 << 8) | 0x8; SPI_REG(SPIFMT0)=control; // -------------------------------- // Set data format for used -> SPIFMT0 // -------------------------------- control=0x00000000 | (0x00 << 24); SPI_REG(SPIDAT1)=control; // -------------------------------- // Set hold time and setup time // -------------------------------- control=0x00000000 | (0x03 << 16) | (0x06 << 24); SPI_REG(SPIDELAY)=control; // -------------------------------- // Set Chip Select Default // CSHOLD -> 0 -> release SPI_EN0 state after transmission-> bit 28 // CSNR -> 2 -> Only use SPI_EN0 // -------------------------------- control=SPI_REG(SPIDAT1); control|= (0x2 << 16); SPI_REG(SPIDAT1)=control; // -------------------------------- // Enable for transmitting // -------------------------------- control=SPI_REG(SPIGCR1); control=control | 1 << 24; // enable SPIENA SPI_REG(SPIGCR1)=control; // Zero out our read buffer memset(g_readbuf, 0, 1024); g_readbufcount = 0; return 0;} // Called when a userspace program closes the fileint dv_spi_release(struct inode *inode, struct file *filp){ // Place SPI peripheral into reset SPI_REG(SPIGCR0)=0; // Remove the SPI output on the GPIO SPI_REG(PINMUX1) &= ~0x100; // Disable the clock thus removing power from the peripheral if(g_clkptr) clk_disable(g_clkptr); g_clkptr = 0; return 0;} // Reading from the SPI devicessize_t dv_spi_read(struct file *filp, char *buf, size_t count, loff_t*f_pos){// printk("<1>Attempting to read %d bytes and we only have %dbytes\n",count,g_readbufcount); if(g_readbufcount == 0) return 0; // No data // See if there is enough available if(count > g_readbufcount) count = g_readbufcount; // Transferring data to user space copy_to_user(buf,g_readbuf,count); // Now shift the memory down memmove(&g_readbuf[0],&g_readbuf[count],g_readbufcount-count); g_readbufcount -= count; // Advance the file pointer *f_pos += count; return count;} // Writing to the SPI devicessize_t dv_spi_write(struct file *filp, const char *buf, size_t count,loff_t *f_pos){ unsigned char spiData; size_t i; unsigned int control; unsigned int ReadByte; // Wait until the TX buffer is clear control = SPI_REG(SPIBUF); while(control & (1 << 29)) // Wait until the TX data has been transmitted control = SPI_REG(SPIBUF); // Write out data one byte at a time for(i=0; i < count; i++) { ReadByte = 0; // Send the data copy_from_user(&spiData,buf+i,1); control = 0x00000000 | (0x1 << 28) | (0x2 <<16) | spiData; // Hold the chip select line between multibytes SPI_REG(SPIDAT1) = control; if(i == (count -1)) SPI_REG(SPIDAT1) = 0x00000000 | (0x2 << 16) | spiData; // Remove the chip select hold control = SPI_REG(SPIBUF); while(control & (1 << 29))// Wait until the TX data has been transmitted { // Check for data received if(!(control & (1 << 31))) { // We havejust read a byte of data, store it. if(g_readbufcount < 1024) { // printk("<1>Read in byte value = %d!\n",count); g_readbuf[g_readbufcount] = control & 0xFF; g_readbufcount++; ReadByte = 1; } } control = SPI_REG(SPIBUF); } // Make sure we have always read in a byte while(!ReadByte) { // Check for data received if(!(control & (1 << 31))) { //printk("<1>we received a byte with value %d\n",control & 0xFF); // We havejust read a byte of data, store it. if(g_readbufcount < 1024) { // printk("<1>Read in byte value = %d\n",control); g_readbuf[g_readbufcount] = control & 0xFF; g_readbufcount++; ReadByte = 1; } } control = SPI_REG(SPIBUF); } } return count;} MODULE_LICENSE("Dual BSD/GPL");module_init(dv_spi_init);module_exit(dv_spi_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -