【Linux 驱动开发】rtc子系统

RTC 子系统

以下分析基于Linux 4.9.88

RTC 内核子系统

rtc子系统是由Linux内核维护人员设计的一个标准、经典、接口统一的rtc 设备框架,驱动工程师只需要在其接口上实现rtc 驱动即可。

  • drivers\rtc\rtc-dev.c
  • drivers\rtc\class.c
  • drivers\rtc\rtc-sysfs.c

RTC 设备驱动

rtc设备注册

首先从设备的注册函数处剖析,

drivers\rtc\class.c
/**
 * rtc_device_register - register w/ RTC class
 * @dev: the device to register
 *
 * rtc_device_unregister() must be called when the class device is no
 * longer needed.
 *
 * Returns the pointer to the new struct class device.
 */
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
					const struct rtc_class_ops *ops,
					struct module *owner)
/**
 * name指rtc驱动名字
 * dev rtc设备的父设备节点,例如ii_client->dev
 * rtc_class_ops 指由驱动工程师实现的操作设备进行时间配置相的接口
 * owner 一般为THIS_MODULE
 */
{
	struct rtc_device *rtc;
	struct rtc_wkalrm alrm;
	int of_id = -1, id = -1, err;

	if (dev->of_node)
		of_id = of_alias_get_id(dev->of_node, "rtc");
	else if (dev->parent && dev->parent->of_node)
		of_id = of_alias_get_id(dev->parent->of_node, "rtc");
		
	if (of_id >= 0) {
		id = ida_simple_get(&rtc_ida, of_id, of_id + 1,
				    GFP_KERNEL);
		if (id < 0)
			dev_warn(dev, "/aliases ID %d not available\n",
				    of_id);
	}

	if (id < 0) {
		id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
		if (id < 0) {
			err = id;
			goto exit;
		}
	}
	//of_alias_get_id()函数为rtc设备申请id
	
	rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
	if (rtc == NULL) {
		err = -ENOMEM;
		goto exit_ida;
	}
	//申请rtc对象内存
	
	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; // 全局变量 struct class *rtc_class;
	rtc->dev.groups = rtc_get_dev_attribute_groups(); //rtc设备在class下属性文件,drivers\rtc\rtc-sysfs.c 有详细说明,
	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);

	/* Init timerqueue */
	timerqueue_init_head(&rtc->timerqueue); //初始化 timerqueue对象
	INIT_WORK(&rtc->irqwork, rtc_timer_do_work);  //进行函数绑定
	/* Init aie timer */
	rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc); // 绑定函数
	/* Init uie timer */
	rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc); // 绑定函数
	/* Init pie timer */
	hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); // 绑定函数
	rtc->pie_timer.function = rtc_pie_update_irq;
	rtc->pie_enabled = 0;

	strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
	dev_set_name(&rtc->dev, "rtc%d", id);// 初始化 rtc->dev->name

	/* Check to see if there is an ALARM already set in hw */
	err = __rtc_read_alarm(rtc, &alrm);

	if (!err && !rtc_valid_tm(&alrm.time))
		rtc_initialize_alarm(rtc, &alrm);
	// 初始化alrm对象
	
	rtc_dev_prepare(rtc);

	err = device_register(&rtc->dev);
	if (err) {
		/* This will free both memory and the ID */
		put_device(&rtc->dev);
		goto exit;
	}
	// 注册rtc->dev对象,应该会生成/dev/rtc%id设备节点和class/rtc/name

	rtc_dev_add_device(rtc); 
	rtc_proc_add_device(rtc);
	// 将rtc字符设备 详细定义在drivers\rtc\rtc-dev.c
	
	dev_info(dev, "rtc core: registered %s as %s\n",
			rtc->name, dev_name(&rtc->dev));

	return rtc;

exit_ida:
	ida_simple_remove(&rtc_ida, id);

exit:
	dev_err(dev, "rtc core: unable to register %s, err = %d\n",
			name, err);
	return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(rtc_device_register);

struct rtc_class_ops

对于这些RTC方法,设备参数是任何总线上持有硬件(I2C、平台、SPI等)的物理设备,它被传递给rtc_device_register(), 入参device->driver_data通常保存设备一些私有数据,例如rtc的rtc_device指针。
以下的device* dev 应该是rtc 的pareent-dev,例如iic节点下的rtc设备,其父节点就是iic-dev

include\linux\rtc.h
/*
 * For these RTC methods the device parameter is the physical device
 * on whatever bus holds the hardware (I2C, Platform, SPI, etc), which
 * was passed to rtc_device_register().  Its driver_data normally holds
 * device state, including the rtc_device pointer for the RTC.
 *
 * Most of these methods are called with rtc_device.ops_lock held,
 * through the rtc_*(struct rtc_device *, ...) calls.
 *
 * The (current) exceptions are mostly filesystem hooks:
 *   - the proc() hook for procfs
 *   - non-ioctl() chardev hooks:  open(), release(), read_callback()
 *
 * REVISIT those periodic irq calls *do* have ops_lock when they're
 * issued through ioctl() ...
 */
 
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_mmss64)(struct device *, time64_t secs);
	int (*set_mmss)(struct device *, unsigned long secs);
	int (*read_callback)(struct device *, int data);
	int (*alarm_irq_enable)(struct device *, unsigned int enabled);
	int (*read_offset)(struct device *, long *offset);
	int (*set_offset)(struct device *, long offset);
};

struct rtc_device

include\linux\rtc.h

struct rtc_device {
	struct device dev;
	struct module *owner;

	int id;
	char name[RTC_DEVICE_NAME_SIZE];

	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;

	struct timerqueue_head timerqueue;
	struct rtc_timer aie_timer;
	struct rtc_timer uie_rtctimer;
	struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
	int pie_enabled;
	struct work_struct irqwork;
	/* Some hardware can't support UIE mode */
	int uie_unsupported;

#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
};

struct rtc_wkalrm

include\linux\rtc.h
/*
 * This data structure is inspired by the EFI (v0.92) wakeup
 * alarm API.
 */
struct rtc_wkalrm {
	unsigned char enabled;	/* 0 = alarm disabled, 1 = alarm enabled */
	unsigned char pending;  /* 0 = alarm not pending, 1 = alarm pending */
	struct rtc_time time;	/* time the alarm is set to */
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值