基于总线驱动设备模型基本框架:
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");
#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");
整个文件代码很简单,设置硬件相关的配置,dev注册等等,在此不做分析。
为了方便,我把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
[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
查看源码 发现在probe函数中有这样一句:
- ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
- if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
- return -ENODEV;
ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
return -ENODEV;
往DS1302的ram0空间写一个数据,再读出来,只有正确读到才会往下去注册设备,不然在这一步就退出了。
现在添加在适合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
...........
#endif
显然我要在#else中添加支持自己平台的代码,为了保持源码的最小改动,我按照代码的原来框架定义了若干参数:
- //#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
//#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的IO口是个双向IO,对于2440的GPIO有个方向设置,我添加了write_io和read_io两个函数,用于对DS1302io口的设置。
在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");
- //=====================================================
- ........
- }
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 */
#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 */
并在写ram之前,取消写保护操作:
- /* 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;
/* 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
[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
发现这一步已经成功,1302的读写操作已经成功,注册了ds1302的设备。
后面的操作应该就没啥问题了。
注意:在读写DS302时要注意IO方向的切换。
在写DS1302寄存器时,应该先取消写保护,写完数据,使能写保护功能(ds1302_writebyte(RTC_ADDR_CONTROL,0x80);)。