led to get memory region resource\n");
return -ENOENT;
}
/*申请RTC的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别),
request_mem_region定义在ioport.h中*/
rtc_mem = request_mem_region(res->start, res->end - res->start + 1, pdev->name);
if (rtc_mem == NULL)
{
/*错误处理*/
dev_err(&pdev->dev, "failed to reserve memory region\n");
ret = -ENOENT;
goto err_nores;
}
/*将RTC的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/
rtc_base = ioremap(res->start, res->end - res->start + 1);
if (rtc_base == NULL)
{
/*错误处理*/
dev_err(&pdev->dev, "failed ioremap()\n");
ret = -EINVAL;
goto err_nomap;
}
/*好了,通过上面的步骤已经将RTC的资源都准备好了,下面就开始使用啦*/
/*这两个函数开始对RTC寄存器操作,定义都在下面*/
rtc_enable(pdev, 1); /*对RTC的实时时钟控制寄存器RTCCON进行操作(功能是初始化或者使能RTC)*/
rtc_setfreq(&pdev->dev, 1);/*对RTC的节拍时间计数寄存器TICNT的0-6位进行操作,即:节拍时间计数值的设定*/
/*device_init_wakeup该函数定义在pm_wakeup.h中,定义如下:
static inline void device_init_wakeup(struct device *dev, int val){
dev->power.can_wakeup = dev->power.should_wakeup = !!val;
}
显然这个函数是让驱动支持电源管理的,这里只要知道,can_wakeup为1时表明这个设备可以被唤醒,设备驱动为了支持
Linux中的电源管理,有责任调用device_init_wakeup()来初始化can_wakeup,而should_wakeup则是在设备的电源状态
发生变化的时候被device_may_wakeup()用来测试,测试它该不该变化,因此can_wakeup表明的是一种能力,
而should_wakeup表明的是有了这种能力以后去不去做某件事。好了,我们没有必要深入研究电源管理的内容了,
要不就扯远了,电源管理以后再讲*/
device_init_wakeup(&pdev->dev, 1);
/*将RTC注册为RTC设备类,RTC设备类在RTC驱动核心部分中由系统定义好的,
注意rtcops这个参数是一个结构体,该结构体的作用和里面的接口函数实现在第③步中。
rtc_device_register函数在rtc.h中定义,在drivers/rtc/class.c中实现*/
rtc = rtc_device_register("my2440", &pdev->dev, &rtcops, THIS_MODULE);
if (IS_ERR(rtc))
{
/*错误处理*/
dev_err(&pdev->dev, "cannot attach rtc\n");
ret = PTR_ERR(rtc);
goto err_nortc;
}
/*设置RTC节拍时间计数寄存器TICNT的节拍时间计数值的用户最大相对值,
这里你可能不理解这句,没关系,等你看到rtc_setfreq函数实现后自然就明白了*/
rtc->max_user_freq = 128;
/*将RTC设备类的数据传递给系统平台设备。
platform_set_drvdata是定义在platform_device.h的宏,如下:
#define platform_set_drvdata(_dev,data)dev_set_drvdata(&(_dev)->dev, (data))
而dev_set_drvdata又被定义在include/linux/device.h中,如下:
static inline void dev_set_drvdata (struct device *dev, void *data){
dev->driver_data = data;
}*/
platform_set_drvdata(pdev, rtc);
return 0;
//以下是上面错误处理的跳转点
err_nortc:
rtc_enable(pdev, 0);
iounmap(rtc_base);
err_nomap:
release_resource(rtc_mem);
err_nores:
return ret;
}
/*该函数主要是初始化或者使能RTC,
以下RTC的各种寄存器的宏定义在arch/arm/plat-s3c/include/plat/regs-rtc.h中,
各寄存器的用途和设置请参考S3C2440数据手册的第十七章实时时钟部分*/
static void rtc_enable(struct platform_device *pdev, int flag)
{
unsigned int tmp;
/*RTC的实时时钟控制寄存器RTCCON共有4个位,各位的初始值均为0,根据数据手册介绍第0位(即:RCTEN位)
可以控制CPU和RTC之间的所有接口(即RTC使能功能),所以在系统复位后应该将RTCCON寄存器的第0为置为1;
在关闭电源前,又应该将该位清零,以避免无意的写RTC寄存器*/
if (!flag)
{
/*当flag=0时(即属于关闭电源前的情况),RTCCON寄存器清零第一位*/
tmp = readb(rtc_base + S3C2410_RTCCON); /*读取RTCCON寄存器的值*/
/* tmp & ~S3C2410_RTCCON_RTCEN = 0 即屏蔽RTC使能*/
writeb(tmp & ~S3C2410_RTCCON_RTCEN, rtc_base + S3C2410_RTCCON);
tmp = readb(rtc_base + S3C2410_TICNT); /*读取TICNT寄存器的值*/
/* tmp & ~S3C2410_TICNT_ENABLE后第7位为0,即屏蔽节拍时间中断使能*/
writeb(tmp & ~S3C2410_TICNT_ENABLE, rtc_base + S3C2410_TICNT);
}
else
{
/*当flag!=0时(即属于系统复位后的情况),使能RTC*/
if ((readb(rtc_base + S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0)
{
dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
tmp = readb(rtc_base + S3C2410_RTCCON);
writeb(tmp | S3C2410_RTCCON_RTCEN, rtc_base + S3C2410_RTCCON);
}
if ((readb(rtc_base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL))
{
dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
tmp = readb(rtc_base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_CNTSEL, rtc_base + S3C2410_RTCCON);
}
if ((readb(rtc_base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST))
{
dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
tmp = readb(rtc_base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_CLKRST, rtc_base + S3C2410_RTCCON);
}
}
}
/*该函数主要是对RTC的节拍时间计数寄存器TICNT的0-6位进行操作,即:节拍时间计数值的设定*/
static int rtc_setfreq(struct device *dev, int freq)
{
unsigned int tmp;
if (!is_power_of_2(freq)) /*对freq的值进行检查*/
return -EINVAL;
spin_lock_irq(&rtc_pie_lock); /*获取自旋锁保护临界区资源*/
/*读取节拍时间计数寄存器TICNT的值*/
tmp = readb(rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
/*看数据手册得知,节拍时间计数值的范围是1-127,
还记得在rtc_enable函数中设置的rtc->max_user_freq=128吗?所以这里要减1*/
tmp |= (128 / freq) - 1;
/*将经运算后值写入节拍时间计数寄存器TICNT中,这里主要是改变TICNT的第0-6位的值*/
writeb(tmp, rtc_base + S3C2410_TICNT);
spin_unlock_irq(&rtc_pie_lock);/*释放自旋锁,即解锁*/
return 0;
}
|