AM437x——RTC驱动

版权声明:本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,欢迎转载,但转载请注明来自hceng blog(www.hceng.cn),并保持转载后文章内容的完整。本人保留所有版权相关权利。 https://blog.csdn.net/hceng_linux/article/details/89913904

CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2017/08/27/AM437x——RTC驱动/#more

本文主要记录AM437X驱动的RTC。包含一个不标准的RTC驱动、一个还算有点标准的RTC驱动,以及正常的测试方式。


0.本次关于驱动的新收获

写RTC驱动的时候,我先尝试的按标准的RTC框架来,写着写着,我想试试之前的一个猜想。
理论上任何字符驱动,我都可以通过填充file_operations里面的函数,实现对硬件的操作控制。
也就是说,写完裸机程序,按之前LED那套标准的字符驱动框架去写驱动,在应用层通过open()等函数去操作/dev/下的设备,是万能的。
实际上RTC驱动也是这样做的,但因为RTC的特殊性,内核提供的是rtc_class_ops这个结构体,而不是file_operations。正常所需做的就是去填充rtc_class_ops的函数,然后注册等。
想想这两个的区别,前面万能那个,应用层就没那么好受了,不通用,比如使用hwclock是不能调用到驱动函数的,因此需要自己去实现去RTC的访问,就像访问LED驱动一样。
后面标准RTC那个,其最后的实质、原理是一样的,只是提供了统一的框架,增强了通用性。

1.不标准的RTC驱动

1.1入口函数和出口函数

先是入口函数,在insmod驱动的时候调用,
分配了主设备号,注册了字符设备驱动,创建了个类,使用cdev机制,自动在/dev/目录下创建设备,应用层就是通过对这个设备操作,调用驱动实现对硬件的操作。
再申请了内存,映射了寄存器地址,后面对这些映射出来的寄存器操作,就实现对硬件层的寄存器操作。
{% codeblock lang:c%}
static int rtc_drv_init(void)
{
dev_t devid;

printk(KERN_INFO"%s OK.\n",__func__);

if(alloc_chrdev_region(&devid, 0, TI_RTC_CNT, "ti_rtc") < 0)
{
    printk(KERN_INFO"%s ERROR.\n",__func__);
    goto error;
}

major = MAJOR(devid);

cdev_init(&rtc_cdev, &rtc_fops);        
cdev_add(&rtc_cdev, devid, TI_RTC_CNT);   

rtc_cls = class_create(THIS_MODULE, "ti_rtc");

device_create(rtc_cls, NULL, MKDEV(major, 0), NULL, "ti_rts0"); 

PRCM_CM_RTC_CLKSTCTRL = ioremap(0x44DF8500+0x00, 0x04*1);
PRCM_CM_RTC_CLKCTRL   = ioremap(0x44DF8500+0x20, 0x04*1);

RTCSS_BASE            = ioremap(0x44E3E000, 0xA0);
RTCSS_SECONDS         = RTCSS_BASE + 0;      
RTCSS_MINUTES         = RTCSS_BASE + 1; 
RTCSS_HOURS           = RTCSS_BASE + 2;  
RTCSS_DAYS            = RTCSS_BASE + 3;
RTCSS_WEEKS           = RTCSS_BASE + 4; 
RTCSS_MONTHS          = RTCSS_BASE + 5;
RTCSS_YEARS           = RTCSS_BASE + 6;  
RTCSS_ALARM_SECONDS   = RTCSS_BASE + 8;  
RTCSS_ALARM_MINUTES   = RTCSS_BASE + 9; 
RTCSS_ALARM_HOURS     = RTCSS_BASE + 10;
RTCSS_ALARM_DAYS      = RTCSS_BASE + 11; 
RTCSS_ALARM_MONTHS    = RTCSS_BASE + 12; 
RTCSS_ALARM_YEARS     = RTCSS_BASE + 13;      
RTCSS_CTRL            = RTCSS_BASE + 15; 
RTCSS_OSC             = RTCSS_BASE + 20;    
RTCSS_KICK0R          = RTCSS_BASE + 25;      
RTCSS_KICK1R          = RTCSS_BASE + 26;      

error:
unregister_chrdev_region(MKDEV(major, 0), TI_RTC_CNT);

return 0;

}
{% endcodeblock %}

然后是出口函数,注册做什么,这里就反过来做什么。
清除设备,清除类,注销字符设备,释放映射的内存。
{% codeblock lang:c%}
static void rtc_drv_exit(void)
{
unsigned i;
printk(KERN_INFO"%s OK.\n",func);

for(i=0;i<TI_RTC_CNT;i++)
{
    device_destroy(rtc_cls,  MKDEV(major, i));	
}
class_destroy(rtc_cls);
cdev_del(&rtc_cdev);
unregister_chrdev(major, "ti_rtc"); 

iounmap(PRCM_CM_RTC_CLKSTCTRL); 
iounmap(PRCM_CM_RTC_CLKCTRL);   
iounmap(RTCSS_BASE);            

}
{% endcodeblock %}

修饰下入口函数和出口函数,让这两个普通函数,能够通过insmod加载的时候被调用。
以及对该添加版权信息,驱动信息等。
{% codeblock lang:c%}
module_init(rtc_drv_init);
module_exit(rtc_drv_exit);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“TI am437x board rtc drvice”);
MODULE_ALIAS(“character device:ti_rtc”);
MODULE_VERSION(“V1.0”);
{% endcodeblock %}

1.2填充构造函数

这里添加构造函数,需要什么,加什么,加了之后,再去实现,应用层就是调用到这些函数的功能。
这里只打开设备、读取和设置时间操作。
{% codeblock lang:c%}
static struct file_operations rtc_fops = {
.owner = THIS_MODULE,
.open = rtc_drv_open,
.read = rtc_drv_read_time,
.write = rtc_drv_set_time,
};
{% endcodeblock %}

扫描二维码关注公众号,回复: 6159324 查看本文章

1.3实现构造函数##

现在去实现构造函数。这部分和裸机的操作是一摸一样的,在open()函数里进行初始化,在read()函数里对寄存器(映射后的)进行读取,传输给应用层。
{% codeblock lang:c%}
struct rtc_struct {
int year;
int month;
//int week;
int day;
int hour;
int minute;
int second;
};

static int rtc_drv_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO"%s OK.\n",func);

*PRCM_CM_RTC_CLKCTRL   &= ~(0x03<<0);
*PRCM_CM_RTC_CLKCTRL   |=  (0x01<<1);
*PRCM_CM_RTC_CLKSTCTRL &= ~(0x03<<0);

*RTCSS_CTRL &= ~(0x01<<6);

*RTCSS_KICK0R = (0x83E70B13);
*RTCSS_KICK1R = (0x95A4F1E0);

*RTCSS_OSC  &= ~(0x01<<3);
*RTCSS_OSC  |=  (0x01<<6);
*RTCSS_CTRL |=  (0x01<<0);

return 0;     

}

static ssize_t rtc_drv_read_time(struct file *file, char __user *user_buf, size_t size, loff_t *ppos)
{
struct rtc_struct rtc_time;

printk(KERN_INFO"%s OK.\n",__func__);  

rtc_time.year   = (((*RTCSS_YEARS   & (0x03<<4))>>4)*10 + (*RTCSS_YEARS   & (0x0F<<0)));
rtc_time.month  = (((*RTCSS_MONTHS  & (0x07<<4))>>4)*10 + (*RTCSS_MONTHS  & (0x0F<<0)));
rtc_time.day    = (((*RTCSS_DAYS    & (0x07<<4))>>4)*10 + (*RTCSS_DAYS    & (0x0F<<0)));
rtc_time.hour   = (((*RTCSS_HOURS   & (0x03<<4))>>4)*10 + (*RTCSS_HOURS   & (0x0F<<0)));
rtc_time.minute = (((*RTCSS_MINUTES & (0x07<<4))>>4)*10 + (*RTCSS_MINUTES & (0x0F<<0)));
rtc_time.second = (((*RTCSS_SECONDS & (0x07<<4))>>4)*10 + (*RTCSS_SECONDS & (0x0F<<0)));

copy_to_user(user_buf, &rtc_time, sizeof(rtc_time)); 

return 0;     

}
{% endcodeblock %}

接收应用层的数据,写入寄存器(映射后的),完成对RTC的设置。和裸机的操作,是一摸一样的。
{% codeblock lang:c%}
static ssize_t rtc_drv_set_time(struct file *file, const char __user *user_buf, size_t count, loff_t * ppos)
{
struct rtc_struct rtc_time;

printk(KERN_INFO"%s OK.\n",__func__);

if(count != sizeof(rtc_time)){
    printk(KERN_INFO"write count != %d.\n",sizeof(rtc_time)); 
    return 1;
}

if (copy_from_user(&rtc_time, user_buf, count))
    return -EFAULT;

*RTCSS_CTRL &= ~(0x01<<0);//stop

if((rtc_time.year-2000) > 99 || (rtc_time.year-2000) < 0)
    goto err;

if(rtc_time.month > 12 || rtc_time.month < 0)
    goto err;
*RTCSS_MONTHS = ((rtc_time.month/10) << 4) | ((rtc_time.month%10) << 0);

if(rtc_time.day > 32 || rtc_time.day < 0)
    goto err;
*RTCSS_DAYS = ((rtc_time.day/10) << 4) | ((rtc_time.day%10) << 0);	

if(rtc_time.hour > 23 || rtc_time.hour < 0)
    goto err;
*RTCSS_HOURS = ((rtc_time.hour/10) << 4) | ((rtc_time.hour%10) << 0);

if(rtc_time.minute > 59 || rtc_time.minute < 0)
    goto err;
*RTCSS_MINUTES = ((rtc_time.minute/10) << 4) | ((rtc_time.minute%10) << 0);

if(rtc_time.second > 59 || rtc_time.second < 0)
    goto err;
*RTCSS_SECONDS = ((rtc_time.second/10) << 4) | ((rtc_time.second%10) << 0);

*RTCSS_CTRL |= (0x01<<0);//start

return 0; 

err:
printk(KERN_INFO"rtc_drv_set_time err.\n");

return 1;  

}
{% endcodeblock %}

1.4完整驱动代码##

{% codeblock lang:c [rtc_drv.c] https://github.com/hceng/am437x/blob/master/drive/2th_rtc/v1.0/rtc_drv.c %}
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/cdev.h>

#define TI_RTC_CNT 1

int major;
static struct cdev rtc_cdev;
static struct class *rtc_cls;

static volatile unsigned long *PRCM_CM_RTC_CLKCTRL = NULL;
static volatile unsigned long *PRCM_CM_RTC_CLKSTCTRL = NULL;
static volatile unsigned long *RTCSS_BASE = NULL;
static volatile unsigned long *RTCSS_CTRL = NULL;
static volatile unsigned long *RTCSS_KICK0R = NULL;
static volatile unsigned long *RTCSS_KICK1R = NULL;
static volatile unsigned long *RTCSS_OSC = NULL;
static volatile unsigned long *RTCSS_YEARS = NULL;
static volatile unsigned long *RTCSS_MONTHS = NULL;
static volatile unsigned long *RTCSS_WEEKS = NULL;
static volatile unsigned long *RTCSS_DAYS = NULL;
static volatile unsigned long *RTCSS_HOURS = NULL;
static volatile unsigned long *RTCSS_MINUTES = NULL;
static volatile unsigned long *RTCSS_SECONDS = NULL;
static volatile unsigned long *RTCSS_ALARM_YEARS = NULL;
static volatile unsigned long *RTCSS_ALARM_MONTHS = NULL;
static volatile unsigned long *RTCSS_ALARM_DAYS = NULL;
static volatile unsigned long *RTCSS_ALARM_HOURS = NULL;
static volatile unsigned long *RTCSS_ALARM_MINUTES = NULL;
static volatile unsigned long *RTCSS_ALARM_SECONDS = NULL;

struct rtc_struct {
int year;
int month;
//int week;
int day;
int hour;
int minute;
int second;
};

static int rtc_drv_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO"%s OK.\n",func);

*PRCM_CM_RTC_CLKCTRL   &= ~(0x03<<0);
*PRCM_CM_RTC_CLKCTRL   |=  (0x01<<1);
*PRCM_CM_RTC_CLKSTCTRL &= ~(0x03<<0);

*RTCSS_CTRL &= ~(0x01<<6);

*RTCSS_KICK0R = (0x83E70B13);
*RTCSS_KICK1R = (0x95A4F1E0);

*RTCSS_OSC  &= ~(0x01<<3);
*RTCSS_OSC  |=  (0x01<<6);
*RTCSS_CTRL |=  (0x01<<0);

return 0;     

}

static ssize_t rtc_drv_read_time(struct file *file, char __user *user_buf, size_t size, loff_t *ppos)
{
struct rtc_struct rtc_time;

printk(KERN_INFO"%s OK.\n",__func__);  

rtc_time.year   = (((*RTCSS_YEARS   & (0x03<<4))>>4)*10 + (*RTCSS_YEARS   & (0x0F<<0)));
rtc_time.month  = (((*RTCSS_MONTHS  & (0x07<<4))>>4)*10 + (*RTCSS_MONTHS  & (0x0F<<0)));
rtc_time.day    = (((*RTCSS_DAYS    & (0x07<<4))>>4)*10 + (*RTCSS_DAYS    & (0x0F<<0)));
rtc_time.hour   = (((*RTCSS_HOURS   & (0x03<<4))>>4)*10 + (*RTCSS_HOURS   & (0x0F<<0)));
rtc_time.minute = (((*RTCSS_MINUTES & (0x07<<4))>>4)*10 + (*RTCSS_MINUTES & (0x0F<<0)));
rtc_time.second = (((*RTCSS_SECONDS & (0x07<<4))>>4)*10 + (*RTCSS_SECONDS & (0x0F<<0)));

copy_to_user(user_buf, &rtc_time, sizeof(rtc_time)); 

return 0;     

}
static ssize_t rtc_drv_set_time(struct file *file, const char __user *user_buf, size_t count, loff_t * ppos)
{
struct rtc_struct rtc_time;

printk(KERN_INFO"%s OK.\n",__func__);

if(count != sizeof(rtc_time)){
    printk(KERN_INFO"write count != %d.\n",sizeof(rtc_time)); 
    return 1;
}

if (copy_from_user(&rtc_time, user_buf, count))
    return -EFAULT;

*RTCSS_CTRL &= ~(0x01<<0);//stop

if((rtc_time.year-2000) > 99 || (rtc_time.year-2000) < 0)
    goto err;

if(rtc_time.month > 12 || rtc_time.month < 0)
    goto err;
*RTCSS_MONTHS = ((rtc_time.month/10) << 4) | ((rtc_time.month%10) << 0);

if(rtc_time.day > 32 || rtc_time.day < 0)
    goto err;
*RTCSS_DAYS = ((rtc_time.day/10) << 4) | ((rtc_time.day%10) << 0);	

if(rtc_time.hour > 23 || rtc_time.hour < 0)
    goto err;
*RTCSS_HOURS = ((rtc_time.hour/10) << 4) | ((rtc_time.hour%10) << 0);

if(rtc_time.minute > 59 || rtc_time.minute < 0)
    goto err;
*RTCSS_MINUTES = ((rtc_time.minute/10) << 4) | ((rtc_time.minute%10) << 0);

if(rtc_time.second > 59 || rtc_time.second < 0)
    goto err;
*RTCSS_SECONDS = ((rtc_time.second/10) << 4) | ((rtc_time.second%10) << 0);

*RTCSS_CTRL |= (0x01<<0);//start

return 0; 

err:
printk(KERN_INFO"rtc_drv_set_time err.\n");

return 1;  

}

static struct file_operations rtc_fops = {
.owner = THIS_MODULE,
.open = rtc_drv_open,
.read = rtc_drv_read_time,
.write = rtc_drv_set_time,
};

static int rtc_drv_init(void)
{
dev_t devid;

printk(KERN_INFO"%s OK.\n",__func__);

if(alloc_chrdev_region(&devid, 0, TI_RTC_CNT, "ti_rtc") < 0)
{
    printk(KERN_INFO"%s ERROR.\n",__func__);
    goto error;
}

major = MAJOR(devid);

cdev_init(&rtc_cdev, &rtc_fops);        
cdev_add(&rtc_cdev, devid, TI_RTC_CNT);   

rtc_cls = class_create(THIS_MODULE, "ti_rtc");

device_create(rtc_cls, NULL, MKDEV(major, 0), NULL, "ti_rts0"); 

PRCM_CM_RTC_CLKSTCTRL = ioremap(0x44DF8500+0x00, 0x04*1);
PRCM_CM_RTC_CLKCTRL   = ioremap(0x44DF8500+0x20, 0x04*1);

RTCSS_BASE            = ioremap(0x44E3E000, 0xA0);
RTCSS_SECONDS         = RTCSS_BASE + 0;      
RTCSS_MINUTES         = RTCSS_BASE + 1; 
RTCSS_HOURS           = RTCSS_BASE + 2;  
RTCSS_DAYS            = RTCSS_BASE + 3;
RTCSS_WEEKS           = RTCSS_BASE + 4; 
RTCSS_MONTHS          = RTCSS_BASE + 5;
RTCSS_YEARS           = RTCSS_BASE + 6;  
RTCSS_ALARM_SECONDS   = RTCSS_BASE + 8;  
RTCSS_ALARM_MINUTES   = RTCSS_BASE + 9; 
RTCSS_ALARM_HOURS     = RTCSS_BASE + 10;
RTCSS_ALARM_DAYS      = RTCSS_BASE + 11; 
RTCSS_ALARM_MONTHS    = RTCSS_BASE + 12; 
RTCSS_ALARM_YEARS     = RTCSS_BASE + 13;      
RTCSS_CTRL            = RTCSS_BASE + 15; 
RTCSS_OSC             = RTCSS_BASE + 20;    
RTCSS_KICK0R          = RTCSS_BASE + 25;      
RTCSS_KICK1R          = RTCSS_BASE + 26;      

error:
unregister_chrdev_region(MKDEV(major, 0), TI_RTC_CNT);

return 0;

}

static void rtc_drv_exit(void)
{
unsigned i;
printk(KERN_INFO"%s OK.\n",func);

for(i=0;i<TI_RTC_CNT;i++)
{
    device_destroy(rtc_cls,  MKDEV(major, i));	
}
class_destroy(rtc_cls);
cdev_del(&rtc_cdev);
unregister_chrdev(major, "ti_rtc"); 

iounmap(PRCM_CM_RTC_CLKSTCTRL); 
iounmap(PRCM_CM_RTC_CLKCTRL);   
iounmap(RTCSS_BASE);            

}

module_init(rtc_drv_init);
module_exit(rtc_drv_exit);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“TI am437x board rtc drvice”);
MODULE_ALIAS(“character device:ti_rtc”);
MODULE_VERSION(“V1.0”);
{% endcodeblock %}

1.5测试程序及效果

{% codeblock lang:c [rtc_app.c] https://github.com/hceng/am437x/blob/master/drive/2th_rtc/v1.0/rtc_app.c %}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define msleep(x) usleep(x*1000)

/*

  • ./rtc_app w 2017 08 25 18 36 20
  • ./rtc_app r [times]
    */
    struct rtc_struct {
    int year;
    int month;
    //int week;
    int day;
    int hour;
    int minute;
    int second;
    };
    struct rtc_struct set_time, rtc_time;

int main(int argc, char **argv)
{
int fd;
int i;

fd = open("/dev/ti_rts0", O_RDWR);
if (fd < 0)
{
    printf("can't open /dev/ti_rts0.\n");
    return 0;
}

if(strcmp(argv[1], "w") == 0 && argc == 8)
{
    set_time.year   = atoi(argv[2]);
    set_time.month  = atoi(argv[3]);
    set_time.day    = atoi(argv[4]);
    set_time.hour   = atoi(argv[5]);
    set_time.minute = atoi(argv[6]);
    set_time.second = atoi(argv[7]);
    
    write(fd, &set_time, sizeof(set_time));  
    printf("write ok\n");
}
else if(strcmp(argv[1], "r") == 0)
{
    if(argv[2] == NULL) 
        i = 999;
    else 
        i = atoi(argv[2]);
    
    do{
        read(fd, &rtc_time, sizeof(rtc_time));
        printf("\n\rcurrent_time is:\n\r\%d-%d-%d %d:%d:%d\n\r",\
        rtc_time.year+2000,rtc_time.month,rtc_time.day,rtc_time.hour,rtc_time.minute,rtc_time.second);
        printf("read ok\n");
        msleep(1000);  
        i--;
    }
    while(i);
}

close(fd);

return 0;

}
{% endcodeblock %}


这里除了年,其它都还OK,估计是和系统的一些设置冲突了。

2.还算有点标准的RTC驱动

本来计划完整的写好RTC驱动的,实际过程中发现ti官方内核的RTC不能关,关了无法正常启动。经验不足,又无法解决这个问题,只能在保持他远样的基础上,能写成什么算什么。大部分框架也算写了,剩下的感觉应该也不难了。
这次用的设备驱动模型来写的。由rtc_dev.c和rtc_drv.c组成。

2.1rtc_dev.c

首先是在rtc_dev.c中注册平台设备,并设置硬件资源。
RTC所需的硬件资源教少,主要是RTC寄存器和中断。
{% codeblock lang:c %}
static struct resource am437x_rtc_resource[] = {
[0] = {
.name = “RTCSS”,
.start = 0x44E3E000,
.end = 0x44E3EFFF,
.flags = IORESOURCE_MEM,
},

[1] = {  
    .name  = "RTCINT",
    .start = 107,  
    .end   = 107, 
    .flags = IORESOURCE_IRQ,//107
},	

[2] = { 
    .name  = "RTCALARMINT",
    .start = 108,  
    .end   = 108, 
    .flags = IORESOURCE_IRQ,//108
},

};
{% endcodeblock %}

2.2rtc_drv.c

rtc_drv.c开始要做的也差不多,注册平台设备。然后两个名字相同,匹配成功后调用probe()函数。
这里的probe()先获取rtc_dev.c里面的中断和RTC物理基地址的资源。随即映射RTC基地址。同时初始化RTC。
然后注册RTC设备devm_rtc_device_register().
这里绑定的是rtc_class_ops,而不是之前的file_operations。这里的rtc_class_ops是为RTC“量身定做”的操作函数,更设备RTC设备。devm_rtc_device_register()里面的操作也是注册字符设备那一套,本质还是一样的。
{% codeblock lang:c %}
static struct rtc_class_ops am437x_rtc_ops = {
.open = rtc_drv_open,
.release = rtc_drv_release,
.read_time = rtc_drv_read_time,
.set_time = rtc_drv_set_time,
.read_alarm = rtc_drv_read_alarm,
.set_alarm = rtc_drv_set_alarm,
.proc = rtc_drv_proc,
.alarm_irq_enable = rtc_drv_alarm_irq_enable,
};

static void am437x_rtc_enable(struct platform_device *pdev, int en)
{
void __iomem *rtc_base = am437x_rtc_base;

unsigned int tmp;

if (am437x_rtc_base == NULL)
    return;

if (en) {
    
    /* Enable the clock/module so that we can access the registers */
    pm_runtime_get_sync(&pdev->dev);
    
    //rtc init.
    tmp = readb(rtc_base + 0x40);//CTRL.Enable the RTC module
    writew(tmp & ~(0x01<<6), rtc_base + 0x40);

    writel(0x83E70B13, rtc_base + 0x6C);//KICK0R.Write Project Disable
    writel(0x95A4F1E0, rtc_base + 0x70);//KICK1R

    tmp = readb(rtc_base + 0x54);//OSC.mode1:Internal clock 
    writew(tmp & ~(0x01<<3), rtc_base + 0x54);
    tmp = readb(rtc_base + 0x54);
    writew(tmp | (0x01<<6), rtc_base + 0x54);
    
    tmp = readb(rtc_base + 0x40);//run.
    writew(tmp | (0x01<<0), rtc_base + 0x40);
} 
else {
    tmp = readb(rtc_base + 0x40);//stop.
    writew(tmp & ~(0x01<<0), rtc_base + 0x40);

    tmp = readb(rtc_base + 0x40);//CTRL.Disable the RTC module
    writew(tmp | (0x01<<6), rtc_base + 0x40); 

    /* Disable the clock/module */
    pm_runtime_put_sync(&pdev->dev);
}

}

struct resource *am437x_rtc_mem;

static int am437x_rtc_probe(struct platform_device *pdev)
{
struct resource *res;

int ret;   

printk(KERN_INFO"%s OK.\n",__func__);

/* find the IRQs */
am437x_rtc_timer_irq = platform_get_irq(pdev, 0);
if (am437x_rtc_timer_irq < 0) {
    dev_err(&pdev->dev, "no irq for rtc tick\n");
    return am437x_rtc_timer_irq;
}

am437x_rtc_alarm_irq = platform_get_irq(pdev, 1);
if (am437x_rtc_alarm_irq < 0) {
    dev_err(&pdev->dev, "no irq for alarm\n");
    return am437x_rtc_alarm_irq;
}

dev_dbg(&pdev->dev, "am437x_rtc: tick irq %d, alarm irq %d\n",
    am437x_rtc_timer_irq, am437x_rtc_alarm_irq);
   
/*RTC*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(res == NULL){
    dev_err(&pdev->dev, "RTC:failed to get memory regin resource.\n");
    return -ENOENT;
}

am437x_rtc_mem = request_mem_region(res->start, res->end - res->start+1, pdev->name);
if (am437x_rtc_mem == NULL){
    dev_err(&pdev->dev, "RTC:failed to reserve memory region.\n");
    return -ENOENT;
    goto err_nores;
}

am437x_rtc_base = ioremap(res->start, res->end - res->start+1);
if (am437x_rtc_mem == NULL){
    dev_err(&pdev->dev, "RTC:failed ioremap.\n");
    return -EINVAL;
    goto err_nomap;
}

am437x_rtc_enable(pdev, 1);

rtc = devm_rtc_device_register(&pdev->dev, pdev->name,&am437x_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
    pr_debug("%s: can't register RTC device, err %ld\n",pdev->name, PTR_ERR(rtc));
    goto err_nortc;
}

return 0;

err_nortc:
am437x_rtc_enable(pdev, 0);
iounmap(am437x_rtc_base);

err_nomap:
release_resource(am437x_rtc_mem);

err_nores:
return ret;
}
{% endcodeblock %}

然后是填充rtc_class_ops里面的操作函数,open()本来计划用来申请中断的,但申请的时候说被占用了,还是之前没有去掉内核RTC的原因。这里就先屏蔽了。read_time()和set_time()里面还是读取/设置寄存器。alarm的也差不多,没有中断,就先搁置了。
{% codeblock lang:c %}
static int rtc_drv_open(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
int ret;

printk(KERN_INFO"%s OK.\n",__func__);

/*
if (devm_request_irq(&pdev->dev, am437x_rtc_timer_irq, rtc_irq, 0, dev_name(&rtc->dev), rtc)) {
pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",pdev->name, am437x_rtc_timer_irq);
goto fail0;
}
if ((am437x_rtc_timer_irq != am437x_rtc_alarm_irq) &&
(devm_request_irq(&pdev->dev, am437x_rtc_alarm_irq, rtc_irq, 0, dev_name(&rtc->dev), rtc))) {
pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n",pdev->name, am437x_rtc_alarm_irq);
goto fail0;
}
*/
return 0;

fail0:
return -EIO;
}

static int rtc_drv_release(struct device *dev)
{
printk(KERN_INFO"%s OK.\n",func);
return 0;
}

static int tm2bcd(struct rtc_time *tm)
{
if (rtc_valid_tm™ != 0)
return -EINVAL;

tm->tm_sec  = bin2bcd(tm->tm_sec);
tm->tm_min  = bin2bcd(tm->tm_min);
tm->tm_hour = bin2bcd(tm->tm_hour);
tm->tm_mday = bin2bcd(tm->tm_mday);

tm->tm_mon  = bin2bcd(tm->tm_mon + 1);

/* epoch == 1900 */
if (tm->tm_year < 100 || tm->tm_year > 199)
    return -EINVAL;
tm->tm_year = bin2bcd(tm->tm_year - 100);

return 0;

}

static void bcd2tm(struct rtc_time tm)
{
tm->tm_sec = bcd2bin(tm->tm_sec);
tm->tm_min = bcd2bin(tm->tm_min);
tm->tm_hour = bcd2bin(tm->tm_hour);
tm->tm_mday = bcd2bin(tm->tm_mday);
tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
/
epoch == 1900 */
tm->tm_year = bcd2bin(tm->tm_year) + 2000;
}

static void rtc_wait_not_busy(void)
{
int count = 0;
u8 status;

/* BUSY may stay active for 1/32768 second (~30 usec) */
for (count = 0; count < 50; count++) {
    status = readb(am437x_rtc_base + 0x44);//STS
    if ((status & (0x01<<0)) == 0)
        break;
    udelay(1);
}
/* now we have ~15 usec to read/write various registers */

}

static int rtc_drv_read_time(struct device *dev, struct rtc_time *rtc_t)
{
printk(KERN_INFO"%s OK.\n",func);

//local_irq_disable();
rtc_wait_not_busy();

rtc_t->tm_sec  = readb(am437x_rtc_base + 0x00);
rtc_t->tm_min  = readb(am437x_rtc_base + 0x04);
rtc_t->tm_hour = readb(am437x_rtc_base + 0x08);
rtc_t->tm_mday = readb(am437x_rtc_base + 0x0C);
rtc_t->tm_mon  = readb(am437x_rtc_base + 0x10);
rtc_t->tm_year = readb(am437x_rtc_base + 0x14);

//local_irq_enable();

bcd2tm(rtc_t);

printk("\n\rcurrent_time is:\n\r\%d-%d-%d %d:%d:%d\n\r",\
    rtc_t->tm_year,rtc_t->tm_mon,rtc_t->tm_mday,rtc_t->tm_hour,rtc_t->tm_min,rtc_t->tm_sec);

return 0;

}

static int rtc_drv_set_time(struct device *dev, struct rtc_time *rtc_t)
{
if (tm2bcd(rtc_t) < 0)
return -EINVAL;

//local_irq_disable();
rtc_wait_not_busy();

writeb(rtc_t->tm_sec, am437x_rtc_base + 0x00);
writeb(rtc_t->tm_min, am437x_rtc_base + 0x04);
writeb(rtc_t->tm_hour, am437x_rtc_base + 0x08);
writeb(rtc_t->tm_mday, am437x_rtc_base + 0x0C);
writeb(rtc_t->tm_mon, am437x_rtc_base + 0x10);
writeb(rtc_t->tm_year, am437x_rtc_base + 0x14);

//local_irq_enable();

return 0;

}

static int rtc_drv_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
printk(KERN_INFO"%s OK.\n",func);
return 0;
}
static int rtc_drv_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
printk(KERN_INFO"%s OK.\n",func);
return 0;
}

static int rtc_drv_proc(struct device *dev, struct seq_file *seq)
{
printk(KERN_INFO"%s OK.\n",func);
return 0;
}

static int rtc_drv_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
printk(KERN_INFO"%s OK.\n",func);
return 0;
}
{% endcodeblock %}

2.3相关代码及测试效果

rtc_dev.c
rtc_drv.c
在系统输入hwclock -f /dev/rtc1

3.正常的测试驱动

额,虽然没有正常调试出RTC,但还是记下正常调试的方法,万一以后用到了呢。

Linux中有两个时间,
一个是系统时间,通过命令date获取;
一个是硬件时间,通过命令hwclock获取;

RTC驱动的一般测试如下:
1.获取系统时间

date

如果时间正常,则进行第2步,如果不正常,修改系统时间为正常时间:

date 082316432017.00          //2017-08-23 16:43:00

2.获取硬件时间

hwclock

由于此处是RTC驱动第一次加载,还没设置正确的时间,所以此时显示的时间,多数是不正确的1970年。

3.同步硬件时间

hwclock -w

第一步设置好了正常的系统时间,现在将系统时间与硬件时间进行同步。

4.自动同步硬件时间
现在的系统时间和硬件时间是同步的,如果关机重启,系统时间将不会实时同步,而硬件时间是一直实时同步的,因此需要将硬件时间同步到系统时间。
修改启动脚本:etc/init.d/rcS,在最后一行加上:

/sbin/hwclock -s

4.验证
重启,检查系统时间和硬件时间是不是实时的。

猜你喜欢

转载自blog.csdn.net/hceng_linux/article/details/89913904