【Linux高级驱动】rtc驱动开发
【1.分层思想】
1.1 rtc-dev.c //设备接口层,功能:给用户提供接口
subsys_initcall(rtc_init); //module_init(rtc_init) //rtc/class.c
/*创建一个设备类*/
rtc_class = class_create(THIS_MODULE, "rtc");
/*初始化*/
rtc_init
rtc_dev_init
/*动态申请主设备号*/
alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
/*创建一个设备类*/
rtc_class = class_create(THIS_MODULE, "rtc");
/*初始化*/
rtc_init
rtc_dev_init
/*动态申请主设备号*/
alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
1.2 rtc-s3c.c //功能:操作硬件
module_init(s3c_rtc_init);
static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.id_table = s3c_rtc_driver_ids,
.driver = {
.name = "s3c-rtc",
.owner = THIS_MODULE,
},
};
s3c_rtc_init
platform_driver_register(&s3c_rtc_driver);
s3c_rtc_probe
/*1.获取中断资源*/
/*2.获取io内存资源*/
/*3.映射*/
/*4.使能RTC*/
/*5.注册*/
rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);
//device_create(class,parent,MKDEV,drvdata,name)
rtc->dev.parent = dev;
rtc->dev.class = rtc_class;
dev_set_name(&rtc->dev, "rtc%d", id); //指定设备文件的名字 /dev/rtc0 /dev/rtc1 /dev/rtc2 .....
rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc_dev_add_device(rtc);
cdev_add(&rtc->char_dev, rtc->dev.devt, 1)
static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.id_table = s3c_rtc_driver_ids,
.driver = {
.name = "s3c-rtc",
.owner = THIS_MODULE,
},
};
s3c_rtc_init
platform_driver_register(&s3c_rtc_driver);
s3c_rtc_probe
/*1.获取中断资源*/
/*2.获取io内存资源*/
/*3.映射*/
/*4.使能RTC*/
/*5.注册*/
rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);
//device_create(class,parent,MKDEV,drvdata,name)
rtc->dev.parent = dev;
rtc->dev.class = rtc_class;
dev_set_name(&rtc->dev, "rtc%d", id); //指定设备文件的名字 /dev/rtc0 /dev/rtc1 /dev/rtc2 .....
rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc_dev_add_device(rtc);
cdev_add(&rtc->char_dev, rtc->dev.devt, 1)
【为了能够读取到rtc的时间】
【一/添加驱动(driver/rtc)】
1.修改driver/rtc/目录下的Kconfig
vi linux-2.6.35.5/driver/rtc/Kconfig
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100(添加的东西)
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
interrupt rates from 1Hz to 64Hz for user programs, and
wakeup from Alarm.
The driver currently supports the common features on all the
S3C24XX range, such as the S3C2410, S3C2412, S3C2413, S3C2440
and S3C2442.
This driver can also be build as a module. If so, the module
will be called rtc-s3c.
tristate "Samsung S3C series SoC RTC"
depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100(添加的东西)
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
interrupt rates from 1Hz to 64Hz for user programs, and
wakeup from Alarm.
The driver currently supports the common features on all the
S3C24XX range, such as the S3C2410, S3C2412, S3C2413, S3C2440
and S3C2442.
This driver can also be build as a module. If so, the module
will be called rtc-s3c.
2.配置内核
make menuconfig
Device Drivers --->
<*> Real Time Clock ---> //class.c rtc-dev.c
<*> Samsung S3C series SoC RTCs //需要修改driver/rtc/Kconfig
<*> Real Time Clock ---> //class.c rtc-dev.c
<*> Samsung S3C series SoC RTCs //需要修改driver/rtc/Kconfig
3.资源添加
vi arch/arm/mach-s5pc100/Kconfig
config MACH_SMDKC100
bool "SMDKC100"
select CPU_S5PC100
select S3C_DEV_FB
select S3C_DEV_I2C1
select S3C_DEV_HSMMC
select S3C_DEV_HSMMC1
select S3C_DEV_HSMMC2
select S5PC100_SETUP_FB_24BPP
select S5PC100_SETUP_I2C1
select S5PC100_SETUP_SDHCI
select S3C_DEV_LED
select S3C_DEV_RTC //添加的代码
bool "SMDKC100"
select CPU_S5PC100
select S3C_DEV_FB
select S3C_DEV_I2C1
select S3C_DEV_HSMMC
select S3C_DEV_HSMMC1
select S3C_DEV_HSMMC2
select S5PC100_SETUP_FB_24BPP
select S5PC100_SETUP_I2C1
select S5PC100_SETUP_SDHCI
select S3C_DEV_LED
select S3C_DEV_RTC //添加的代码
vi arch/arm/mach-s5pc100/mach-smdkc100.c
static struct platform_device *smdkc100_devices[] __initdata = {
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_fb,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_hsmmc2,
&smdkc100_lcd_powerdev,
&s5pc100_device_iis0,
&s5pc100_device_ac97,
#ifdef CONFIG_DM9000
&s5pc100_device_dm9000,
#endif
&fsled_device,
&s3c_device_rtc,
};
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_fb,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_hsmmc2,
&smdkc100_lcd_powerdev,
&s5pc100_device_iis0,
&s5pc100_device_ac97,
#ifdef CONFIG_DM9000
&s5pc100_device_dm9000,
#endif
&fsled_device,
&s3c_device_rtc,
};
4.修改linux-2.6.35.5/arch/arm/mach-s5pc100/includ/mach/map.h
vi linux-2.6.35.5/arch/arm/mach-s5pc100/includ/mach/map.h
在其中添加
#define S3C_PA_RTC 0xEA300000
5.重新编译内核
测试:
1.编写测试程序,见rtc_test.c
2.运行测试程序
./rtc_test
Current RTC date/time is 0-0-2000, 00:00:00.
说明时间没有成功读取到.猜测:没有成功初始化硬件,导致不能成功读取到时间
<解决办法>
使能rtc模块的时钟,在rtc-s3c.c文件的probe函数中,在使能RTC之前添加如下代码
/*开始rtc时钟,使能rtc模块的时钟*/
rtc_clk=clk_get(&pdev->dev, "rtc");
clk_enable(rtc_clk);
rtc_clk=clk_get(&pdev->dev, "rtc");
clk_enable(rtc_clk);
2.现象:一直读出来的数据位0,也设置不进去,
原因:硬件问题.
【代码跟踪】
open
app: open("/dev/rtc0", O_RDONLY);
====================================
vfs: sys_open
...
...
rtc-dev.c struct file_operations rtc_dev_fops
.open = rtc_dev_open,
struct rtc_class_ops *ops = rtc->ops;
err = ops->open ? ops->open(rtc->dev.parent) : 0; //s3c_rtc_open
rtc-s3c.c s3c_rtc_open
request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);
request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);
====================================
vfs: sys_open
...
...
rtc-dev.c struct file_operations rtc_dev_fops
.open = rtc_dev_open,
struct rtc_class_ops *ops = rtc->ops;
err = ops->open ? ops->open(rtc->dev.parent) : 0; //s3c_rtc_open
rtc-s3c.c s3c_rtc_open
request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);
request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);
RTC_SET_TIME
app:ioctl(fd, RTC_SET_TIME, &time);
====================================
vfs: sys_ioctl
...
...
rtc-dev.c struct file_operations rtc_dev_fops
.unlocked_ioctl = rtc_dev_ioctl,
struct rtc_class_ops *ops = rtc->ops;
switch (cmd) {
case RTC_SET_TIME:
mutex_unlock(&rtc->ops_lock);
/*从用户空间获取要设置的时间*/
if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT;
return rtc_set_time(rtc, &tm);
if (rtc->ops->set_time)
err = rtc->ops->set_time(rtc->dev.parent, tm); //s3c_rtc_settime
}
rtc-s3c.c s3c_rtc_settime
int year = tm->tm_year - 100;
if (year < 0 || year >= 100) {
dev_err(dev, "rtc only supports 100 years\n");
return -EINVAL;
}
writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC);
writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN);
writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);
writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
====================================
vfs: sys_ioctl
...
...
rtc-dev.c struct file_operations rtc_dev_fops
.unlocked_ioctl = rtc_dev_ioctl,
struct rtc_class_ops *ops = rtc->ops;
switch (cmd) {
case RTC_SET_TIME:
mutex_unlock(&rtc->ops_lock);
/*从用户空间获取要设置的时间*/
if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT;
return rtc_set_time(rtc, &tm);
if (rtc->ops->set_time)
err = rtc->ops->set_time(rtc->dev.parent, tm); //s3c_rtc_settime
}
rtc-s3c.c s3c_rtc_settime
int year = tm->tm_year - 100;
if (year < 0 || year >= 100) {
dev_err(dev, "rtc only supports 100 years\n");
return -EINVAL;
}
writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC);
writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN);
writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);
writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
RTC_RD_TIME
app:ioctl(fd, RTC_RD_TIME, &time);
====================================
vfs: sys_ioctl
...
...
rtc-dev.c struct file_operations rtc_dev_fops
.unlocked_ioctl = rtc_dev_ioctl,
struct rtc_class_ops *ops = rtc->ops;
switch (cmd) {
case RTC_RD_TIME:
mutex_unlock(&rtc->ops_lock);
err = rtc_read_time(rtc, &tm);
memset(tm, 0, sizeof(struct rtc_time));
err = rtc->ops->read_time(rtc->dev.parent, tm); //s3c_rtc_gettime
if (err < 0)
return err;
if (copy_to_user(uarg, &tm, sizeof(tm)))
err = -EFAULT;
return err;
}
rtc-s3c.c s3c_rtc_gettime
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
rtc_tm->tm_year += 100;
rtc_tm->tm_mon -= 1;
====================================
vfs: sys_ioctl
...
...
rtc-dev.c struct file_operations rtc_dev_fops
.unlocked_ioctl = rtc_dev_ioctl,
struct rtc_class_ops *ops = rtc->ops;
switch (cmd) {
case RTC_RD_TIME:
mutex_unlock(&rtc->ops_lock);
err = rtc_read_time(rtc, &tm);
memset(tm, 0, sizeof(struct rtc_time));
err = rtc->ops->read_time(rtc->dev.parent, tm); //s3c_rtc_gettime
if (err < 0)
return err;
if (copy_to_user(uarg, &tm, sizeof(tm)))
err = -EFAULT;
return err;
}
rtc-s3c.c s3c_rtc_gettime
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
rtc_tm->tm_year += 100;
rtc_tm->tm_mon -= 1;
【linux设备驱动之rtc驱动开发】
@成鹏致远(wwwlllll@126.com)