misc的方式写驱动相对简单,最底层的还是操作寄存器,和单片机同样的原理。代码直接贴出来,非常时候新手学习。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#define GPIO_4_BASE 0x20180000
#define SDA (1 << 3) /* GPIO 4_3 */
#define SCL (1 << 4) /* GPIO 4_4 */
#define GPIO_SPI_SCL_REG IO_ADDRESS(GPIO_4_BASE + 0x040)
#define GPIO_SPI_SDA_REG IO_ADDRESS(GPIO_4_BASE + 0x020)
#define GPIO_SPI_SDASCL_REG IO_ADDRESS(GPIO_4_BASE + 0x060)
#define GPIO_4_DIR IO_ADDRESS(GPIO_4_BASE + 0x400)
#define GPIO_3_BASE 0x20170000
#define CS (1 << 2) /* GPIO 3_3 */
#define DC (1 << 5) /* GPIO 3_5 */
#define GPIO_SPI_CS_REG IO_ADDRESS(GPIO_3_BASE + 0x020)
#define GPIO_SPI_DC_REG IO_ADDRESS(GPIO_3_BASE + 0x080)
#define GPIO_3_DIR IO_ADDRESS(GPIO_3_BASE + 0x400)
#define HW_REG(reg) *((volatile unsigned int *)(reg))
#define SEND_COMMAND 0x01
#define SEND_DATA 0x03
static void spi_clr(unsigned char whichline)
{
unsigned char regvalue;
if(whichline == SCL){
regvalue = HW_REG(GPIO_4_DIR);
regvalue |= SCL;
HW_REG(GPIO_4_DIR) = regvalue;
HW_REG(GPIO_SPI_SCL_REG) = 0;
}else if(whichline == SDA){
regvalue = HW_REG(GPIO_4_DIR);
regvalue |= SDA;
HW_REG(GPIO_4_DIR) = regvalue;
HW_REG(GPIO_SPI_SDA_REG) = 0;
}else if(whichline == (SDA|SCL)){
regvalue = HW_REG(GPIO_4_DIR);
regvalue |= (SCL|SDA);
HW_REG(GPIO_4_DIR) = regvalue;
HW_REG(GPIO_SPI_SDASCL_REG) = 0;
}else if(whichline == CS){
regvalue = HW_REG(GPIO_3_DIR);
regvalue |= CS;
HW_REG(GPIO_3_DIR) = regvalue;
HW_REG(GPIO_SPI_CS_REG) = 0;
}else if(whichline == DC){
regvalue = HW_REG(GPIO_3_DIR);
regvalue |= DC;
HW_REG(GPIO_3_DIR) = regvalue;
HW_REG(GPIO_SPI_DC_REG) = 0;
}else{
printk("Error input.\n");
}
}
static void spi_set(unsigned char whichline)
{
unsigned char regvalue;
if(whichline == SCL){
regvalue = HW_REG(GPIO_4_DIR);
regvalue |= SCL;
HW_REG(GPIO_4_DIR) = regvalue;
HW_REG(GPIO_SPI_SCL_REG) = SCL;
}else if(whichline == SDA){
regvalue = HW_REG(GPIO_4_DIR);
regvalue |= SDA;
HW_REG(GPIO_4_DIR) = regvalue;
HW_REG(GPIO_SPI_SDA_REG) = SDA;
}else if(whichline == (SDA|SCL)){
regvalue = HW_REG(GPIO_4_DIR);
regvalue |= (SCL|SDA);
HW_REG(GPIO_4_DIR) = regvalue;
HW_REG(GPIO_SPI_SDASCL_REG) = (SDA|SCL);
}else if(whichline == CS){
regvalue = HW_REG(GPIO_3_DIR);
regvalue |= CS;
HW_REG(GPIO_3_DIR) = regvalue;
HW_REG(GPIO_SPI_CS_REG) = 0xff;
}else if(whichline == DC){
regvalue = HW_REG(GPIO_3_DIR);
regvalue |= DC;
HW_REG(GPIO_3_DIR) = regvalue;
HW_REG(GPIO_SPI_DC_REG) = 0xff;
}else{
printk("Error input.\n");
}
}
static void spi_send_byte(unsigned char c,unsigned char dir)
{
unsigned char temp,i;
local_irq_disable();
spi_clr(CS);
if(dir == 1)
spi_set(DC);//data
else
spi_clr(DC);//command
temp = c;
for(i=0; i<8; i++){
if(temp&0x80)
spi_set(SDA);
else
spi_clr(SDA);
temp = temp<<1;
spi_clr(SCL);
udelay(2);
spi_set(SCL);
udelay(2);
}
spi_set(SCL);
spi_set(CS);
}
int gpiospi_open(struct inode * inode, struct file * file)
{
return 0;
}
int gpiospi_close(struct inode * inode, struct file * file)
{
return 0;
}
static long gpiospi_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
switch(cmd){
case SEND_COMMAND:
spi_send_byte((unsigned char)arg,0);
break;
case SEND_DATA:
spi_send_byte((unsigned char)arg,1);
break;
default :
return -1;
}
return 0;
}
static struct file_operations gpiospi_fops =
{
.owner = THIS_MODULE,
.unlocked_ioctl = gpiospi_ioctl,
.open = gpiospi_open,
.release = gpiospi_close,
};
static struct miscdevice gpiospi_dev =
{
MISC_DYNAMIC_MINOR,
"gpio_spi",
&gpiospi_fops,
};
static int __init gpio_spi_init(void)
{
int ret;
ret = misc_register(&gpiospi_dev);
if(ret != 0)
{
printk("could not register gpio_spi device\n");
return -1;
}
printk("load gpio_spi.ko ok!\n");
return ret;
}
static void __exit gpio_spi_exit(void)
{
misc_deregister(&gpiospi_dev);
printk("I'll be leaving, bye!\n");
}
module_init(gpio_spi_init);
module_exit(gpio_spi_exit);
MODULE_LICENSE("GPL");
非常简单的结构,连probe都没有。最核心的是misc_register和misc_deregister,基本就是填充结构体
驱动的open和write方法内没有内容,所有功能放在了ioctl内
另外一种驱动方法:嵌入了系统的I2c总线,稍微复杂一点,因为使用了平台驱动,调用了系统的框架,多了probe等方法,这样的好处是方便移植驱动。I2C和SPi原理类似,可以相互参考。