DS1302芯片在am335xD上的移植

基于总线驱动设备模型基本框架:

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的源码如下:

  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/fs.h>  
  4. #include <linux/init.h>  
  5. #include <linux/delay.h>  
  6. #include <asm/uaccess.h>  
  7. #include <asm/irq.h>  
  8. #include <mach/io.h>  
  9. #include <mach/regs-gpio.h>  
  10. #include <mach/hardware.h>  
  11. #include <linux/device.h>  
  12. #include <linux/platform_device.h>  
  13. #include <linux/cdev.h>  
  14.   
  15. static void ds1302_release(struct device * dev)  
  16. {  
  17. }  
  18. static struct resource ds1302_resource[] = {  
  19.     [0] = {  
  20.         .start = 0x56000050,  
  21.         .end   = 0x56000050 + 8 - 1,  
  22.         .flags = IORESOURCE_MEM,  
  23.     },  
  24.     [1] = {  
  25.         .name  = "ds1302_rst",  
  26.         .start = 0,  
  27.         .end   = 0,  
  28.         .flags = IORESOURCE_IO,  
  29.     },  
  30.     [2] = {  
  31.         .name  = "ds1302_io",  
  32.         .start = 1,  
  33.         .end   = 1,  
  34.         .flags = IORESOURCE_IO,  
  35.     },  
  36.     [3] = {  
  37.         .name  = "ds1302_clk",  
  38.         .start = 2,  
  39.         .end   = 2,  
  40.         .flags = IORESOURCE_IO,  
  41.     },  
  42.   
  43. };  
  44.   
  45.   
  46. static struct platform_device ds1302_device_driver = {  
  47.     .name         = "rtc-ds1302",  
  48.     .id       = -1,  
  49.     .num_resources    = ARRAY_SIZE(ds1302_resource),  
  50.     .resource     = ds1302_resource,  
  51.     .dev = {   
  52.         .release = ds1302_release,   
  53.     },  
  54. };  
  55.   
  56.   
  57.   
  58. static int ds1302_dev_init(void)  
  59. {  
  60.   
  61.     return platform_device_register(&ds1302_device_driver);  
  62.   
  63. }  
  64.   
  65.   
  66. static void ds1302_dev_exit(void)  
  67. {  
  68.   
  69.     platform_device_unregister(&ds1302_device_driver);  
  70.   
  71. }  
  72.   
  73.   
  74. module_init(ds1302_dev_init);  
  75. module_exit(ds1302_dev_exit);  
  76.   
  77. 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函数已经被调用,但出错了:

  1. [root@FriendlyARM /]# insmod ds1302_dev.ko  
  2. [root@FriendlyARM /]# insmod ds1302_drv.ko  
  3. ds1302_rtc_probe  
  4. rtc_reset pin = 0  
  5. rtc_iodata pin = 1  
  6. rtc_sclk pin = 2  
  7. iomap over   
  8. 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函数中有这样一句:

  1. ds1302_writebyte(RTC_ADDR_RAM0, 0x42);  
  2. if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)  
  3.     return -ENODEV;  

	ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
	if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
		return -ENODEV;

往DS1302的ram0空间写一个数据,再读出来,只有正确读到才会往下去注册设备,不然在这一步就退出了。


现在添加在适合S3C2440的一些代码:

在文件的开头 有这样的定义:

  1. //#define   RTC_RESET   0x1000  
  2. //#define   RTC_IODATA  0x0800  
  3. //#define   RTC_SCLK    0x0400  
  4.   
  5. #ifdef CONFIG_SH_SECUREEDGE5410  
  6. #include <mach/snapgear.h>  
  7. #define set_dp(x)   SECUREEDGE_WRITE_IOPORT(x, 0x1c00)  
  8. #define get_dp()    SECUREEDGE_READ_IOPORT()  
  9. #else  
  10. ...........  
  11. #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中添加支持自己平台的代码,为了保持源码的最小改动,我按照代码的原来框架定义了若干参数:

  1. //#define   RTC_RESET   0x1000  
  2. //#define   RTC_IODATA  0x0800  
  3. //#define   RTC_SCLK    0x0400  
  4.   
  5. #ifdef CONFIG_SH_SECUREEDGE5410  
  6. #include <mach/snapgear.h>  
  7. #define set_dp(x)   SECUREEDGE_WRITE_IOPORT(x, 0x1c00)  
  8. #define get_dp()    SECUREEDGE_READ_IOPORT()  
  9. #else  
  10. static volatile unsigned long *gpio_con;  
  11. static volatile unsigned long *gpio_dat;  
  12. unsigned int rtc_pinmap[16]={0x0001,0x0002,0x0004,0x0008,  
  13.                              0x0010,0x0020,0x0040,0x0080,  
  14.                              0x0100,0x0200,0x0400,0x0800,  
  15.                              0x1000,0x2000,0x4000,0x8000,};  
  16. static unsigned int RTC_RESET;  
  17. static unsigned int RTC_IODATA;  
  18. static unsigned int RTC_SCLK;  
  19.   
  20. static unsigned int rtc_reset_num;  
  21. static unsigned int rtc_iodata_num;  
  22. static unsigned int rtc_sclk_num;  
  23.   
  24.   
  25. #define set_dp(x)   *gpio_dat=(x)  
  26. #define get_dp()    *gpio_dat  
  27.   
  28. static void write_io(void)  
  29. {  
  30.     *gpio_con &= ~(0x3<<(rtc_iodata_num)*2);  
  31.     *gpio_con |=  (0x1<<(rtc_iodata_num)*2);  
  32.   
  33. }  
  34.   
  35. static void read_io(void)  
  36. {  
  37.     *gpio_con &= ~(0x3<<(rtc_iodata_num)*2);  
  38. }  
  39.   
  40. #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中获取相关寄存器配置:

  1. static int __init ds1302_rtc_probe(struct platform_device *pdev)  
  2. {  
  3.     struct rtc_device *rtc;   
  4. //====================================================  
  5.     struct resource     *res;  
  6.     int k =0;  
  7.     printk("ds1302_rtc_probe\n");  
  8. /* 根据platform_device的资源进行ioremap */  
  9.   res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  10.   gpio_con = ioremap(res->start, res->end - res->start + 1);  
  11.   gpio_dat = gpio_con + 1;  
  12.   
  13.   while ((res = platform_get_resource(pdev, IORESOURCE_IO, k))) {  
  14.       if(strcmp(res->name,"ds1302_rst") == 0)  
  15.       {  
  16.          rtc_reset_num = res->start;  
  17.          RTC_RESET = rtc_pinmap[rtc_reset_num];  
  18.          printk("rtc_reset pin = %d\n",rtc_reset_num);  
  19.            
  20.       }  
  21.       else if (strcmp(res->name,"ds1302_io") == 0)  
  22.       {  
  23.          rtc_iodata_num = res->start;  
  24.          RTC_IODATA= rtc_pinmap[rtc_iodata_num];  
  25.          printk("rtc_iodata pin = %d\n",rtc_iodata_num);  
  26.       }  
  27.       else if (strcmp(res->name,"ds1302_clk") == 0)  
  28.       {  
  29.          rtc_sclk_num = res->start;  
  30.          RTC_SCLK = rtc_pinmap[rtc_sclk_num];  
  31.          printk("rtc_sclk pin = %d\n",rtc_sclk_num);  
  32.       }  
  33.       k++;  
  34.   }  
  35.   
  36.     /* 配置为输出 */  
  37.     *gpio_con &= ~((0x3<<(rtc_reset_num*2))|(0x3<<(rtc_iodata_num*2))|\  
  38.                    (0x3<<(rtc_sclk_num*2)));  
  39.     *gpio_con |=  ((0x1<<(rtc_reset_num*2))|(0x1<<(rtc_iodata_num*2))|\  
  40.                    (0x1<<(rtc_sclk_num*2)));  
  41.   
  42.    printk("iomap over \n");  
  43. //=====================================================  
  44. ........  
  45. }  
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");
//=====================================================
........
}

编译加载后,发现还是错在

  1. if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)  
  2.         return -ENODEV;  
if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
		return -ENODEV;

这一步。

因为之前仔细的看了DS1302的相关操作,很容易就发现了代码的一个bug,在写数据前,应该去掉DS1302的写保护功能(在CONTROL寄存器里写0x00)。

定义RTC_ADDR_CONTROL,参照代码其他寄存器的宏定义,并查看1302的datasheet,应该这样定义:

  1. #define RTC_ADDR_CONTROL 0x07  
  2.   
  3. #define RTC_CMD_READ    0x81        /* Read command */  
  4. #define RTC_CMD_WRITE   0x80        /* Write command */  
  5.   
  6. #define RTC_ADDR_RAM0   0x20        /* Address of RAM0 */  
  7. #define RTC_ADDR_TCR    0x08        /* Address of trickle charge register */  
  8. #define RTC_ADDR_YEAR   0x06        /* Address of year register */  
  9. #define RTC_ADDR_DAY    0x05        /* Address of day of week register */  
  10. #define RTC_ADDR_MON    0x04        /* Address of month register */  
  11. #define RTC_ADDR_DATE   0x03        /* Address of day of month register */  
  12. #define RTC_ADDR_HOUR   0x02        /* Address of hour register */  
  13. #define RTC_ADDR_MIN    0x01        /* Address of minute register */  
  14. #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之前,取消写保护操作:

  1. /* Reset */  
  2. set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));  
  3.   
  4. /* Write a magic value to the DS1302 RAM, and see if it sticks. */  
  5. ds1302_writebyte(RTC_ADDR_CONTROL,0x00); //关闭写保护   
  6. ds1302_writebyte(RTC_ADDR_RAM0, 0x42);  
  7. if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)  
  8.     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;

编译,加载,此时:

  1. [root@FriendlyARM /]# insmod ds1302_drv.ko  
  2. ds1302_rtc_probe  
  3. rtc_reset pin = 0  
  4. rtc_iodata pin = 1  
  5. rtc_sclk pin = 2  
  6. iomap over   
  7. rtc-ds1302 rtc-ds1302: rtc core: registered ds1302 as rtc1  
  8. [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);)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gorlen-Boot

随手打赏,为知识买单

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值