现在的位置: 首页 > 综合 > 正文

RTC驱动模型

2018年02月07日 ⁄ 综合 ⁄ 共 12425字 ⁄ 字号 评论关闭
 RTC驱动模型 

一、概述
1.在Linux中有硬件时钟与系统时钟等两种时钟。硬件时钟是指主机板上的时钟设备,也就是通常可在BIOS画面设定的时钟。系统时钟则是指kernel中的时钟。当Linux启动时,系统时钟会去读取硬件时钟的设定,之后系统时钟即独立运作。所有Linux相关指令与函数都是读取系统时钟的设定。
系统时钟的设定就是我们常用的date命令,而我们写的RTC驱动就是为硬件时钟服务的,它有属于自己的命令hwclock,因此使用date命令是不可能调用到我们的驱动的,我们可以通过hwclock的一些指令来实现更新rtc时钟——也就是系统时钟和硬件时钟的交互。
hwclock –r 显示硬件时钟与日期
hwclock –s 将系统时钟调整为与目前的硬件时钟一致。
hwclock –w 将硬件时钟调整为与目前的系统时钟一致。
        
2.RTC核心文件:
/include/linux/rtc.h 定义了与RTC有关的数据结构
/drivers/rtc/hctosys.c 在启动时初始化系统时间。
/drivers/rtc/rtc-lib.c 提供了一些时间格式相互转化的函数。
/drivers/rtc/class.c 这个文件向linux设备模型核心注册了一个类RTC,然后向驱动程序提供了注册/注销接口
/drivers/rtc/interface.c 这个文件主要提供了用户程序与RTC驱动的接口函数,用户程序一般通过ioctl与RTC驱动交互,这里定义了每个ioctl命令需要调用的函数
/drivers/rtc/rtc-dev.c: 字符设备的注册和用户层文件操作函数接口。 
/drivers/rtc/rtc-proc.c     与proc文件系统有关
/drivers/rtc/rtc-sysfs.c 与sysfs有关
/drivers/rtc/rtc-omap.c: davinci RTC的芯片平台驱动。
3.驱动模型结构


二. 基本数据结构
1. struct rtc_device 结构
//这个结构是RTC驱动程序的基本数据结构,但是他不像其他核心的基本结构一样,驱动程序以他为参数调用注册函数注册到核心。这个结构是由注册函数返回给驱动程序的。
struct rtc_device 
{ 
    struct device dev; 
    struct module *owner; 
    int id; 
    char name[RTC_DEVICE_NAME_SIZE];//RTC名称 
    const struct rtc_class_ops *ops;//驱动程序的基本操作函数,操作底层寄存器 
    struct mutex ops_lock; 
    struct cdev char_dev;//嵌入的字符设备结构 
    unsigned long flags; 
    unsigned long irq_data; 
    spinlock_t irq_lock; 
    wait_queue_head_t irq_queue; 
    struct fasync_struct *async_queue; 
  
    struct rtc_task *irq_task; 
    spinlock_t irq_task_lock; 
    int irq_freq; 
    int max_user_freq; 
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 
    struct work_struct uie_task; 
    struct timer_list uie_timer; 
    /* Those fields are protected by rtc->irq_lock */ 
    unsigned int oldsecs; 
    unsigned int uie_irq_active:1; 
    unsigned int stop_uie_polling:1; 
    unsigned int uie_task_active:1; 
    unsigned int uie_timer_active:1; 
#endif 
}; 

2. struct rtc_class_ops 结构
//这个结构是RTC驱动程序要实现的基本操作函数,注意这里的操作不是文件操作。驱动程序通过初始化这样一个结构,将自己实现的函数与RTC核心联系起来。
//这里面的大部分函数都要驱动程序来实现。而且这些函数都是操作底层硬件的,属于最底层的函数。
struct rtc_class_ops { 
    int (*open)(struct device *); 
    void (*release)(struct device *); 
    int (*ioctl)(struct device *, unsigned int, unsigned long); 
    int (*read_time)(struct device *, struct rtc_time *); 
    int (*set_time)(struct device *, struct rtc_time *); 
    int (*read_alarm)(struct device *, struct rtc_wkalrm *); 
    int (*set_alarm)(struct device *, struct rtc_wkalrm *); 
    int (*proc)(struct device *, struct seq_file *); 
    int (*set_mmss)(struct device *, unsigned long secs); 
    int (*irq_set_state)(struct device *, int enabled); 
    int (*irq_set_freq)(struct device *, int freq); 
    int (*read_callback)(struct device *, int data); 
    int (*alarm_irq_enable)(struct device *, unsigned int enabled); 
    int (*update_irq_enable)(struct device *, unsigned int enabled); 
}; 

3. struct rtc_time 结构
//代表了时间与日期,从RTC设备读回的时间和日期就保存在这个结构体中
struct rtc_time { 
    int tm_sec; 
    int tm_min; 
    int tm_hour; 
    int tm_mday; 
    int tm_mon; 
    int tm_year; 
    int tm_wday; 
    int tm_yday; 
    int tm_isdst; 
}; 


三、RTC驱动过程
1.platform 设备注册
//在board-da850-evm.c中注册RTC的platform设备。
static __init void da850_evm_init(void)
{
    。。。。。。
    ret = da8xx_register_rtc();
    if (ret)
        pr_warning("da850_evm_init: rtc setup failed: %dn", ret);
    。。。。。。
}

static struct resource da8xx_rtc_resources[] = {
    {
        .start        = DA8XX_RTC_BASE,//#define DA8XX_RTC_BASE            0x01C23000
        .end        = DA8XX_RTC_BASE + SZ_4K - 1,
        .flags        = IORESOURCE_MEM,
    },
    { /* timer irq */
        .start        = IRQ_DA8XX_RTC,//中断号是19
        .end        = IRQ_DA8XX_RTC,
        .flags        = IORESOURCE_IRQ,
    },
    { /* alarm irq */
        .start        = IRQ_DA8XX_RTC,
        .end        = IRQ_DA8XX_RTC,
        .flags        = IORESOURCE_IRQ,
    },
};

static struct platform_device da8xx_rtc_device = {
    .name = "omap_rtc",
    .id = -1,
    .num_resources    = ARRAY_SIZE(da8xx_rtc_resources),
    .resource    = da8xx_rtc_resources,
};

int da8xx_register_rtc(void)
{
    int ret;
    void __iomem *base;

    base = ioremap(DA8XX_RTC_BASE, SZ_4K);//RTC寄存器基地址映射到内存中
    if (WARN_ON(!base))
        return -ENOMEM;

    //写RTC寄存器,解除RTC的写保护
    __raw_writel(0x83e70b13, base + 0x6c);
    __raw_writel(0x95a4f1e0, base + 0x70);

    iounmap(base);//使用虚拟地址访问物理空间,访问结束后,释放本次映射

    ret = platform_device_register(&da8xx_rtc_device);//注册platform设备
    if (!ret)/* Atleast on DA850, RTC is a wakeup source */
        device_init_wakeup(&da8xx_rtc_device.dev, true);

    return ret;
}

2.调用class_create创建了一个类--rtc。我们知道类是一个设备的高层视图,他抽象出了底层的实现细节。
//driversrtcclass.c
static int __init rtc_init(void)
{
    rtc_class = class_create(THIS_MODULE, "rtc");//为sysfs文件系统创建一个类rtc_class
    if (IS_ERR(rtc_class)) {
        printk(KERN_ERR "%s: couldn't create classn", __FILE__);
        return PTR_ERR(rtc_class);
    }
    rtc_class->suspend = rtc_suspend;
    rtc_class->resume = rtc_resume;
    rtc_dev_init();//为RTC设备动态分配一个设备号
    rtc_sysfs_init(rtc_class);//设置属性
    return 0;
}
//driversrtcrtc-dev.c
void __init rtc_dev_init(void)
{
    int err;

    err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");//动态分配设备号,赋值给rtc_devt
    if (err < 0)
        printk(KERN_ERR "%s: failed to allocate char dev regionn",__FILE__);
}

//driversrtcrtc-sysfs.c
static struct device_attribute rtc_attrs[] = {
    __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),
    __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),
    __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),
    __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),
    __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,rtc_sysfs_set_max_user_freq),
    __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),
    { },
};

void __init rtc_sysfs_init(struct class *rtc_class)
{
    //sysfs文件系统会根据这些属性创建属性文件
    rtc_class->dev_attrs = rtc_attrs;//RTC属性
}

3.注册RTC设备的platform driver
//在目录driversrtcrtc-omap.c
static struct rtc_class_ops omap_rtc_ops = {
    .ioctl        = omap_rtc_ioctl,
    .read_time    = omap_rtc_read_time,
    .set_time    = omap_rtc_set_time,
    .read_alarm    = omap_rtc_read_alarm,
    .set_alarm    = omap_rtc_set_alarm,
};

static int omap_rtc_alarm;//RTC的alarm中断号
static int omap_rtc_timer;//RTC的timer中断号

static struct platform_driver omap_rtc_driver = {
    .remove        = __exit_p(omap_rtc_remove),
    .suspend    = omap_rtc_suspend,
    .resume        = omap_rtc_resume,
    .shutdown    = omap_rtc_shutdown,
    .driver        = {
        .name    = "omap_rtc",
        .owner    = THIS_MODULE,
    },
};

static int __init rtc_init(void)
{
    //注册RTC的platform driver驱动,platform的设备和驱动配对成功后,会调用omap_rtc_probe()函数
    return platform_driver_probe(&omap_rtc_driver, omap_rtc_probe);
}
module_init(rtc_init);

static int __init omap_rtc_probe(struct platform_device *pdev)
{
    struct resource        *res, *mem;
    struct rtc_device    *rtc;
    u8            reg, new_ctrl;

    omap_rtc_timer = platform_get_irq(pdev, 0);//从platform_device资源中获得RTC的timer中断号
    if (omap_rtc_timer <= 0) {
        pr_debug("%s: no update irq?n", pdev->name);
        return -ENOENT;
    }

    omap_rtc_alarm = platform_get_irq(pdev, 1);//从platform_device资源中获得RTC的alarm中断号
    if (omap_rtc_alarm <= 0) {
        pr_debug("%s: no alarm irq?n", pdev->name);
        return -ENOENT;
    }

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//从platform_device资源中获得RTC的寄存器地址资源
    if (!res) {
        pr_debug("%s: RTC resource data missingn", pdev->name);
        return -ENOENT;
    }

    mem = request_mem_region(res->start, resource_size(res), pdev->name);//申请内存资源
    if (!mem) {
        pr_debug("%s: RTC registers at %08x are not freen",
            pdev->name, res->start);
        return -EBUSY;
    }

    rtc_base = ioremap(res->start, resource_size(res));//将RTC寄存器物理地址映射到内存虚拟地址
    if (!rtc_base) {
        pr_debug("%s: RTC registers can't be mappedn", pdev->name);
        goto fail;
    }

    rtc = rtc_device_register(pdev->name, &pdev->dev,&omap_rtc_ops, THIS_MODULE);//注册RTC设备
    if (IS_ERR(rtc)) {
        pr_debug("%s: can't register RTC device, err %ldn",pdev->name, PTR_ERR(rtc));
        goto fail0;
    }
    platform_set_drvdata(pdev, rtc);//将RTC数据结构rtc_device保存在platform_device结构中的device字段中
    dev_set_drvdata(&rtc->dev, mem);//将资源保存

    //Disable RTC的alarm和timer中断,并且设置秒周期
    rtc_write(0, OMAP_RTC_INTERRUPTS_REG);

    /* clear old status */
    reg = rtc_read(OMAP_RTC_STATUS_REG);//读RTC状态寄存器
    if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) {//davinci平台没有第7位的状态
        pr_info("%s: RTC power up reset detectedn",pdev->name);
        rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG);
    }
    
    if (reg & (u8) OMAP_RTC_STATUS_ALARM)//如果有alarm中断产生
        rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);//此位写1清除alarm中断状态

    //申请中断并设置中断函数
    if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED,dev_name(&rtc->dev), rtc)) {
        pr_debug("%s: RTC timer interrupt IRQ%d already claimedn",pdev->name, omap_rtc_timer);
        goto fail1;
    }
    if ((omap_rtc_timer != omap_rtc_alarm) &&(request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,dev_name(&rtc->dev), rtc))) {
        pr_debug("%s: RTC alarm interrupt IRQ%d already claimedn",pdev->name, omap_rtc_alarm);
        goto fail2;
    }

    reg = rtc_read(OMAP_RTC_CTRL_REG);//读RTC控制寄存器
    if (reg & (u8) OMAP_RTC_CTRL_STOP)//查看RTC是否在运行
        pr_info("%s: already runningn", pdev->name);

    //采用24H模式,不自主补偿矫正时间,并且Disable split power.
    new_ctrl = reg & ~(OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
    new_ctrl |= OMAP_RTC_CTRL_STOP;//启动RTC计数

    if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT)
        pr_info("%s: split power moden", pdev->name);

    if (reg != new_ctrl)
        rtc_write(new_ctrl, OMAP_RTC_CTRL_REG);

    return 0;

fail2:
    free_irq(omap_rtc_timer, NULL);
fail1:
    rtc_device_unregister(rtc);
fail0:
    iounmap(rtc_base);
fail:
    release_resource(mem);
    return -EIO;
}

struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner)
{
    struct rtc_device *rtc;
    int id, err;
    
    //处理一个idr的结构,idr在linux内核中指的就是整数ID管理机制,从本质上来说,idr是一种将整数ID号和特定指针关联在一起的机制。这里从内核中获取一个idr结构,并与id相关联。
    if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
        err = -ENOMEM;
        goto exit;
    }

    mutex_lock(&idr_lock);
    err = idr_get_new(&rtc_idr, NULL, &id);
    mutex_unlock(&idr_lock);

    if (err < 0)
        goto exit;

    id = id & MAX_ID_MASK;
    
    //分配RTC设备的结构体
    rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
    if (rtc == NULL) {
        err = -ENOMEM;
        goto exit_idr;
    }
    
    //对结构体进行初始化
    rtc->id = id;
    rtc->ops = ops;//rtc的底层驱动操作函数
    rtc->owner = owner;
    rtc->max_user_freq = 64;
    rtc->dev.parent = dev;//rtc_device设备的父设备是platform设备中的device结构
    rtc->dev.class = rtc_class;//指向rtc的类
    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);

    strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);//设置名称为omap_rtc
    dev_set_name(&rtc->dev, "rtc%d", id);//设置RTC设备名称,生成设备节点就是这个名称
    
    //因为RTC设备本质来讲还是字符设备,所以这里初始化了字符设备相关的结构:设备号以及文件操作。
    rtc_dev_prepare(rtc);//RTC字符设备注册的准备

    err = device_register(&rtc->dev);//RTC设备注册,生成sysfs文件系统相关的诸多文件和目录,udev会根据生成的dev文件创建设备节点
    if (err) {
        put_device(&rtc->dev);
        goto exit_kfree;
    }

    rtc_dev_add_device(rtc);//添加RTC的字符设备
    rtc_sysfs_add_device(rtc);//为设备添加了一个闹钟属性
    rtc_proc_add_device(rtc);//创建proc文件系统接口

    dev_info(dev, "rtc core: registered %s as %sn",rtc->name, dev_name(&rtc->dev));
    return rtc;

exit_kfree:
    kfree(rtc);

exit_idr:
    mutex_lock(&idr_lock);
    idr_remove(&rtc_idr, id);
    mutex_unlock(&idr_lock);

exit:
    dev_err(dev, "rtc core: unable to register %s, err = %dn",name, err);
    return ERR_PTR(err);
}

//用户层文件操作函数接口
static const struct file_operations rtc_dev_fops = {
    .owner        = THIS_MODULE,
    .llseek        = no_llseek,
    .read        = rtc_dev_read,
    .poll        = rtc_dev_poll,
    .unlocked_ioctl    = rtc_dev_ioctl,
    .open        = rtc_dev_open,
    .release    = rtc_dev_release,
    .fasync        = rtc_dev_fasync,
};

void rtc_dev_prepare(struct rtc_device *rtc)
{
    if (!rtc_devt)
        return;

    if (rtc->id >= RTC_DEV_MAX) {
        pr_debug("%s: too many RTC devicesn", rtc->name);
        return;
    }

    rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);//获得设备号

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
    INIT_WORK(&rtc->uie_task, rtc_uie_task);
    setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
#endif

    cdev_init(&rtc->char_dev, &rtc_dev_fops);
    rtc->char_dev.owner = rtc->owner;
}

void rtc_dev_add_device(struct rtc_device *rtc)
{
    //添加字符设备
    if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
        printk(KERN_WARNING "%s: failed to add char device %d:%dn",rtc->name, MAJOR(rtc_devt), rtc->id);
    else
        pr_debug("%s: dev (%d:%d)n", rtc->name,MAJOR(rtc_devt), rtc->id);
}

void rtc_sysfs_add_device(struct rtc_device *rtc)
{
    int err;

    /* not all RTCs support both alarms and wakeup */
    if (!rtc_does_wakealarm(rtc))
        return;
    
    err = device_create_file(&rtc->dev, &dev_attr_wakealarm);
    if (err)
        dev_err(rtc->dev.parent,"failed to create alarm attribute, %dn", err);
}

四、用户层应用
1.读时间
用户打开设备后,通过ioctl命令发出读时间操作,进而调用interface.c中的rtc_read_time()
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
    int err;

    err = mutex_lock_interruptible(&rtc->ops_lock);
    if (err)
        return 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);//调用底层驱动函数omap_rtc_read_time()
    }

    mutex_unlock(&rtc->ops_lock);
    return err;
}

static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
    /* we don't report wday/yday/isdst ... */
    local_irq_disable();
    rtc_wait_not_busy();
    //读RTC寄存器
    tm->tm_sec = rtc_read(OMAP_RTC_SECONDS_REG);
    tm->tm_min = rtc_read(OMAP_RTC_MINUTES_REG);
    tm->tm_hour = rtc_read(OMAP_RTC_HOURS_REG);
    tm->tm_mday = rtc_read(OMAP_RTC_DAYS_REG);
    tm->tm_mon = rtc_read(OMAP_RTC_MONTHS_REG);
    tm->tm_year = rtc_read(OMAP_RTC_YEARS_REG);

    local_irq_enable();

    bcd2tm(tm);
    return 0;
}

2.写时间
int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
    int err;

    err = rtc_valid_tm(tm);
    if (err != 0)
        return err;

    err = mutex_lock_interruptible(&rtc->ops_lock);
    if (err)
        return err;

    if (!rtc->ops)
        err = -ENODEV;
    else if (rtc->ops->set_time)
        err = rtc->ops->set_time(rtc->dev.parent, tm);
    else if (rtc->ops->set_mmss) {
        unsigned long secs;
        err = rtc_tm_to_time(tm, &secs);
        if (err == 0)
            err = rtc->ops->set_mmss(rtc->dev.parent, secs);//调用omap_rtc_set_time
    } else
        err = -EINVAL;

    mutex_unlock(&rtc->ops_lock);
    return err;
}

static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
    if (tm2bcd(tm) < 0)
        return -EINVAL;
    local_irq_disable();
    rtc_wait_not_busy();

    rtc_write(tm->tm_year, OMAP_RTC_YEARS_REG);
    rtc_write(tm->tm_mon, OMAP_RTC_MONTHS_REG);
    rtc_write(tm->tm_mday, OMAP_RTC_DAYS_REG);
    rtc_write(tm->tm_hour, OMAP_RTC_HOURS_REG);
    rtc_write(tm->tm_min, OMAP_RTC_MINUTES_REG);
    rtc_write(tm->tm_sec, OMAP_RTC_SECONDS_REG);

    local_irq_enable();

    return 0;
}

抱歉!评论已关闭.