近期做毕业设计,移植了一个ARM9,AD38344 SPI驱动。现在我慢慢把我经历写出来。对于驱动我是个小白,如果
有什么写错的,望各位指正。期间参考了 国嵌和 http://my.csdn.net/weiqing1981127 博客的大量资料。
目标板:TQ2440开发板 cpu:s3c2440 linux内核:2.6.30
网上有很多关于SPI驱动的移植,当是很少拿一个真正的SPI外设作讲解,显得不过生动。AD38344是8通道16位AD,基于spi通信协议。在SPI子系统中,包含两类设备驱动。一类称之为.SPI主控设备驱动,用于驱动SPI主控设备,以和SPI总线交互,读写通信数据。另一类称之为SPI接口设备驱动,用于解析SPI主控设备驱动读取的i数据,形成有意义的协议数据。我们所写的ADS8344驱动属于SPI接口设备驱动。而SPI接口设备驱动是基于 总线设备驱动模型的。我们所写的ADS8344驱动是这个模型的驱动。spi总线在内核代码将会注册(如果你在编译内核时在勾选上spi选项)。SPI的设备信息需要你在机器配置文件当中添加(机器配置文件一般在arch/arm/mach-s3c2440/中。不同的内核,机器配置文件将会不同。我的2.6.30内核是采用天嵌科技资料包内提供的内核。故我的机器配置文件为mach-tq2440.c)。
我先将ADS8344驱动的代码贴出,稍后进行分析。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/spi/spi.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/cache.h>
#include <linux/mutex.h>
#include <linux/spi/spi.h>
#define AVERAGETIMES 4
static int send2user=0;
static struct {
struct spi_device *spi;
struct class *ads8344_class;
int major;
struct mutex lock;
} ads38344_data;
static inline int spi_nor_setup(struct spi_device *spi, u8 bst_len)
{
spi->bits_per_word = bst_len << 3;
return spi_setup(spi);
}
static ssize_t ads8344_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
char __user readResult[2];
readResult[0] =(char __user)(send2user&0XFF);
readResult[1] =(char __user)((send2user>>8)&0XFF);
copy_to_user(buf,readResult,sizeof(readResult));
return sizeof(readResult);
}
static ssize_t ads8344_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
return 0;
}
static int ads8344_open(struct inode *inode, struct file *file)
{
return 0;
}
static int ads8344_release(struct inode *inode, struct file *file)
{
return 0;
}
static int ads8344_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
send2user=0X00000000;
u8 channel,revData[AVERAGETIMES][3];
u8 control_word;
int ret = 0, i,k,z;
int temp[AVERAGETIMES][3];
pr_debug("ioctl cmd %d is issued...\n", cmd);
if((cmd>7)||(cmd<0))
return -EINVAL;
switch (cmd) {
case 0: channel = 0; break;
case 1: channel = 4; break;
case 2: channel = 1; break;
case 3: channel = 5; break;
case 4: channel = 2; break;
case 5: channel = 6; break;
case 6: channel = 3; break;
case 7: channel = 7; break;
default: channel = 0; break;
}
mutex_lock(&ads8344_data.lock);
for (i = 0; i < AVERAGETIMES; i++) {
control_word = (1u << 7)|(channel << 4)|(1u << 2)|0x3;
ret = spi_write_then_read(ads8344_data.spi, &control_word, 1,revData[i],3);
if (ret)
break;
}
mutex_unlock(&ads8344_data.lock);
if (ret)
return -EIO;
for(k=0;k<AVERAGETIMES;k++)
{
temp[k][0]=(int)revData[k][0];
temp[k][1]=(int)revData[k][1];
temp[k][2]=(int)revData[k][2];
}
for(z=0;z<AVERAGETIMES;z++)
{
send2user += ((temp[z][0]<<16)|(temp[z][1]<<8)|temp[z][2])>>7;
}
send2user= send2user>>2;
send2user &=0xffff;
return ret;
}
//设备文件访问操作接口
static const struct file_operations ads8344_fops = {
.owner = THIS_MODULE,
.read = ads8344_read,
.write = ads8344_write,
.open = ads8344_open,
.release = ads8344_release,
.ioctl = ads8344_ioctl,
};
static int ads8344_probe(struct spi_device *spi) //监测系统是否有ads8344设备,并进行设备注册
{
printk("we has find spi dev\n");
int ret = 0;
struct device *dev;
printk("we find spi dev! \n");
mutex_init(&ads8344_data.lock); //初始化lock信号量
ads8344_data.spi = spi_dev_get(spi); //获取SPI设备
aads8344_data.major = 0; //定义主设备号
ret = register_chrdev(ads8344_data.major, "ads8344", &ads8344_fops);//注册字符设备
if (ret < 0)
return ret;
if (ads8344_data.major == 0)
{
ads8344_data.major = ret;
printk(KERN_INFO "ads8344: major number %d\n", ads8344_data.major);
}
ad7490_data.ads8344_class = class_create(THIS_MODULE, "ads8344"); //创建ad7490设备类
if (IS_ERR(ads8344_data.ads8344_class))
{
dev_err(&ads8344_data.spi->dev, "ads8344: failed to create ads8344 class\n");
goto char_dev_remove;
}
dev = device_create(ads8344_data.ads8344_class, NULL,
MKDEV(ads8344_data.major, 0), NULL, "ads8344");//创建设备节点
if (IS_ERR(dev)) {
dev_err(&ads8344_data.spi->dev,
"ads8344: failed to create class device\n");
goto class_remove;
}
return 0;
class_remove:
class_destroy(ads8344_data.ads8344_class);
char_dev_remove:
unregister_chrdev(ads8344_data.major, "ads8344");//注销字符设备
return -ENODEV;
}
static int __devexit ads8344_remove(struct spi_device *spi)
{
device_destroy(ads8344_data.ads8344_class, MKDEV(ads8344_data.major, 0));
class_destroy(ads8344_data.ads8344_class);
unregister_chrdev(ads8344_data.major, "ads8344");
return 0;
}
static struct spi_driver ads8344_driver = {
.driver = {
.name = "ads8344",
.owner = THIS_MODULE,
},
.probe = ads8344_probe,
.remove = __devexit_p(ads8344_remove),
};
static int __init ads8344_init(void)
{
printk("ads8344_init\n");
return spi_register_driver(&ads8344_driver);
}
module_init(ads8344_init);
static void __exit ads8344_exit(void)
{
printk("ads8344_exit\n");
spi_unregister_driver(&ads8344_driver);
}
module_exit(ads8344_exit);
MODULE_DESCRIPTION("Driver for ads8344");
MODULE_AUTHOR("Electronics, Inc");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:ads8344");
现在讲解如何进行SPI移植。
step1:
在 arch/arm/mach-s3c2440/mach-tq2440.c中添加以下信息
#include <linux/spi/spi.h>
#include <mach/spi.h>
然后加入如下代码:
static struct spi_board_info s3c2410_spi0_board[] =
{
[0] = {
.modalias = "ads8344", //设备的名称用来和驱动进行匹配,一定要和ADS8344驱动中 ads8344_driver中的名字相同,
.bus_num = 0, //总线的编号,实际指对应的SPI寄存器
.chip_select = 0, //反映了这个芯片是不是被连接到SPI上
.irq = IRQ_EINT9, //设备的中断号
.max_speed_hz = 500 * 1000, //SPI的最大速率
}
};
static struct s3c2410_spi_info s3c2410_spi0_platdata = {
.pin_cs = S3C2410_GPG(2),
.num_cs = 1, //所有的片选信号
.bus_num = 0, //SPI多对应的总线编号
.gpio_setup = s3c24xx_spi_gpiocfg_bus0_gpe11_12_13, //引脚设置函数
};
Step2:
在tq2440_devices[]平台数组中添加如下代码:
&s3c_device_spi0,
Step3:
最后在tq2440_machine_init函数中加入如下代码:
s3c_device_spi0.dev.platform_data= &s3c2410_spi0_platdata;
spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board));
Step4:
最后需要修改arch/arm/plat-s3c24xx/KConfig文件
找到
config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13
bool
help
SPI GPIO configuration code for BUS0 when connected to
GPE11, GPE12 and GPE13.
config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13
bool "S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13"
help
SPI GPIO configuration code for BUS0 when connected to
GPE11, GPE12 and GPE13.
Step5:到内核代码当中输入 make menuconfig ARCH=arm ,进入内核配置菜单
将System Type->S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13
Device Drivers->SPI support勾选上~
(贴图))
然后重新编译内核。
step6:
将ADS8344驱动编译成内核模块加载到内核当中。这样一个完整的ADS833驱动就移植好了。我将在下一讲中对代码进行详解。