内核版本: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 <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <mach/io.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/cdev.h> 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 <mach/snapgear.h> #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 <mach/snapgear.h> #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);)。