一. RTC定义
sun@ubuntu:/work/6410/kernel/linux-3.0.1/drivers/rtc$ tree
.
├── class.c //rtc_class类
├── hctosys.c //系统时钟相关的操作
├── interface.c //很多接口
├── rtc-core.h
├── rtc-dev.c //file_ops
├── rtc-lib.c //时间相关的操作
├── rtc-proc.c //rtc 中 proc相关的操作
├── rtc-s3c.c //s3c-rtc platform初始化 + class_ops
└── rtc-sysfs.c //rtc中sysfs相关的操作
1. s3c-rtc的定义
arch/arm/plat-samsung/dev-rtc.c中
-
static struct resource s3c_rtc_resource[] = {
-
[0] = {
-
.start = S3C_PA_RTC,
-
.end = S3C_PA_RTC + 0xff,
-
.flags = IORESOURCE_MEM, //控制寄存器
-
},
-
[1] = {
-
.start = IRQ_RTC_ALARM,
-
.end = IRQ_RTC_ALARM,
-
.flags = IORESOURCE_IRQ, //alarm中断
-
},
-
[2] = {
-
.start = IRQ_RTC_TIC,
-
.end = IRQ_RTC_TIC,
-
.flags = IORESOURCE_IRQ //TIC中断
-
}
-
};
-
-
struct platform_device s3c_device_rtc = {
-
.name = "s3c64xx-rtc",
-
.id = -1,
-
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
-
.resource = s3c_rtc_resource,
-
};
在arch/arm/mach-s3c64xx/mach-smdk6410.c中
-
static struct platform_device *smdk6410_devices[] __initdata = {
-
&s3c_device_rtc, //添加到设备列表中
-
};
-
-
arch/arm/mach-s3c64xx/mach-smdk6410.c
-
static void __init smdk6410_machine_init(void)
-
{
-
platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); //设备在此处注册
-
}
2. 系统控制rtc的开关
-
arch/arm/plat-samsung/clock.c
-
struct clk clk_p = {
-
.name = "pclk",
-
.id = -1,
-
.rate = 0,
-
.parent = NULL,
-
.ctrlbit = 0,
-
.ops = &clk_ops_def_setrate,
-
};
-
arch/arm/mach-s3c64xx/clock.c
-
static struct clk init_clocks_disable[] = {
-
{
-
.name = "rtc",
-
.id = -1,
-
.parent = &clk_p,
-
.enable = s3c64xx_pclk_ctrl, //控制是否启用rtc
-
.ctrlbit = S3C_CLKCON_PCLK_RTC, //#define S3C_CLKCON_PCLK_RTC (1<<6)
-
},
-
};
-
-
static int s3c64xx_pclk_ctrl(struct clk *clk, int enable) //控制是否启用rtc
-
{
-
return s3c64xx_gate(S3C_PCLK_GATE, clk, enable); //PCLK_GATE相应位置0--disable,反之enbale
-
}
-
-
void __init s3c64xx_register_clocks(unsigned long xtal, unsigned armclk_divlimit)
-
{
-
struct clk *clkp;
-
int ret;
-
int ptr;
-
-
armclk_mask = armclk_divlimit;
-
-
s3c24xx_register_baseclocks(xtal);
-
s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
-
-
s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
-
-
clkp = init_clocks_disable;
-
for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
-
ret = s3c24xx_register_clock(clkp);
-
(clkp->enable)(clkp, 0); //调用每个设备的enable函数
-
}
-
-
s3c24xx_register_clocks(clks1, ARRAY_SIZE(clks1));
-
s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
-
s3c_pwmclk_init();
-
}
二. RTC驱动
1. 在driver/rtc/rtc-s3c.c中,匹配设备与设备的驱动
-
static struct platform_device_id s3c_rtc_driver_ids[] = {
-
{
-
.name = "s3c2410-rtc",
-
.driver_data = TYPE_S3C2410,
-
}, {
-
.name = "s3c64xx-rtc", //name="s3c64xx-rtc"与设备一致完成匹配
-
.driver_data = TYPE_S3C64XX,
-
},
-
{ }
-
};
-
-
MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids);
-
-
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,
-
},
-
};
-
static int __init s3c_rtc_init(void)
-
{
-
return platform_driver_register(&s3c_rtc_driver);
-
}
-
module_init(s3c_rtc_init);
2. s3c_rtc_probe
-
static int __devinit s3c_rtc_probe(struct platform_device *pdev)
-
{
-
struct rtc_time rtc_tm;
-
int ret;
-
-
s3c_rtc_tickno = platform_get_irq(pdev, 1); //tick irq 34
-
s3c_rtc_alarmno = platform_get_irq(pdev, 0); //alarm irq 92
-
-
//申请io内存,三步曲
-
struct resource * res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
s3c_rtc_mem = request_mem_region(res->start, res->end-res->start+1,pdev->name);
-
s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
-
-
//设置PCLK_GATE中rtc位,使能rtc,因为在初始化时disable了
-
rtc_clk = clk_get(&pdev->dev, "rtc");
-
clk_enable(rtc_clk);
-
-
s3c_rtc_enable(pdev, 1); //检查rtc是否己启用,若没有启用则启用
-
-
device_init_wakeup(&pdev->dev, 1);
-
struct rtc_device * rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE);
-
s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; //将系统类型保存在全局变量中
-
-
s3c_rtc_gettime(NULL, &rtc_tm);
-
-
if (rtc_valid_tm(&rtc_tm)) {
-
rtc_tm.tm_year = 100;
-
rtc_tm.tm_mon = 0;
-
rtc_tm.tm_mday = 1;
-
rtc_tm.tm_hour = 0;
-
rtc_tm.tm_min = 0;
-
rtc_tm.tm_sec = 0;
-
s3c_rtc_settime(NULL, &rtc_tm);
-
}
-
-
if (s3c_rtc_cpu_type == TYPE_S3C64XX) //根据系统类型设定rtc时钟频率
-
rtc->max_user_freq = 32768;
-
else
-
rtc->max_user_freq = 128;
-
platform_set_drvdata(pdev, rtc);
-
s3c_rtc_setfreq(&pdev->dev, 1);
-
return 0;
-
}
s3c_rtc_probe
--> s3c_rtc_enable
-
static void s3c_rtc_enable(struct platform_device *pdev, int en)
-
{
-
void __iomem *base = s3c_rtc_base;
-
unsigned int tmp;
-
-
if (s3c_rtc_base == NULL)
-
return;
-
//检查RTCEN是否在运行
-
if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { //读取RTCEN位是否为1
-
tmp = readw(base + S3C2410_RTCCON); //不为1,设为1,使能rtc
-
writew(tmp | S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);
-
}
-
//检查BCD count select是否己运行
-
if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) {
-
tmp = readw(base + S3C2410_RTCCON);
-
writew(tmp & ~S3C2410_RTCCON_CNTSEL, base + S3C2410_RTCCON);
-
}
-
//检查RTC clock count reset是否在运行
-
if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) {
-
tmp = readw(base + S3C2410_RTCCON);
-
writew(tmp & ~S3C2410_RTCCON_CLKRST, base + S3C2410_RTCCON);
-
}
-
}
s3c_rtc_probe
--> rtc_device_register
-
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
-
const struct rtc_class_ops *ops, struct module *owner)
-
{
-
struct rtc_wkalrm alrm;
-
-
idr_pre_get(&rtc_idr, GFP_KERNEL) ;
-
-
mutex_lock(&idr_lock);
-
idr_get_new(&rtc_idr, NULL, &id);
-
mutex_unlock(&idr_lock);
-
-
int id = id & MAX_ID_MASK;
-
-
//申请并初始化rtc_device
-
struct rtc_device * rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
-
rtc->id = id;
-
rtc->ops = ops;
-
rtc->owner = owner;
-
rtc->irq_freq = 1;
-
rtc->max_user_freq = 64;
-
rtc->dev.parent = dev;
-
rtc->dev.class = rtc_class;
-
rtc->dev.release = rtc_device_release;
-
-
-
mutex_init(&rtc->ops_lock);
-
spin_lock_init(&rtc->irq_lock);
-
spin_lock_init(&rtc->irq_task_lock);
-
init_waitqueue_head(&rtc->irq_queue);
-
-
timerqueue_init_head(&rtc->timerqueue);
-
INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
-
-
rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
-
rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
-
hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-
rtc->pie_timer.function = rtc_pie_update_irq;
-
rtc->pie_enabled = 0;
-
-
err = __rtc_read_alarm(rtc, &alrm);
-
-
if (!err && !rtc_valid_tm(&alrm.time))
-
rtc_initialize_alarm(rtc, &alrm);
-
-
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
-
dev_set_name(&rtc->dev, "rtc%d", id);
-
-
rtc_dev_prepare(rtc); //rtc->char_dev: cdev_init
-
-
err = device_register(&rtc->dev);
-
-
rtc_dev_add_device(rtc); //rtc->char_dev: cdev_add
-
rtc_sysfs_add_device(rtc); //rtc->dev: device_create_file
-
rtc_proc_add_device(rtc); //proc_create_data -->ops: rtc_proc_fops
-
-
return rtc;
-
}
-
EXPORT_SYMBOL_GPL(rtc_device_register);
s3c_rtc_probe
-->
s3c_rtc_setfreq
-
static int s3c_rtc_setfreq(struct device *dev, int freq)
-
{
-
struct platform_device *pdev = to_platform_device(dev);
-
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
-
unsigned int tmp = 0;
-
-
if (!is_power_of_2(freq))
-
return -EINVAL;
-
-
spin_lock_irq(&s3c_rtc_pie_lock);
-
-
if (s3c_rtc_cpu_type == TYPE_S3C2410) { //s3c_rtc_cpu_type是一个全局变量
-
tmp = readb(s3c_rtc_base + S3C2410_TICNT);
-
tmp &= S3C2410_TICNT_ENABLE;
-
}
-
-
tmp |= (rtc_dev->max_user_freq / freq)-1; //将TICNT设为32768-1
-
-
writel(tmp, s3c_rtc_base + S3C2410_TICNT);
-
spin_unlock_irq(&s3c_rtc_pie_lock);
-
-
return 0;
-
}
注:为什么这儿 tmp
|= (rtc_dev
->max_user_freq
/ freq
)-1; 有个减1呢?
Period = (n+1)/32768 second (n= tick counter value)
三. class的处理方法
root@OK6410:~# cat /sys/class/rtc/rtc0/date
2001-11-05
-
static int s3c_rtc_open(struct device *dev)
-
{
-
struct platform_device *pdev = to_platform_device(dev);
-
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
-
int ret;
-
ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq, IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);
-
-
ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq, IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);
-
-
return ret;
-
}
driver/rtc/rtc-sysfs.c
-
rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr, char *buf)
-
{
-
ssize_t retval;
-
struct rtc_time tm;
-
-
retval = rtc_read_time(to_rtc_device(dev), &tm); //将数据读取到结构体tm中
-
if (retval == 0) {
-
retval = sprintf(buf, "%02d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec); //输出到buf
-
}
-
-
return retval;
-
}
rtc_sysfs_show_time
--> rtc_read_time
在driver/rtc/interface.c中
-
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) //时间读取过程
-
{
-
err = mutex_lock_interruptible(&rtc->ops_lock);
-
if (err)
-
return err;
-
err = __rtc_read_time(rtc, tm); //时间读取
-
mutex_unlock(&rtc->ops_lock);
-
return err;
-
}
-
EXPORT_SYMBOL_GPL(rtc_read_time);
-
rtc_sysfs_show_time
--> rtc_read_time
-
--> __rtc_read_time
-
static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
-
{
-
int err;
-
if (!rtc->ops)
-
err = -ENODEV;
-
else if (!rtc->ops->read_time)
-
err = -EINVAL;
-
else {
-
memset(tm, 0, sizeof(struct rtc_time));
-
err = rtc->ops->read_time(rtc->dev.parent, tm); //调用rtc->ops->read_time
-
}
-
return err;
-
}
rtc_device_register 中有 rtc->ops = ops; ops=s3c_rtops
-
static const struct rtc_class_ops s3c_rtcops = {
-
.open = s3c_rtc_open,
-
.release = s3c_rtc_release,
-
.read_time = s3c_rtc_gettime,
-
.set_time = s3c_rtc_settime,
-
.read_alarm = s3c_rtc_getalarm,
-
.set_alarm = s3c_rtc_setalarm,
-
.proc = s3c_rtc_proc,
-
.alarm_irq_enable = s3c_rtc_setaie,
-
};
rtc_sysfs_show_time
--> rtc_read_time
--> __rtc_read_time
--> s3c_rtc_gettime
-
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
-
{
-
unsigned int have_retried = 0;
-
void __iomem *base = s3c_rtc_base;
-
-
retry_get_time:
-
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);
-
-
-
if (rtc_tm->tm_sec == 0 && !have_retried) {
-
have_retried = 1;
-
goto retry_get_time;
-
}
-
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;
-
-
return rtc_valid_tm(rtc_tm);
-
}
四. 测试及附录
1. 测试一下
-
root@OK6410:~# date 080516492013 //设定当前时间为2013年08月05号16点49分
-
Mon Aug 5 16:49:00 CST 2013
-
root@OK6410:~# hwclock -w //写入到硬件时钟,即cmos中去
-
root@OK6410:~# hwclock //查看一下硬件时钟
-
Mon Aug 5 16:49:04 2013 0.000000 seconds
2. bcd2bin函数
-
unsigned bcd2bin(unsigned char val)
-
{
-
return (val & 0x0f) + (val >> 4) * 10;
-
}
-
EXPORT_SYMBOL(bcd2bin);
-
-
unsigned char bin2bcd(unsigned val)
-
{
-
return ((val / 10) << 4) + val % 10;
-
}
-
EXPORT_SYMBOL(bin2bcd);
bcd码是用4位二进制来表示10进制的,
所以要把char(8位)的bcd码转成10进制,
假设bcd码 0010 0101 --> 10进制是25
阅读(1483) | 评论(0) | 转发(0) |