Linux核心啟動過程分析(十)-----RTC驅動分析
阿新 • • 發佈:2018-12-01
參考https://blog.csdn.net/xuao20060793/article/details/46433263這篇博文
RTC驅動分析:
Class.c (drivers\rtc):subsys_initcall(rtc_init);
static int __init rtc_init(void) { rtc_class = class_create(THIS_MODULE, "rtc");//建立rtc類 if (IS_ERR(rtc_class)) { printk(KERN_ERR "%s: couldn't create class\n", __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);//建立sysfs相關的檔案 return 0; }
/* 平臺裝置驅動的初始化*/
Rtc-omap.c (drivers\rtc):module_init(rtc_init);
static int __init rtc_init(void)
{
return platform_driver_probe(&omap_rtc_driver, omap_rtc_probe);
}
platform_driver_register,向platform這個虛擬總線上註冊omap-rtc驅動,在setup_arch這個函式裡面會呼叫platform_add_device,所以在總線上有omap-rtc裝置,它們之間在"bus,device,device_driver"這個框架的設計下相遇,然後進行probe,分配相應的資料結構(struct rtc_device),向核心註冊此資料結構。
* platform_driver_probe - register driver for non-hotpluggable device * @drv: platform driver structure * @probe: the driver probe routine, probably from an __init section * * Use this instead of platform_driver_register() when you know the device * is not hotpluggable and has already been registered, and you want to * remove its run-once probe() infrastructure from memory after the driver * has bound to the device. * * One typical use for this would be with drivers for controllers integrated * into system-on-chip processors, where the controller devices have been * configured as part of board setup. * * Returns zero if the driver registered and bound to a device, else returns * a negative error code and with the driver not registered. */ int __init_or_module platform_driver_probe(struct platform_driver *drv, int (*probe)(struct platform_device *)) { int retval, code; /* make sure driver won't have bind/unbind attributes */ drv->driver.suppress_bind_attrs = true; /* temporary section violation during probe() */ drv->probe = probe; retval = code = platform_driver_register(drv); /* * Fixup that section violation, being paranoid about code scanning * the list of drivers in order to probe new devices. Check to see * if the probe was successful, and make sure any forced probes of * new devices fail. */ spin_lock(&drv->driver.bus->p->klist_drivers.k_lock); drv->probe = NULL; if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) retval = -ENODEV; drv->driver.probe = platform_drv_probe_fail; spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); if (code != retval) platform_driver_unregister(drv); return retval; }
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);//獲取滴答中斷號
if (omap_rtc_timer <= 0) {
pr_debug("%s: no update irq?\n", pdev->name);
return -ENOENT;
}
omap_rtc_alarm = platform_get_irq(pdev, 1);//獲取報警中斷號
if (omap_rtc_alarm <= 0) {
pr_debug("%s: no alarm irq?\n", pdev->name);
return -ENOENT;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//得到IO記憶體資源資訊
if (!res) {
pr_debug("%s: RTC resource data missing\n", pdev->name);
return -ENOENT;
}
mem = request_mem_region(res->start, resource_size(res), pdev->name);//申請IO記憶體資源
if (!mem) {
pr_debug("%s: RTC registers at %08x are not free\n",
pdev->name, res->start);
return -EBUSY;
}
rtc_base = ioremap(res->start, resource_size(res));//完成IO記憶體資源的對映
if (!rtc_base) {
pr_debug("%s: RTC registers can't be mapped\n", pdev->name);
goto fail;
}
rtc = rtc_device_register(pdev->name, &pdev->dev,
&omap_rtc_ops, THIS_MODULE);//分配struct rtc_device結構
if (IS_ERR(rtc)) {
pr_debug("%s: can't register RTC device, err %ld\n",
pdev->name, PTR_ERR(rtc));
goto fail0;
}
platform_set_drvdata(pdev, rtc);//設定平臺裝置驅動資料是struct rtc_device的指標
dev_set_drvdata(&rtc->dev, mem);
/* clear pending irqs, and set 1/second periodic,
* which we'll use instead of update irqs
*/
rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
/* clear old status */
reg = rtc_read(OMAP_RTC_STATUS_REG);
if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) {
pr_info("%s: RTC power up reset detected\n",
pdev->name);
rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG);
}
if (reg & (u8) OMAP_RTC_STATUS_ALARM)
rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
/* handle periodic and alarm irqs */
if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED,
dev_name(&rtc->dev), rtc)) {
pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",
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 claimed\n",
pdev->name, omap_rtc_alarm);
goto fail2;
}
/* On boards with split power, RTC_ON_NOFF won't reset the RTC */
reg = rtc_read(OMAP_RTC_CTRL_REG);
if (reg & (u8) OMAP_RTC_CTRL_STOP)
pr_info("%s: already running\n", pdev->name);
/* force to 24 hour mode */
new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
new_ctrl |= OMAP_RTC_CTRL_STOP;
/* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
*
* - Device wake-up capability setting should come through chip
* init logic. OMAP1 boards should initialize the "wakeup capable"
* flag in the platform device if the board is wired right for
* being woken up by RTC alarm. For OMAP-L138, this capability
* is built into the SoC by the "Deep Sleep" capability.
*
* - Boards wired so RTC_ON_nOFF is used as the reset signal,
* rather than nPWRON_RESET, should forcibly enable split
* power mode. (Some chip errata report that RTC_CTRL_SPLIT
* is write-only, and always reads as zero...)
*/
if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT)
pr_info("%s: split power mode\n", pdev->name);
if (reg != new_ctrl)
rtc_write(new_ctrl, OMAP_RTC_CTRL_REG);
if ((unsigned int)pdev->dev.platform_data == true)
device_init_wakeup(&pdev->dev, 1);
return 0;
fail2:
free_irq(omap_rtc_timer, rtc);
fail1:
rtc_device_unregister(rtc);
fail0:
iounmap(rtc_base);
fail:
release_mem_region(mem->start, resource_size(mem));
return -EIO;
}
/* 在啟動階段初始化系統時鐘*/
Hctosys.c (drivers\rtc):late_initcall(rtc_hctosys)
static int __init rtc_hctosys(void)
{
int err = -ENODEV;
struct rtc_time tm;
struct timespec tv = {
.tv_nsec = NSEC_PER_SEC >> 1,
};
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
if (rtc == NULL) {
pr_err("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
goto err_open;
}
err = rtc_read_time(rtc, &tm);
if (err) {
dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
goto err_read;
}
err = rtc_valid_tm(&tm);
if (err) {
dev_err(rtc->dev.parent,
"hctosys: invalid date/time\n");
goto err_invalid;
}
rtc_tm_to_time(&tm, &tv.tv_sec);
do_settimeofday(&tv);
dev_info(rtc->dev.parent,
"setting system clock to "
"%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(unsigned int) tv.tv_sec);
err_invalid:
err_read:
rtc_class_close(rtc);
err_open:
rtc_hctosys_ret = err;
return err;
}