内核版本:linux-2.6.32.2
开发板:mini2440
基于总线驱动设备模型基本框架:
bus_drv_dev模型(是一种机制)
对于device来说:
1.把device相关信息放入bus的dev链表
2.从bus的drv链表取出每一个drv,用bus的match函数判断drv能否支持dev
3.若能支持,调用drv的probe函数。
对于driver来说
1.把driver放入drv链表
2.从dev链表中取出,利用mach函数一一比较,若发现匹配的,调用probe函数。
最近在弄DS1302芯片,对于单片机的程序,可以参考我的另外一篇博文stm8及DS1302的一些操作,然后想移植到linux系统中来,在源码中包含了一个DS1302的驱动文件(\linux-2.6.32.2\drivers\rtc\rtc-ds1302.c
打开该文件,首先查看他的模块init函数:
static int __init ds1302_rtc_init(void)
{
return platform_driver_probe(&ds1302_platform_driver, ds1302_rtc_probe);
}
继续追踪,在platform_driver_probe函数里,看到了retval = code = platform_driver_register(drv);确定该实现,运用了总线驱动设备模型。
如果我要用它的驱动的话可以自己先完成platform_device_registerv的注册,先确定硬件相关的接口,我把ds1302模块飞线接在了mini2440开发板的GPIOF0,1,2三个IO口上。
创建一个ds1302_dev.c文件,完成platform_device_registerv的注册。注意,在rtc-ds1302.c中ds1302的驱动name是"rtc-ds1302",所以该文件中dev name也一定是"rtc-ds1302",只有两者匹配,最后才会调用probe函数。
ds1302_dev.c的源码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static void ds1302_release(struct device * dev)
{
}
static struct resource ds1302_resource[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.name = "ds1302_rst",
.start = 0,
.end = 0,
.flags = IORESOURCE_IO,
},
[2] = {
.name = "ds1302_io",
.start = 1,
.end = 1,
.flags = IORESOURCE_IO,
},
[3] = {
.name = "ds1302_clk",
.start = 2,
.end = 2,
.flags = IORESOURCE_IO,
},
};
static struct platform_device ds1302_device_driver = {
.name = "rtc-ds1302",
.id = -1,
.num_resources = ARRAY_SIZE(ds1302_resource),
.resource = ds1302_resource,
.dev = {
.release = ds1302_release,
},
};
static int ds1302_dev_init(void)
{
return platform_device_register(&ds1302_device_driver);
}
static void ds1302_dev_exit(void)
{
platform_device_unregister(&ds1302_device_driver);
}
module_init(ds1302_dev_init);
module_exit(ds1302_dev_exit);
MODULE_LICENSE("GPL");
为了方便,我把rtc-ds1302.c文件单独取出来,和ds1302_dev.c放一起,改名为ds1302_drv.c..。
简单修改编译后,分别加载ds1302_dev.ko,ds1302_drv.ko。从打印信息上看,probe函数已经被调用,但出错了:
[root@FriendlyARM /]# insmod ds1302_dev.ko
[root@FriendlyARM /]# insmod ds1302_drv.ko
ds1302_rtc_probe
rtc_reset pin = 0
rtc_iodata pin = 1
rtc_sclk pin = 2
iomap over
insmod: cannot insert 'ds1302_drv.ko': No such device
ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
return -ENODEV;
现在添加在适合S3C2440的一些代码:
在文件的开头 有这样的定义:
//#define RTC_RESET 0x1000
//#define RTC_IODATA 0x0800
//#define RTC_SCLK 0x0400
#ifdef CONFIG_SH_SECUREEDGE5410
#include
#define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
#define get_dp() SECUREEDGE_READ_IOPORT()
#else
...........
#endif
//#define RTC_RESET 0x1000
//#define RTC_IODATA 0x0800
//#define RTC_SCLK 0x0400
#ifdef CONFIG_SH_SECUREEDGE5410
#include
#define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
#define get_dp() SECUREEDGE_READ_IOPORT()
#else
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
unsigned int rtc_pinmap[16]={0x0001,0x0002,0x0004,0x0008,
0x0010,0x0020,0x0040,0x0080,
0x0100,0x0200,0x0400,0x0800,
0x1000,0x2000,0x4000,0x8000,};
static unsigned int RTC_RESET;
static unsigned int RTC_IODATA;
static unsigned int RTC_SCLK;
static unsigned int rtc_reset_num;
static unsigned int rtc_iodata_num;
static unsigned int rtc_sclk_num;
#define set_dp(x) *gpio_dat=(x)
#define get_dp() *gpio_dat
static void write_io(void)
{
*gpio_con &= ~(0x3<<(rtc_iodata_num)*2);
*gpio_con |= (0x1<<(rtc_iodata_num)*2);
}
static void read_io(void)
{
*gpio_con &= ~(0x3<<(rtc_iodata_num)*2);
}
#endif
在ds1302的probe函数中首先要ioremap寄存器地址,故要从ds1302_dev.c中获取相关寄存器配置:
static int __init ds1302_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
//====================================================
struct resource *res;
int k =0;
printk("ds1302_rtc_probe\n");
/* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio_con = ioremap(res->start, res->end - res->start + 1);
gpio_dat = gpio_con + 1;
while ((res = platform_get_resource(pdev, IORESOURCE_IO, k))) {
if(strcmp(res->name,"ds1302_rst") == 0)
{
rtc_reset_num = res->start;
RTC_RESET = rtc_pinmap[rtc_reset_num];
printk("rtc_reset pin = %d\n",rtc_reset_num);
}
else if (strcmp(res->name,"ds1302_io") == 0)
{
rtc_iodata_num = res->start;
RTC_IODATA= rtc_pinmap[rtc_iodata_num];
printk("rtc_iodata pin = %d\n",rtc_iodata_num);
}
else if (strcmp(res->name,"ds1302_clk") == 0)
{
rtc_sclk_num = res->start;
RTC_SCLK = rtc_pinmap[rtc_sclk_num];
printk("rtc_sclk pin = %d\n",rtc_sclk_num);
}
k++;
}
/* 配置为输出 */
*gpio_con &= ~((0x3<<(rtc_reset_num*2))|(0x3<<(rtc_iodata_num*2))|\
(0x3<<(rtc_sclk_num*2)));
*gpio_con |= ((0x1<<(rtc_reset_num*2))|(0x1<<(rtc_iodata_num*2))|\
(0x1<<(rtc_sclk_num*2)));
printk("iomap over \n");
//=====================================================
........
}
if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
return -ENODEV;
因为之前仔细的看了DS1302的相关操作,很容易就发现了代码的一个bug,在写数据前,应该去掉DS1302的写保护功能(在CONTROL寄存器里写0x00)。
定义RTC_ADDR_CONTROL,参照代码其他寄存器的宏定义,并查看1302的datasheet,应该这样定义:
#define RTC_ADDR_CONTROL 0x07
#define RTC_CMD_READ 0x81 /* Read command */
#define RTC_CMD_WRITE 0x80 /* Write command */
#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */
#define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */
#define RTC_ADDR_YEAR 0x06 /* Address of year register */
#define RTC_ADDR_DAY 0x05 /* Address of day of week register */
#define RTC_ADDR_MON 0x04 /* Address of month register */
#define RTC_ADDR_DATE 0x03 /* Address of day of month register */
#define RTC_ADDR_HOUR 0x02 /* Address of hour register */
#define RTC_ADDR_MIN 0x01 /* Address of minute register */
#define RTC_ADDR_SEC 0x00 /* Address of second register */
/* Reset */
set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
/* Write a magic value to the DS1302 RAM, and see if it sticks. */
ds1302_writebyte(RTC_ADDR_CONTROL,0x00); //关闭写保护
ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
return -ENODEV;
[root@FriendlyARM /]# insmod ds1302_drv.ko
ds1302_rtc_probe
rtc_reset pin = 0
rtc_iodata pin = 1
rtc_sclk pin = 2
iomap over
rtc-ds1302 rtc-ds1302: rtc core: registered ds1302 as rtc1
[root@FriendlyARM /]# insmod ds1302_drv.ko
后面的操作应该就没啥问题了。
注意:在读写DS302时要注意IO方向的切换。
在写DS1302寄存器时,应该先取消写保护,写完数据,使能写保护功能(ds1302_writebyte(RTC_ADDR_CONTROL,0x80);)。