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 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 <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);//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<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 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 <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 modulewritew(tmp & ~(0x01<<6), rtc_base + 0x40);writel(0x83E70B13, rtc_base + 0x6C);//KICK0R.Write Project Disablewritel(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 modulewritew(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);//STSif ((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.验证
重启,检查系统时间和硬件时间是不是实时的。

AM437x——RTC驱动相关推荐

  1. linux下测试RTC驱动相关的命令date和hwclock常见用法简介

    之前对Linux下面时间相关的内容,一无所知,第一次见到hwclock,不知为何物,也没找到解释清楚的帖子.故此整理一下,简单介绍Linux下验证rtc驱动是否工作正常,相关的的命令:date和hwc ...

  2. rtems 4.11 RTC驱动 (arm, beagle)

    RTC驱动的框架在 c/src/lib/libbsp/shared/tod.c 中,大部分功能都已经实现了,入口函数是 rtc_initialize(),BSP要实现的东西非常少. beagle的实现 ...

  3. Linux RTC 驱动实验

    目录 Linux 内核RTC 驱动简介 I.MX6U 内部RTC 驱动分析 RTC 时间查看与设置 RTC 也就是实时时钟,用于记录当前系统时间,对于Linux 系统而言时间是非常重要的,就和我们使用 ...

  4. linux RTC 驱动模型分析

    linux RTC 驱动模型分析 RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间.RTC因为是电池供电的,所以掉电后时间不丢失.Linux内核把RTC用作&quo ...

  5. imx6ul rtc 驱动修改

    imx6ul rtc 驱动修改 删除自带rtc 增加rx8010J驱动 修改modules.tar.bz2 修改启动脚本 hwclock 说明 删除自带rtc 增加rx8010J驱动 由于硬件问题,将 ...

  6. RK3399外设驱动之RTC驱动(二):hym8563驱动

    RK3399外设驱动之RTC驱动(二):hym8563驱动 文章目录 RK3399外设驱动之RTC驱动(二):hym8563驱动 hym8563硬件相关 注册驱动 probe函数 hym8563_in ...

  7. ESP32设备驱动-DS3231实时时钟(RTC)驱动

    DS3231实时时钟(RTC)驱动 1.DS3231介绍 DS3231 是一款低成本.极其精确的 I2C 实时时钟 (RTC),具有集成的温度补偿晶体振荡器 (TCXO) 和晶体. 该设备包含电池输入 ...

  8. ESP8266-Arduino编程实例-PCF8563实时时钟(RTC)驱动

    PCF8563实时时钟(RTC)驱动 1.PCF8563介绍 PCF8563 是针对低功耗优化的 CMOS 实时时钟 (RTC) 和日历. 还提供了可编程时钟输出.中断输出和低电压检测器. 所有地址和 ...

  9. 【正点原子Linux连载】第六十章 Linux RTC驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

最新文章

  1. 怎样把字符1变成数字1
  2. MySQL优化篇:执行计划explain中key_len计算方式
  3. C++双目/单目运算符的重载
  4. u-boot移植第二弹——移植2012.10u-boot到RealARM210 cortex-A8开发板
  5. 新浪微博api(.net)时间格式问题
  6. react-native 打包apk 并解决 图片 不出现问题
  7. flink(一个流处理,一个批处理)
  8. NETBACKUP error 90
  9. redis db0 到 db15_深入剖析Redis系列: Redis集群模式搭建与原理详解
  10. Excel成神之道-005-用countif统计客人第几次进店
  11. 画基因结构图 gggenes 用法
  12. Java小游戏练习---超级玛丽代码实现
  13. 房东拿租金去还房贷是天经地义的嘛
  14. Linux九阴真经之大伏魔拳残卷4 nginx(模型,安装配置,模块)
  15. Salt Pepper—The Art of Illustrating Texture
  16. meego linux 双系统,安装MeeGo和Windows 7双系统的方法
  17. R语言进阶之3:数据汇总/透视/提炼
  18. 安卓电视机(此处用小米电视机)远程连接电脑
  19. 【程序设计】日期和时间
  20. 36岁失业了,我该何去何从怎么选择?

热门文章

  1. macbook视频格式转换_mac视频格式转换怎么操作?如何将视频转换成mac能播放的格式?...
  2. python中省略号三个点(...)的作用
  3. 前端模块化(CommenJS规范、ES6规范)
  4. 【华为云技术分享】如何快速实现鲲鹏弹性云服务器Node.js部署和高可用性?
  5. windows生成App Store上架所需的各尺寸截图的方法
  6. 图像处理:实现图片镜像(基于python)
  7. 【pen200-lab】10.11.1.115
  8. 简述数据中心网络的特点,数据中心网络规划设计
  9. ASP是什么?ASP初识
  10. 计算机系统c是什么意思,电脑C盘显示红条是什么意思?用的是Visast系统刚才点击计算机 爱问知识人...