嵌入式基础|AM437x——RTC驱动

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

}
{% endcodeblock %}
修饰下入口函数和出口函数,让这两个普通函数,能够通过insmod加载的时候被调用。
以及对该添加版权信息,驱动信息等。
{% codeblock lang:c%}
module_init(rtc_drv_init);
module_exit(rtc_drv_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng huangcheng.job@foxmail.com”);
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 %}
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); //stopif((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); //startreturn 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
#include
#include
#include
#include
#include
#include
#include
#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); //stopif((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); //startreturn 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

}
module_init(rtc_drv_init);
module_exit(rtc_drv_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng huangcheng.job@foxmail.com”);
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
#include
#include
#include
#include
#include
#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); //KICK1Rtmp = 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); }

}
【嵌入式基础|AM437x——RTC驱动】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.验证
重启,检查系统时间和硬件时间是不是实时的。

    推荐阅读