From f2eef045de9defbc6fc6b72b17f0941cbe26c81d Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 18 Nov 2017 00:15:58 +0300 Subject: [PATCH 01/19] rtc: brcmstb-waketimer: fix error handling in brcmstb_waketmr_probe() brcmstb_waketmr_probe() does not disable timer->clk on error paths. Found by Linux Driver Verification project (linuxtesting.org). Fixes: c4f07ecee22e ("rtc: brcmstb-waketimer: Add Broadcom STB wake-timer") Signed-off-by: Alexey Khoroshilov Reviewed-by: Florian Fainelli Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-brcmstb-waketimer.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c index 796ac792a381..6cee61201c30 100644 --- a/drivers/rtc/rtc-brcmstb-waketimer.c +++ b/drivers/rtc/rtc-brcmstb-waketimer.c @@ -253,7 +253,7 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev) ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0, "brcmstb-waketimer", timer); if (ret < 0) - return ret; + goto err_clk; timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot; register_reboot_notifier(&timer->reboot_notifier); @@ -262,12 +262,21 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev) &brcmstb_waketmr_ops, THIS_MODULE); if (IS_ERR(timer->rtc)) { dev_err(dev, "unable to register device\n"); - unregister_reboot_notifier(&timer->reboot_notifier); - return PTR_ERR(timer->rtc); + ret = PTR_ERR(timer->rtc); + goto err_notifier; } dev_info(dev, "registered, with irq %d\n", timer->irq); + return 0; + +err_notifier: + unregister_reboot_notifier(&timer->reboot_notifier); + +err_clk: + if (timer->clk) + clk_disable_unprepare(timer->clk); + return ret; } From 5c6e5eca79e8a5a89a7ae98b30ba6d71e367d899 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Sat, 18 Nov 2017 11:03:10 +0900 Subject: [PATCH 02/19] rtc: r9701: Remove r9701_remove function r9701_remove function is now empty, remove it. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-r9701.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index 83d2bcca6a8f..b6c5eb97051c 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c @@ -164,17 +164,11 @@ static int r9701_probe(struct spi_device *spi) return 0; } -static int r9701_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver r9701_driver = { .driver = { .name = "rtc-r9701", }, .probe = r9701_probe, - .remove = r9701_remove, }; module_spi_driver(r9701_driver); From 876d1b53c5bfecc6a4d0fc7ea8bf5f288535322f Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sat, 25 Nov 2017 19:41:32 -0500 Subject: [PATCH 03/19] rtc: Remove unused RTC_DEVICE_NAME_SIZE The last usage was removed in 5c82a6ae0 when rtc_device.name was removed Signed-off-by: Cole Robinson Signed-off-by: Alexandre Belloni --- include/linux/rtc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 41319a2e409b..fc6c90b57be0 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -87,7 +87,6 @@ struct rtc_class_ops { int (*set_offset)(struct device *, long offset); }; -#define RTC_DEVICE_NAME_SIZE 20 typedef struct rtc_task { void (*func)(void *private_data); void *private_data; From 9f33399c90741196be71cc4637e623a7d33f8c7f Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 29 Nov 2017 15:26:44 +0100 Subject: [PATCH 04/19] rtc: stm32: Fix copyright Uniformize STMicroelectronics copyrights header Signed-off-by: Benjamin Gaignard CC: Amelie Delaunay Acked-by: Alexandre TORGUE Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-stm32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 3a5c3d7d0c77..f25dabe8fd02 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -1,6 +1,6 @@ /* - * Copyright (C) Amelie Delaunay 2016 - * Author: Amelie Delaunay + * Copyright (C) STMicroelectronics SA 2017 + * Author: Amelie Delaunay for STMicroelectronics. * License terms: GNU General Public License (GPL), version 2 */ From 3eff6d2cc995e5a4cffb7a35dc368f02f39c4cd3 Mon Sep 17 00:00:00 2001 From: Stephen Barber Date: Fri, 10 Nov 2017 22:55:52 +0100 Subject: [PATCH 05/19] mfd: cros_ec: Introduce RTC commands and events definitions. The EC can function as a simple RT, this patch adds the RTC related definitions needed by the rtc-cros-ec driver. Signed-off-by: Stephen Barber Signed-off-by: Enric Balletbo i Serra Acked-by: Lee Jones Acked-by: Benson Leung Reviewed-by: Brian Norris Signed-off-by: Alexandre Belloni --- include/linux/mfd/cros_ec_commands.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index 2b16e95b9bb8..c6046a2e94d5 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h @@ -291,6 +291,9 @@ enum host_event_code { /* EC desires to change state of host-controlled USB mux */ EC_HOST_EVENT_USB_MUX = 28, + /* EC RTC event occurred */ + EC_HOST_EVENT_RTC = 26, + /* * The high bit of the event mask is not used as a host event code. If * it reads back as set, then the entire event mask should be @@ -799,6 +802,8 @@ enum ec_feature_code { EC_FEATURE_USB_MUX = 23, /* Motion Sensor code has an internal software FIFO */ EC_FEATURE_MOTION_SENSE_FIFO = 24, + /* EC has RTC feature that can be controlled by host commands */ + EC_FEATURE_RTC = 27, }; #define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32)) @@ -1709,6 +1714,9 @@ struct ec_response_rtc { #define EC_CMD_RTC_SET_VALUE 0x46 #define EC_CMD_RTC_SET_ALARM 0x47 +/* Pass as param to SET_ALARM to clear the current alarm */ +#define EC_RTC_ALARM_CLEAR 0 + /*****************************************************************************/ /* Port80 log access */ From 6f2a71a31afd738af446c802e1ed40365afa55b8 Mon Sep 17 00:00:00 2001 From: Stephen Barber Date: Fri, 10 Nov 2017 22:55:53 +0100 Subject: [PATCH 06/19] rtc: cros-ec: add cros-ec-rtc driver. On platforms with a Chrome OS EC, the EC can function as a simple RTC. Add a basic driver with this functionality. Signed-off-by: Stephen Barber Signed-off-by: Enric Balletbo i Serra Acked-by: Alexandre Belloni Acked-by: Benson Leung Reviewed-by: Brian Norris Tested-by: Brian Norris Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-cros-ec.c | 413 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 424 insertions(+) create mode 100644 drivers/rtc/rtc-cros-ec.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b59a31b079a5..64f4b164932a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1255,6 +1255,16 @@ config RTC_DRV_ZYNQMP If you say yes here you get support for the RTC controller found on Xilinx Zynq Ultrascale+ MPSoC. +config RTC_DRV_CROS_EC + tristate "Chrome OS EC RTC driver" + depends on MFD_CROS_EC + help + If you say yes here you will get support for the + Chrome OS Embedded Controller's RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-cros-ec. + comment "on-CPU RTC drivers" config RTC_DRV_ASM9260 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index f2f50c11dc38..cf9d29e34dbd 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o +obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.o obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c new file mode 100644 index 000000000000..f0ea6899c731 --- /dev/null +++ b/drivers/rtc/rtc-cros-ec.c @@ -0,0 +1,413 @@ +/* + * RTC driver for Chrome OS Embedded Controller + * + * Copyright (c) 2017, Google, Inc + * + * Author: Stephen Barber + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-ec-rtc" + +/** + * struct cros_ec_rtc - Driver data for EC RTC + * + * @cros_ec: Pointer to EC device + * @rtc: Pointer to RTC device + * @notifier: Notifier info for responding to EC events + * @saved_alarm: Alarm to restore when interrupts are reenabled + */ +struct cros_ec_rtc { + struct cros_ec_device *cros_ec; + struct rtc_device *rtc; + struct notifier_block notifier; + u32 saved_alarm; +}; + +static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command, + u32 *response) +{ + int ret; + struct { + struct cros_ec_command msg; + struct ec_response_rtc data; + } __packed msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg.command = command; + msg.msg.insize = sizeof(msg.data); + + ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); + if (ret < 0) { + dev_err(cros_ec->dev, + "error getting %s from EC: %d\n", + command == EC_CMD_RTC_GET_VALUE ? "time" : "alarm", + ret); + return ret; + } + + *response = msg.data.time; + + return 0; +} + +static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command, + u32 param) +{ + int ret = 0; + struct { + struct cros_ec_command msg; + struct ec_response_rtc data; + } __packed msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg.command = command; + msg.msg.outsize = sizeof(msg.data); + msg.data.time = param; + + ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); + if (ret < 0) { + dev_err(cros_ec->dev, "error setting %s on EC: %d\n", + command == EC_CMD_RTC_SET_VALUE ? "time" : "alarm", + ret); + return ret; + } + + return 0; +} + +/* Read the current time from the EC. */ +static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 time; + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, &time); + if (ret) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + rtc_time64_to_tm(time, tm); + + return 0; +} + +/* Set the current EC time. */ +static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + time64_t time; + + time = rtc_tm_to_time64(tm); + if (time < 0 || time > U32_MAX) + return -EINVAL; + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, (u32)time); + if (ret < 0) { + dev_err(dev, "error setting time: %d\n", ret); + return ret; + } + + return 0; +} + +/* Read alarm time from RTC. */ +static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 current_time, alarm_offset; + + /* + * The EC host command for getting the alarm is relative (i.e. 5 + * seconds from now) whereas rtc_wkalrm is absolute. Get the current + * RTC time first so we can calculate the relative time. + */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, &alarm_offset); + if (ret < 0) { + dev_err(dev, "error getting alarm: %d\n", ret); + return ret; + } + + rtc_time64_to_tm(current_time + alarm_offset, &alrm->time); + + return 0; +} + +/* Set the EC's RTC alarm. */ +static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + time64_t alarm_time; + u32 current_time, alarm_offset; + + /* + * The EC host command for setting the alarm is relative + * (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute. + * Get the current RTC time first so we can calculate the + * relative time. + */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + alarm_time = rtc_tm_to_time64(&alrm->time); + + if (alarm_time < 0 || alarm_time > U32_MAX) + return -EINVAL; + + if (!alrm->enabled) { + /* + * If the alarm is being disabled, send an alarm + * clear command. + */ + alarm_offset = EC_RTC_ALARM_CLEAR; + cros_ec_rtc->saved_alarm = (u32)alarm_time; + } else { + /* Don't set an alarm in the past. */ + if ((u32)alarm_time < current_time) + alarm_offset = EC_RTC_ALARM_CLEAR; + else + alarm_offset = (u32)alarm_time - current_time; + } + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset); + if (ret < 0) { + dev_err(dev, "error setting alarm: %d\n", ret); + return ret; + } + + return 0; +} + +static int cros_ec_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 current_time, alarm_offset, alarm_value; + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + if (enabled) { + /* Restore saved alarm if it's still in the future. */ + if (cros_ec_rtc->saved_alarm < current_time) + alarm_offset = EC_RTC_ALARM_CLEAR; + else + alarm_offset = cros_ec_rtc->saved_alarm - current_time; + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + alarm_offset); + if (ret < 0) { + dev_err(dev, "error restoring alarm: %d\n", ret); + return ret; + } + } else { + /* Disable alarm, saving the old alarm value. */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, + &alarm_offset); + if (ret < 0) { + dev_err(dev, "error saving alarm: %d\n", ret); + return ret; + } + + alarm_value = current_time + alarm_offset; + + /* + * If the current EC alarm is already past, we don't want + * to set an alarm when we go through the alarm irq enable + * path. + */ + if (alarm_value < current_time) + cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR; + else + cros_ec_rtc->saved_alarm = alarm_value; + + alarm_offset = EC_RTC_ALARM_CLEAR; + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + alarm_offset); + if (ret < 0) { + dev_err(dev, "error disabling alarm: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int cros_ec_rtc_event(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct cros_ec_rtc *cros_ec_rtc; + struct rtc_device *rtc; + struct cros_ec_device *cros_ec; + u32 host_event; + + cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier); + rtc = cros_ec_rtc->rtc; + cros_ec = cros_ec_rtc->cros_ec; + + host_event = cros_ec_get_host_event(cros_ec); + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) { + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + return NOTIFY_OK; + } else { + return NOTIFY_DONE; + } +} + +static const struct rtc_class_ops cros_ec_rtc_ops = { + .read_time = cros_ec_rtc_read_time, + .set_time = cros_ec_rtc_set_time, + .read_alarm = cros_ec_rtc_read_alarm, + .set_alarm = cros_ec_rtc_set_alarm, + .alarm_irq_enable = cros_ec_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(cros_ec_rtc->cros_ec->irq); + + return 0; +} + +static int cros_ec_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(cros_ec_rtc->cros_ec->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend, + cros_ec_rtc_resume); + +static int cros_ec_rtc_probe(struct platform_device *pdev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct cros_ec_rtc *cros_ec_rtc; + struct rtc_time tm; + int ret; + + cros_ec_rtc = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_rtc), + GFP_KERNEL); + if (!cros_ec_rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, cros_ec_rtc); + cros_ec_rtc->cros_ec = cros_ec; + + /* Get initial time */ + ret = cros_ec_rtc_read_time(&pdev->dev, &tm); + if (ret) { + dev_err(&pdev->dev, "failed to read RTC time\n"); + return ret; + } + + ret = device_init_wakeup(&pdev->dev, 1); + if (ret) { + dev_err(&pdev->dev, "failed to initialize wakeup\n"); + return ret; + } + + cros_ec_rtc->rtc = devm_rtc_device_register(&pdev->dev, DRV_NAME, + &cros_ec_rtc_ops, + THIS_MODULE); + if (IS_ERR(cros_ec_rtc->rtc)) { + ret = PTR_ERR(cros_ec_rtc->rtc); + dev_err(&pdev->dev, "failed to register rtc device\n"); + return ret; + } + + /* Get RTC events from the EC. */ + cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event; + ret = blocking_notifier_chain_register(&cros_ec->event_notifier, + &cros_ec_rtc->notifier); + if (ret) { + dev_err(&pdev->dev, "failed to register notifier\n"); + return ret; + } + + return 0; +} + +static int cros_ec_rtc_remove(struct platform_device *pdev) +{ + struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + ret = blocking_notifier_chain_unregister( + &cros_ec_rtc->cros_ec->event_notifier, + &cros_ec_rtc->notifier); + if (ret) { + dev_err(dev, "failed to unregister notifier\n"); + return ret; + } + + return 0; +} + +static struct platform_driver cros_ec_rtc_driver = { + .probe = cros_ec_rtc_probe, + .remove = cros_ec_rtc_remove, + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_rtc_pm_ops, + }, +}; + +module_platform_driver(cros_ec_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for Chrome OS ECs"); +MODULE_AUTHOR("Stephen Barber "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); From e9982024619c7f8f8cce97b0038a0075b135089c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Nov 2017 17:16:18 +0000 Subject: [PATCH 07/19] rtc: sun6i: ensure rtc is kfree'd on error The error return path on clk_data allocation failure does not kfree the allocated rtc object. Fix this with a kfree of rtc on the error exit path. Detected by CoverityScan, CID#1452264 ("Resource Leak") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sun6i.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 3d2216ccd860..5bc28eed1adf 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -201,8 +201,10 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2), GFP_KERNEL); - if (!clk_data) + if (!clk_data) { + kfree(rtc); return; + } spin_lock_init(&rtc->lock); From 994ec64c0a193940be7a6fd074668b9446d3b6c3 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Dec 2017 14:58:33 +0100 Subject: [PATCH 08/19] rtc: ac100: Fix multiple race conditions The probe function is not allowed to fail after registering the RTC because the following may happen: CPU0: CPU1: sys_load_module() do_init_module() do_one_initcall() cmos_do_probe() rtc_device_register() __register_chrdev() cdev->owner = struct module* open("/dev/rtc0") rtc_device_unregister() module_put() free_module() module_free(mod->module_core) /* struct module *module is now freed */ chrdev_open() spin_lock(cdev_lock) cdev_get() try_module_get() module_is_live() /* dereferences already freed struct module* */ Also, the interrupt handler: ac100_rtc_irq() is dereferencing chip->rtc but this may still be NULL when it is called, resulting in: Unable to handle kernel NULL pointer dereference at virtual address 00000194 pgd = (ptrval) [00000194] *pgd=00000000 Internal error: Oops: 5 [#1] SMP ARM Modules linked in: CPU: 0 PID: 72 Comm: irq/71-ac100-rt Not tainted 4.15.0-rc1-next-20171201-dirty #120 Hardware name: Allwinner sun8i Family task: (ptrval) task.stack: (ptrval) PC is at mutex_lock+0x14/0x3c LR is at ac100_rtc_irq+0x38/0xc8 pc : [] lr : [] psr: 60000053 sp : ee9c9f28 ip : 00000000 fp : ee9adfdc r10: 00000000 r9 : c0a04c48 r8 : c015ed18 r7 : ee9bd600 r6 : ee9c9f28 r5 : ee9af590 r4 : c0a04c48 r3 : ef3cb3c0 r2 : 00000000 r1 : ee9af590 r0 : 00000194 Flags: nZCv IRQs on FIQs off Mode SVC_32 ISA ARM Segment none Control: 10c5387d Table: 4000406a DAC: 00000051 Process irq/71-ac100-rt (pid: 72, stack limit = 0x(ptrval)) Stack: (0xee9c9f28 to 0xee9ca000) 9f20: 00000000 7c2fd1be c015ed18 ee9adf40 ee9c0400 ee9c0400 9f40: ee9adf40 c015ed34 ee9c8000 ee9adf64 ee9c0400 c015f040 ee9adf80 00000000 9f60: c015ee24 7c2fd1be ee9adfc0 ee9adf80 00000000 ee9c8000 ee9adf40 c015eef4 9f80: ef1eba34 c0138f14 ee9c8000 ee9adf80 c0138df4 00000000 00000000 00000000 9fa0: 00000000 00000000 00000000 c01010e8 00000000 00000000 00000000 00000000 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000 ffffffff ffffffff [] (mutex_lock) from [] (ac100_rtc_irq+0x38/0xc8) [] (ac100_rtc_irq) from [] (irq_thread_fn+0x1c/0x54) [] (irq_thread_fn) from [] (irq_thread+0x14c/0x214) [] (irq_thread) from [] (kthread+0x120/0x150) [] (kthread) from [] (ret_from_fork+0x14/0x2c) Solve both issues by moving to devm_rtc_allocate_device()/rtc_register_device() Reported-by: Quentin Schulz Tested-by: Quentin Schulz Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ac100.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index 9e336184491c..0e358d4b6738 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -567,6 +567,12 @@ static int ac100_rtc_probe(struct platform_device *pdev) return chip->irq; } + chip->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(chip->rtc)) + return PTR_ERR(chip->rtc); + + chip->rtc->ops = &ac100_rtc_ops; + ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL, ac100_rtc_irq, IRQF_SHARED | IRQF_ONESHOT, @@ -586,17 +592,16 @@ static int ac100_rtc_probe(struct platform_device *pdev) /* clear counter alarm pending interrupts */ regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE); - chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-ac100", - &ac100_rtc_ops, THIS_MODULE); - if (IS_ERR(chip->rtc)) { - dev_err(&pdev->dev, "unable to register device\n"); - return PTR_ERR(chip->rtc); - } - ret = ac100_rtc_register_clks(chip); if (ret) return ret; + ret = rtc_register_device(chip->rtc); + if (ret) { + dev_err(&pdev->dev, "unable to register device\n"); + return ret; + } + dev_info(&pdev->dev, "RTC enabled\n"); return 0; From 2da6877f0e2ced7825a31556ada6b2ac2e50877f Mon Sep 17 00:00:00 2001 From: Andreas Platschek Date: Wed, 6 Dec 2017 20:42:38 +0100 Subject: [PATCH 09/19] rtc: omap: fix unbalanced clk_prepare_enable/clk_disable_unprepare There are 2 error paths after clk_prepare_enable() was called, where clk_disable_unprepare() is missing. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Andreas Platschek Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-omap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 1d666ac9ef70..09ef802d6e54 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -753,8 +753,10 @@ static int omap_rtc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rtc->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(rtc->base)) + if (IS_ERR(rtc->base)) { + clk_disable_unprepare(rtc->clk); return PTR_ERR(rtc->base); + } platform_set_drvdata(pdev, rtc); @@ -887,6 +889,7 @@ static int omap_rtc_probe(struct platform_device *pdev) return 0; err: + clk_disable_unprepare(rtc->clk); device_init_wakeup(&pdev->dev, false); rtc->type->lock(rtc); pm_runtime_put_sync(&pdev->dev); From cd8c0bb2bd57c48c451ebea86065257ef2500b00 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 13 Dec 2017 21:55:50 +0800 Subject: [PATCH 10/19] rtc: r7301: Fix a possible sleep-in-atomic bug in rtc7301_read_time The driver may sleep under a spinlock. The function call path is: rtc7301_read_time (acquire the spinlock) rtc7301_wait_while_busy usleep_range --> may sleep To fix it, usleep_range is replaced with udelay. This bug is found by my static analysis tool(DSAC) and checked by my code review. Signed-off-by: Jia-Ju Bai Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-r7301.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c index 28d540885f3d..d846e9703ad6 100644 --- a/drivers/rtc/rtc-r7301.c +++ b/drivers/rtc/rtc-r7301.c @@ -95,7 +95,7 @@ static int rtc7301_wait_while_busy(struct rtc7301_priv *priv) if (!(val & RTC7301_CONTROL_BUSY)) return 0; - usleep_range(200, 300); + udelay(300); } return -ETIMEDOUT; From 298c8545206b2dd963079ff48fafd909e099856e Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 13 Dec 2017 21:56:04 +0800 Subject: [PATCH 11/19] rtc: r7301: Fix a possible sleep-in-atomic bug in rtc7301_set_time The driver may sleep under a spinlock. The function call path is: rtc7301_set_time (acquire the spinlock) usleep_range --> may sleep To fix it, usleep_range is replaced with udelay. This bug is found by my static analysis tool(DSAC) and checked by my code review. Signed-off-by: Jia-Ju Bai Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-r7301.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c index d846e9703ad6..500e8c8a2605 100644 --- a/drivers/rtc/rtc-r7301.c +++ b/drivers/rtc/rtc-r7301.c @@ -235,7 +235,7 @@ static int rtc7301_set_time(struct device *dev, struct rtc_time *tm) spin_lock_irqsave(&priv->lock, flags); rtc7301_stop(priv); - usleep_range(200, 300); + udelay(300); rtc7301_select_bank(priv, 0); rtc7301_write_time(priv, tm, false); rtc7301_start(priv); From 3394f594de244632c682f9faee054063158f8941 Mon Sep 17 00:00:00 2001 From: Patrick Bruenn Date: Mon, 18 Dec 2017 12:51:30 +0100 Subject: [PATCH 12/19] dt-bindings: rtc: add bindings for i.MX53 SRTC Document the binding for i.MX53 SRTC implemented by rtc-mxc_v2 Signed-off-by: Patrick Bruenn Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/rtc-mxc_v2.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/rtc-mxc_v2.txt diff --git a/Documentation/devicetree/bindings/rtc/rtc-mxc_v2.txt b/Documentation/devicetree/bindings/rtc/rtc-mxc_v2.txt new file mode 100644 index 000000000000..79d7e87b0d91 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-mxc_v2.txt @@ -0,0 +1,17 @@ +* i.MX53 Secure Real Time Clock (SRTC) + +Required properties: +- compatible: should be: "fsl,imx53-rtc" +- reg: physical base address of the controller and length of memory mapped + region. +- clocks: should contain the phandle for the rtc clock +- interrupts: rtc alarm interrupt + +Example: + +rtc@53fa4000 { + compatible = "fsl,imx53-rtc"; + reg = <0x53fa4000 0x4000>; + interrupts = <24>; + clocks = <&clks IMX5_CLK_SRTC_GATE>; +}; From 83c880f79e88cc60593f627e612326f5d43db81e Mon Sep 17 00:00:00 2001 From: Patrick Bruenn Date: Mon, 18 Dec 2017 12:51:32 +0100 Subject: [PATCH 13/19] rtc: add mxc driver for i.MX53 SRTC Neither rtc-imxdi, rtc-mxc nor rtc-snvs are compatible with i.MX53. This is driver enables support for the low power domain SRTC features: - 32-bit MSB of non-rollover time counter - 32-bit alarm register Select the new config option RTC_DRV_MXC_V2 to build this driver Based on: http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/drivers/rtc/rtc-mxc_v2.c?h=imx_2.6.35_11.09.01 Signed-off-by: Patrick Bruenn Acked-by: Philippe Ombredanne Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-mxc_v2.c | 422 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 433 insertions(+) create mode 100644 drivers/rtc/rtc-mxc_v2.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 64f4b164932a..b71cc7b5fbc9 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1699,6 +1699,16 @@ config RTC_DRV_MXC This driver can also be built as a module, if so, the module will be called "rtc-mxc". +config RTC_DRV_MXC_V2 + tristate "Freescale MXC Real Time Clock for i.MX53" + depends on ARCH_MXC + help + If you say yes here you get support for the Freescale MXC + SRTC module in i.MX53 processor. + + This driver can also be built as a module, if so, the module + will be called "rtc-mxc_v2". + config RTC_DRV_SNVS tristate "Freescale SNVS RTC support" select REGMAP_MMIO diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index cf9d29e34dbd..6dfbbe9cd8a2 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o +obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c new file mode 100644 index 000000000000..b637095b0716 --- /dev/null +++ b/drivers/rtc/rtc-mxc_v2.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock (RTC) Driver for i.MX53 + * Copyright (c) 2004-2011 Freescale Semiconductor, Inc. + * Copyright (c) 2017 Beckhoff Automation GmbH & Co. KG + */ + +#include +#include +#include +#include +#include + +#define SRTC_LPPDR_INIT 0x41736166 /* init for glitch detect */ + +#define SRTC_LPCR_EN_LP BIT(3) /* lp enable */ +#define SRTC_LPCR_WAE BIT(4) /* lp wakeup alarm enable */ +#define SRTC_LPCR_ALP BIT(7) /* lp alarm flag */ +#define SRTC_LPCR_NSA BIT(11) /* lp non secure access */ +#define SRTC_LPCR_NVE BIT(14) /* lp non valid state exit bit */ +#define SRTC_LPCR_IE BIT(15) /* lp init state exit bit */ + +#define SRTC_LPSR_ALP BIT(3) /* lp alarm flag */ +#define SRTC_LPSR_NVES BIT(14) /* lp non-valid state exit status */ +#define SRTC_LPSR_IES BIT(15) /* lp init state exit status */ + +#define SRTC_LPSCMR 0x00 /* LP Secure Counter MSB Reg */ +#define SRTC_LPSCLR 0x04 /* LP Secure Counter LSB Reg */ +#define SRTC_LPSAR 0x08 /* LP Secure Alarm Reg */ +#define SRTC_LPCR 0x10 /* LP Control Reg */ +#define SRTC_LPSR 0x14 /* LP Status Reg */ +#define SRTC_LPPDR 0x18 /* LP Power Supply Glitch Detector Reg */ + +/* max. number of retries to read registers, 120 was max during test */ +#define REG_READ_TIMEOUT 2000 + +struct mxc_rtc_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + struct clk *clk; + spinlock_t lock; /* protects register access */ + int irq; +}; + +/* + * This function does write synchronization for writes to the lp srtc block. + * To take care of the asynchronous CKIL clock, all writes from the IP domain + * will be synchronized to the CKIL domain. + * The caller should hold the pdata->lock + */ +static void mxc_rtc_sync_lp_locked(struct device *dev, void __iomem *ioaddr) +{ + unsigned int i; + + /* Wait for 3 CKIL cycles */ + for (i = 0; i < 3; i++) { + const u32 count = readl(ioaddr + SRTC_LPSCLR); + unsigned int timeout = REG_READ_TIMEOUT; + + while ((readl(ioaddr + SRTC_LPSCLR)) == count) { + if (!--timeout) { + dev_err_once(dev, "SRTC_LPSCLR stuck! Check your hw.\n"); + return; + } + } + } +} + +/* This function is the RTC interrupt service routine. */ +static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + unsigned long flags; + u32 lp_status; + u32 lp_cr; + + spin_lock_irqsave(&pdata->lock, flags); + if (clk_enable(pdata->clk)) { + spin_unlock_irqrestore(&pdata->lock, flags); + return IRQ_NONE; + } + + lp_status = readl(ioaddr + SRTC_LPSR); + lp_cr = readl(ioaddr + SRTC_LPCR); + + /* update irq data & counter */ + if (lp_status & SRTC_LPSR_ALP) { + if (lp_cr & SRTC_LPCR_ALP) + rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF); + + /* disable further lp alarm interrupts */ + lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE); + } + + /* Update interrupt enables */ + writel(lp_cr, ioaddr + SRTC_LPCR); + + /* clear interrupt status */ + writel(lp_status, ioaddr + SRTC_LPSR); + + mxc_rtc_sync_lp_locked(dev, ioaddr); + clk_disable(pdata->clk); + spin_unlock_irqrestore(&pdata->lock, flags); + return IRQ_HANDLED; +} + +/* + * Enable clk and aquire spinlock + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_lock(struct mxc_rtc_data *const pdata) +{ + int ret; + + spin_lock_irq(&pdata->lock); + ret = clk_enable(pdata->clk); + if (ret) { + spin_unlock_irq(&pdata->lock); + return ret; + } + return 0; +} + +static int mxc_rtc_unlock(struct mxc_rtc_data *const pdata) +{ + clk_disable(pdata->clk); + spin_unlock_irq(&pdata->lock); + return 0; +} + +/* + * This function reads the current RTC time into tm in Gregorian date. + * + * @param tm contains the RTC time value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + const int clk_failed = clk_enable(pdata->clk); + + if (!clk_failed) { + const time64_t now = readl(pdata->ioaddr + SRTC_LPSCMR); + + rtc_time64_to_tm(now, tm); + clk_disable(pdata->clk); + return 0; + } + return clk_failed; +} + +/* + * This function sets the internal RTC time based on tm in Gregorian date. + * + * @param tm the time value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + time64_t time = rtc_tm_to_time64(tm); + int ret; + + if (time > U32_MAX) { + dev_err(dev, "RTC exceeded by %llus\n", time - U32_MAX); + return -EINVAL; + } + + ret = mxc_rtc_lock(pdata); + if (ret) + return ret; + + writel(time, pdata->ioaddr + SRTC_LPSCMR); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + return mxc_rtc_unlock(pdata); +} + +/* + * This function reads the current alarm value into the passed in \b alrm + * argument. It updates the \b alrm's pending field value based on the whether + * an alarm interrupt occurs or not. + * + * @param alrm contains the RTC alarm value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + int ret; + + ret = mxc_rtc_lock(pdata); + if (ret) + return ret; + + rtc_time_to_tm(readl(ioaddr + SRTC_LPSAR), &alrm->time); + alrm->pending = !!(readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_ALP); + return mxc_rtc_unlock(pdata); +} + +/* + * Enable/Disable alarm interrupt + * The caller should hold the pdata->lock + */ +static void mxc_rtc_alarm_irq_enable_locked(struct mxc_rtc_data *pdata, + unsigned int enable) +{ + u32 lp_cr = readl(pdata->ioaddr + SRTC_LPCR); + + if (enable) + lp_cr |= (SRTC_LPCR_ALP | SRTC_LPCR_WAE); + else + lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE); + + writel(lp_cr, pdata->ioaddr + SRTC_LPCR); +} + +static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + int ret = mxc_rtc_lock(pdata); + + if (ret) + return ret; + + mxc_rtc_alarm_irq_enable_locked(pdata, enable); + return mxc_rtc_unlock(pdata); +} + +/* + * This function sets the RTC alarm based on passed in alrm. + * + * @param alrm the alarm value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + const time64_t time = rtc_tm_to_time64(&alrm->time); + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + int ret = mxc_rtc_lock(pdata); + + if (ret) + return ret; + + if (time > U32_MAX) { + dev_err(dev, "Hopefully I am out of service by then :-(\n"); + return -EINVAL; + } + + writel((u32)time, pdata->ioaddr + SRTC_LPSAR); + + /* clear alarm interrupt status bit */ + writel(SRTC_LPSR_ALP, pdata->ioaddr + SRTC_LPSR); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + + mxc_rtc_alarm_irq_enable_locked(pdata, alrm->enabled); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + mxc_rtc_unlock(pdata); + return ret; +} + +static const struct rtc_class_ops mxc_rtc_ops = { + .read_time = mxc_rtc_read_time, + .set_time = mxc_rtc_set_time, + .read_alarm = mxc_rtc_read_alarm, + .set_alarm = mxc_rtc_set_alarm, + .alarm_irq_enable = mxc_rtc_alarm_irq_enable, +}; + +static int mxc_rtc_wait_for_flag(void *__iomem ioaddr, int flag) +{ + unsigned int timeout = REG_READ_TIMEOUT; + + while (!(readl(ioaddr) & flag)) { + if (!--timeout) + return -EBUSY; + } + return 0; +} + +static int mxc_rtc_probe(struct platform_device *pdev) +{ + struct mxc_rtc_data *pdata; + struct resource *res; + void __iomem *ioaddr; + int ret = 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdata->ioaddr)) + return PTR_ERR(pdata->ioaddr); + + ioaddr = pdata->ioaddr; + + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "unable to get rtc clock!\n"); + return PTR_ERR(pdata->clk); + } + + spin_lock_init(&pdata->lock); + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq < 0) + return pdata->irq; + + device_init_wakeup(&pdev->dev, 1); + + ret = clk_prepare_enable(pdata->clk); + if (ret) + return ret; + /* initialize glitch detect */ + writel(SRTC_LPPDR_INIT, ioaddr + SRTC_LPPDR); + + /* clear lp interrupt status */ + writel(0xFFFFFFFF, ioaddr + SRTC_LPSR); + + /* move out of init state */ + writel((SRTC_LPCR_IE | SRTC_LPCR_NSA), ioaddr + SRTC_LPCR); + ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_IES); + if (ret) { + dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_IES\n"); + clk_disable_unprepare(pdata->clk); + return ret; + } + + /* move out of non-valid state */ + writel((SRTC_LPCR_IE | SRTC_LPCR_NVE | SRTC_LPCR_NSA | + SRTC_LPCR_EN_LP), ioaddr + SRTC_LPCR); + ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_NVES); + if (ret) { + dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_NVES\n"); + clk_disable_unprepare(pdata->clk); + return ret; + } + + clk_disable(pdata->clk); + platform_set_drvdata(pdev, pdata); + ret = + devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt, 0, + pdev->name, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "interrupt not available.\n"); + clk_unprepare(pdata->clk); + return ret; + } + + pdata->rtc = + devm_rtc_device_register(&pdev->dev, pdev->name, &mxc_rtc_ops, + THIS_MODULE); + if (IS_ERR(pdata->rtc)) { + clk_unprepare(pdata->clk); + return PTR_ERR(pdata->rtc); + } + + return 0; +} + +static int __exit mxc_rtc_remove(struct platform_device *pdev) +{ + struct mxc_rtc_data *pdata = platform_get_drvdata(pdev); + + clk_disable_unprepare(pdata->clk); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mxc_rtc_suspend(struct device *dev) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pdata->irq); + + return 0; +} + +static int mxc_rtc_resume(struct device *dev) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pdata->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mxc_rtc_pm_ops, mxc_rtc_suspend, mxc_rtc_resume); + +static const struct of_device_id mxc_ids[] = { + { .compatible = "fsl,imx53-rtc", }, + {} +}; + +static struct platform_driver mxc_rtc_driver = { + .driver = { + .name = "mxc_rtc_v2", + .of_match_table = mxc_ids, + .pm = &mxc_rtc_pm_ops, + }, + .probe = mxc_rtc_probe, + .remove = mxc_rtc_remove, +}; + +module_platform_driver(mxc_rtc_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Real Time Clock (RTC) Driver for i.MX53"); +MODULE_LICENSE("GPL"); From a9c705a8bd52d8a5205cc779d6e64af4321a9fa8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 21 Dec 2017 09:43:24 -0200 Subject: [PATCH 14/19] rtc: mxc_v2: Remove unnecessary platform_get_resource() error check devm_ioremap_resource() already checks if the resource is NULL, so remove the unnecessary platform_get_resource() error check. Signed-off-by: Fabio Estevam Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mxc_v2.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c index b637095b0716..eecc0cf9ee95 100644 --- a/drivers/rtc/rtc-mxc_v2.c +++ b/drivers/rtc/rtc-mxc_v2.c @@ -296,9 +296,6 @@ static int mxc_rtc_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pdata->ioaddr)) return PTR_ERR(pdata->ioaddr); From db00d38e7b59ae913cfa10f1c40bf656a969fb82 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 2 Jan 2018 10:43:33 +0100 Subject: [PATCH 15/19] rtc: mxc_v2: remove __exit annotation The mxc_rtc_remove is incorrectly annotated as __exit: `mxc_rtc_remove' referenced in section `.data' of drivers/rtc/rtc-mxc_v2.o: defined in discarded section `.exit.text' of drivers/rtc/rtc-mxc_v2.o This should not be done, as devices can be dynamically bound and unbound to a driver. Fixes: 54c47014b474 ("rtc: add mxc driver for i.MX53 SRTC") Signed-off-by: Arnd Bergmann Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mxc_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c index eecc0cf9ee95..784221dfc9c7 100644 --- a/drivers/rtc/rtc-mxc_v2.c +++ b/drivers/rtc/rtc-mxc_v2.c @@ -365,7 +365,7 @@ static int mxc_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit mxc_rtc_remove(struct platform_device *pdev) +static int mxc_rtc_remove(struct platform_device *pdev) { struct mxc_rtc_data *pdata = platform_get_drvdata(pdev); From 81fdc7afdd7dbf2fdd5139049b887121472015ef Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 12 Jan 2018 00:00:21 +0100 Subject: [PATCH 16/19] Documentation: rtc: move sysfs documentation to ABI The sysfs documentation should be located under Documentation/ABI. Signed-off-by: Alexandre Belloni --- Documentation/ABI/testing/sysfs-class-rtc | 89 +++++++++++++++++++++++ Documentation/rtc.txt | 32 -------- 2 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-rtc diff --git a/Documentation/ABI/testing/sysfs-class-rtc b/Documentation/ABI/testing/sysfs-class-rtc new file mode 100644 index 000000000000..a19f5e2def3a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-rtc @@ -0,0 +1,89 @@ +What: /sys/class/rtc/ +Date: March 2006 +KernelVersion: 2.6.17 +Contact: linux-rtc@vger.kernel.org +Description: + The rtc/ class subdirectory belongs to the RTC subsystem. + +What: /sys/class/rtc/rtcX/ +Date: March 2006 +KernelVersion: 2.6.17 +Contact: linux-rtc@vger.kernel.org +Description: + The /sys/class/rtc/rtc{0,1,2,3,...} directories correspond + to each RTC device. + +What: /sys/class/rtc/rtcX/date +Date: March 2006 +KernelVersion: 2.6.17 +Contact: linux-rtc@vger.kernel.org +Description: + RTC-provided date in YYYY-MM-DD format + +What: /sys/class/rtc/rtcX/hctosys +Date: September 2009 +KernelVersion: 2.6.32 +Contact: linux-rtc@vger.kernel.org +Description: + 1 if the RTC provided the system time at boot via the + CONFIG_RTC_HCTOSYS kernel option, 0 otherwise + +What: /sys/class/rtc/rtcX/max_user_freq +Date: October 2007 +KernelVersion: 2.6.24 +Contact: linux-rtc@vger.kernel.org +Description: + The maximum interrupt rate an unprivileged user may request + from this RTC. + +What: /sys/class/rtc/rtcX/name +Date: March 2006 +KernelVersion: 2.6.17 +Contact: linux-rtc@vger.kernel.org +Description: + The name of the RTC corresponding to this sysfs directory + +What: /sys/class/rtc/rtcX/since_epoch +Date: March 2006 +KernelVersion: 2.6.17 +Contact: linux-rtc@vger.kernel.org +Description: + RTC-provided time as the number of seconds since the epoch + +What: /sys/class/rtc/rtcX/time +Date: March 2006 +KernelVersion: 2.6.17 +Contact: linux-rtc@vger.kernel.org +Description: + RTC-provided time in 24-hour notation (hh:mm:ss) + +What: /sys/class/rtc/rtcX/*/nvmem +Date: February 2016 +KernelVersion: 4.6 +Contact: linux-rtc@vger.kernel.org +Description: + The non volatile storage exported as a raw file, as described in + Documentation/nvmem/nvmem.txt + +What: /sys/class/rtc/rtcX/offset +Date: February 2016 +KernelVersion: 4.6 +Contact: linux-rtc@vger.kernel.org +Description: + The amount which the rtc clock has been adjusted in firmware. + Visible only if the driver supports clock offset adjustment. + The unit is parts per billion, i.e. The number of clock ticks + which are added to or removed from the rtc's base clock per + billion ticks. A positive value makes a day pass more slowly, + longer, and a negative value makes a day pass more quickly. + +What: /sys/class/rtc/rtcX/wakealarm +Date: February 2007 +KernelVersion: 2.6.20 +Contact: linux-rtc@vger.kernel.org +Description: + The time at which the clock will generate a system wakeup event. + This is a one shot wakeup event, so must be reset after wake if + a daily wakeup is required. Format is seconds since the epoch by + default, or if there's a leading +, seconds in the future, or if + there is a leading +=, seconds ahead of the current alarm. diff --git a/Documentation/rtc.txt b/Documentation/rtc.txt index c0c977445fb9..f521262cdc85 100644 --- a/Documentation/rtc.txt +++ b/Documentation/rtc.txt @@ -136,38 +136,6 @@ a high functionality RTC is integrated into the SOC. That system might read the system clock from the discrete RTC, but use the integrated one for all other tasks, because of its greater functionality. -SYSFS interface ---------------- - -The sysfs interface under /sys/class/rtc/rtcN provides access to various -rtc attributes without requiring the use of ioctls. All dates and times -are in the RTC's timezone, rather than in system time. - -================ ============================================================== -date RTC-provided date -hctosys 1 if the RTC provided the system time at boot via the - CONFIG_RTC_HCTOSYS kernel option, 0 otherwise -max_user_freq The maximum interrupt rate an unprivileged user may request - from this RTC. -name The name of the RTC corresponding to this sysfs directory -since_epoch The number of seconds since the epoch according to the RTC -time RTC-provided time -wakealarm The time at which the clock will generate a system wakeup - event. This is a one shot wakeup event, so must be reset - after wake if a daily wakeup is required. Format is seconds - since the epoch by default, or if there's a leading +, seconds - in the future, or if there is a leading +=, seconds ahead of - the current alarm. -offset The amount which the rtc clock has been adjusted in firmware. - Visible only if the driver supports clock offset adjustment. - The unit is parts per billion, i.e. The number of clock ticks - which are added to or removed from the rtc's base clock per - billion ticks. A positive value makes a day pass more slowly, - longer, and a negative value makes a day pass more quickly. -*/nvmem The non volatile storage exported as a raw file, as described - in Documentation/nvmem/nvmem.txt -================ ============================================================== - IOCTL interface --------------- From d5bc5cde844ebecbc6fac2d65d23610cfd3affc3 Mon Sep 17 00:00:00 2001 From: Aishwarya Pant Date: Mon, 1 Jan 2018 23:00:42 +0530 Subject: [PATCH 17/19] Documentation: rtc: add sysfs file permissions Annotate the sysfs interface of rtc with file specific permissions (RO/RW). Signed-off-by: Aishwarya Pant Signed-off-by: Alexandre Belloni --- Documentation/ABI/testing/sysfs-class-rtc | 40 ++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-rtc b/Documentation/ABI/testing/sysfs-class-rtc index a19f5e2def3a..cf60412882f0 100644 --- a/Documentation/ABI/testing/sysfs-class-rtc +++ b/Documentation/ABI/testing/sysfs-class-rtc @@ -18,14 +18,14 @@ Date: March 2006 KernelVersion: 2.6.17 Contact: linux-rtc@vger.kernel.org Description: - RTC-provided date in YYYY-MM-DD format + (RO) RTC-provided date in YYYY-MM-DD format What: /sys/class/rtc/rtcX/hctosys Date: September 2009 KernelVersion: 2.6.32 Contact: linux-rtc@vger.kernel.org Description: - 1 if the RTC provided the system time at boot via the + (RO) 1 if the RTC provided the system time at boot via the CONFIG_RTC_HCTOSYS kernel option, 0 otherwise What: /sys/class/rtc/rtcX/max_user_freq @@ -33,7 +33,7 @@ Date: October 2007 KernelVersion: 2.6.24 Contact: linux-rtc@vger.kernel.org Description: - The maximum interrupt rate an unprivileged user may request + (RW) The maximum interrupt rate an unprivileged user may request from this RTC. What: /sys/class/rtc/rtcX/name @@ -41,49 +41,51 @@ Date: March 2006 KernelVersion: 2.6.17 Contact: linux-rtc@vger.kernel.org Description: - The name of the RTC corresponding to this sysfs directory + (RO) The name of the RTC corresponding to this sysfs directory What: /sys/class/rtc/rtcX/since_epoch Date: March 2006 KernelVersion: 2.6.17 Contact: linux-rtc@vger.kernel.org Description: - RTC-provided time as the number of seconds since the epoch + (RO) RTC-provided time as the number of seconds since the epoch What: /sys/class/rtc/rtcX/time Date: March 2006 KernelVersion: 2.6.17 Contact: linux-rtc@vger.kernel.org Description: - RTC-provided time in 24-hour notation (hh:mm:ss) + (RO) RTC-provided time in 24-hour notation (hh:mm:ss) What: /sys/class/rtc/rtcX/*/nvmem Date: February 2016 KernelVersion: 4.6 Contact: linux-rtc@vger.kernel.org Description: - The non volatile storage exported as a raw file, as described in - Documentation/nvmem/nvmem.txt + (RW) The non volatile storage exported as a raw file, as + described in Documentation/nvmem/nvmem.txt What: /sys/class/rtc/rtcX/offset Date: February 2016 KernelVersion: 4.6 Contact: linux-rtc@vger.kernel.org Description: - The amount which the rtc clock has been adjusted in firmware. - Visible only if the driver supports clock offset adjustment. - The unit is parts per billion, i.e. The number of clock ticks - which are added to or removed from the rtc's base clock per - billion ticks. A positive value makes a day pass more slowly, - longer, and a negative value makes a day pass more quickly. + (RW) The amount which the rtc clock has been adjusted in + firmware. Visible only if the driver supports clock offset + adjustment. The unit is parts per billion, i.e. The number of + clock ticks which are added to or removed from the rtc's base + clock per billion ticks. A positive value makes a day pass more + slowly, longer, and a negative value makes a day pass more + quickly. What: /sys/class/rtc/rtcX/wakealarm Date: February 2007 KernelVersion: 2.6.20 Contact: linux-rtc@vger.kernel.org Description: - The time at which the clock will generate a system wakeup event. - This is a one shot wakeup event, so must be reset after wake if - a daily wakeup is required. Format is seconds since the epoch by - default, or if there's a leading +, seconds in the future, or if - there is a leading +=, seconds ahead of the current alarm. + (RW) The time at which the clock will generate a system wakeup + event. This is a one shot wakeup event, so must be reset after + wake if a daily wakeup is required. Format is seconds since the + epoch by default, or if there's a leading +, seconds in the + future, or if there is a leading +=, seconds ahead of the + current alarm. From 796c0ad79ee65abb9c891e44ccd6e347a6be486f Mon Sep 17 00:00:00 2001 From: Aishwarya Pant Date: Mon, 1 Jan 2018 23:01:24 +0530 Subject: [PATCH 18/19] Documentation: rtc: move iotcl interface documentation to ABI The ioctl interface should be in Documentation/ABI along with the rest of the interfaces. Signed-off-by: Aishwarya Pant Signed-off-by: Alexandre Belloni --- Documentation/ABI/testing/rtc-cdev | 42 +++++++++++++++++++++++++ Documentation/rtc.txt | 49 ++---------------------------- 2 files changed, 44 insertions(+), 47 deletions(-) create mode 100644 Documentation/ABI/testing/rtc-cdev diff --git a/Documentation/ABI/testing/rtc-cdev b/Documentation/ABI/testing/rtc-cdev new file mode 100644 index 000000000000..97447283f13b --- /dev/null +++ b/Documentation/ABI/testing/rtc-cdev @@ -0,0 +1,42 @@ +What: /dev/rtcX +Date: April 2005 +KernelVersion: 2.6.12 +Contact: linux-rtc@vger.kernel.org +Description: + The ioctl interface to drivers for real-time clocks (RTCs). + Following actions are supported: + + * RTC_RD_TIME, RTC_SET_TIME: Read or set the RTC time. Time + format is a Gregorian calendar date and 24 hour wall clock + time. + + * RTC_AIE_ON, RTC_AIE_OFF: Enable or disable the alarm interrupt + for RTCs that support alarms + + * RTC_ALM_READ, RTC_ALM_SET: Read or set the alarm time for + RTCs that support alarms. Can be set upto 24 hours in the + future. Requires a separate RTC_AIE_ON call to enable the + alarm interrupt. (Prefer to use RTC_WKALM_*) + + * RTC_WKALM_RD, RTC_WKALM_SET: For RTCs that support a more + powerful interface, which can issue alarms beyond 24 hours and + enable IRQs in the same request. + + * RTC_PIE_ON, RTC_PIE_OFF: Enable or disable the periodic + interrupt for RTCs that support periodic interrupts. + + * RTC_UIE_ON, RTC_UIE_OFF: Enable or disable the update + interrupt for RTCs that support it. + + * RTC_IRQP_READ, RTC_IRQP_SET: Read or set the frequency for + periodic interrupts for RTCs that support periodic interrupts. + Requires a separate RTC_PIE_ON call to enable the periodic + interrupts. + + The ioctl() calls supported by the older /dev/rtc interface are + also supported by the newer RTC class framework. However, + because the chips and systems are not standardized, some PC/AT + functionality might not be provided. And in the same way, some + newer features -- including those enabled by ACPI -- are exposed + by the RTC class framework, but can't be supported by the older + driver. diff --git a/Documentation/rtc.txt b/Documentation/rtc.txt index f521262cdc85..a129acf38537 100644 --- a/Documentation/rtc.txt +++ b/Documentation/rtc.txt @@ -136,50 +136,5 @@ a high functionality RTC is integrated into the SOC. That system might read the system clock from the discrete RTC, but use the integrated one for all other tasks, because of its greater functionality. -IOCTL interface ---------------- - -The ioctl() calls supported by /dev/rtc are also supported by the RTC class -framework. However, because the chips and systems are not standardized, -some PC/AT functionality might not be provided. And in the same way, some -newer features -- including those enabled by ACPI -- are exposed by the -RTC class framework, but can't be supported by the older driver. - - * RTC_RD_TIME, RTC_SET_TIME ... every RTC supports at least reading - time, returning the result as a Gregorian calendar date and 24 hour - wall clock time. To be most useful, this time may also be updated. - - * RTC_AIE_ON, RTC_AIE_OFF, RTC_ALM_SET, RTC_ALM_READ ... when the RTC - is connected to an IRQ line, it can often issue an alarm IRQ up to - 24 hours in the future. (Use RTC_WKALM_* by preference.) - - * RTC_WKALM_SET, RTC_WKALM_RD ... RTCs that can issue alarms beyond - the next 24 hours use a slightly more powerful API, which supports - setting the longer alarm time and enabling its IRQ using a single - request (using the same model as EFI firmware). - - * RTC_UIE_ON, RTC_UIE_OFF ... if the RTC offers IRQs, the RTC framework - will emulate this mechanism. - - * RTC_PIE_ON, RTC_PIE_OFF, RTC_IRQP_SET, RTC_IRQP_READ ... these icotls - are emulated via a kernel hrtimer. - -In many cases, the RTC alarm can be a system wake event, used to force -Linux out of a low power sleep state (or hibernation) back to a fully -operational state. For example, a system could enter a deep power saving -state until it's time to execute some scheduled tasks. - -Note that many of these ioctls are handled by the common rtc-dev interface. -Some common examples: - - * RTC_RD_TIME, RTC_SET_TIME: the read_time/set_time functions will be - called with appropriate values. - - * RTC_ALM_SET, RTC_ALM_READ, RTC_WKALM_SET, RTC_WKALM_RD: gets or sets - the alarm rtc_timer. May call the set_alarm driver function. - - * RTC_IRQP_SET, RTC_IRQP_READ: These are emulated by the generic code. - - * RTC_PIE_ON, RTC_PIE_OFF: These are also emulated by the generic code. - -If all else fails, check out the tools/testing/selftests/timers/rtctest.c test! +Check out tools/testing/selftests/timers/rtctest.c for an example usage of the +ioctl interface. From 4402be2b4ca7f53b6213a9e6b3abe9cdcc683e9b Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 18 Jan 2018 21:08:46 +0100 Subject: [PATCH 19/19] rtc: remove rtc-at32ap700x Since PLATFORM_AT32AP is an AVR32 platform which was removed, the rtc driver rtc-at32ap700x is useless. This patch remove it. Signed-off-by: Corentin Labbe Acked-by: Hans-Christian Noren Egtvedt Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 7 - drivers/rtc/Makefile | 1 - drivers/rtc/rtc-at32ap700x.c | 287 ----------------------------------- 3 files changed, 295 deletions(-) delete mode 100644 drivers/rtc/rtc-at32ap700x.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b71cc7b5fbc9..8ab5f0a5d323 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1402,13 +1402,6 @@ config RTC_DRV_PL031 To compile this driver as a module, choose M here: the module will be called rtc-pl031. -config RTC_DRV_AT32AP700X - tristate "AT32AP700X series RTC" - depends on PLATFORM_AT32AP || COMPILE_TEST - help - Driver for the internal RTC (Realtime Clock) on Atmel AVR32 - AT32AP700x family processors. - config RTC_DRV_AT91RM9200 tristate "AT91RM9200 or some AT91SAM9 RTC" depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6dfbbe9cd8a2..4fbf87e45a7c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -33,7 +33,6 @@ obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o -obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c deleted file mode 100644 index de8bf56a41e7..000000000000 --- a/drivers/rtc/rtc-at32ap700x.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * An RTC driver for the AVR32 AT32AP700x processor series. - * - * Copyright (C) 2007 Atmel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -/* - * This is a bare-bones RTC. It runs during most system sleep states, but has - * no battery backup and gets reset during system restart. It must be - * initialized from an external clock (network, I2C, etc) before it can be of - * much use. - * - * The alarm functionality is limited by the hardware, not supporting - * periodic interrupts. - */ - -#define RTC_CTRL 0x00 -#define RTC_CTRL_EN 0 -#define RTC_CTRL_PCLR 1 -#define RTC_CTRL_TOPEN 2 -#define RTC_CTRL_PSEL 8 - -#define RTC_VAL 0x04 - -#define RTC_TOP 0x08 - -#define RTC_IER 0x10 -#define RTC_IER_TOPI 0 - -#define RTC_IDR 0x14 -#define RTC_IDR_TOPI 0 - -#define RTC_IMR 0x18 -#define RTC_IMR_TOPI 0 - -#define RTC_ISR 0x1c -#define RTC_ISR_TOPI 0 - -#define RTC_ICR 0x20 -#define RTC_ICR_TOPI 0 - -#define RTC_BIT(name) (1 << RTC_##name) -#define RTC_BF(name, value) ((value) << RTC_##name) - -#define rtc_readl(dev, reg) \ - __raw_readl((dev)->regs + RTC_##reg) -#define rtc_writel(dev, reg, value) \ - __raw_writel((value), (dev)->regs + RTC_##reg) - -struct rtc_at32ap700x { - struct rtc_device *rtc; - void __iomem *regs; - unsigned long alarm_time; - unsigned long irq; - /* Protect against concurrent register access. */ - spinlock_t lock; -}; - -static int at32_rtc_readtime(struct device *dev, struct rtc_time *tm) -{ - struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); - unsigned long now; - - now = rtc_readl(rtc, VAL); - rtc_time_to_tm(now, tm); - - return 0; -} - -static int at32_rtc_settime(struct device *dev, struct rtc_time *tm) -{ - struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); - unsigned long now; - int ret; - - ret = rtc_tm_to_time(tm, &now); - if (ret == 0) - rtc_writel(rtc, VAL, now); - - return ret; -} - -static int at32_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); - - spin_lock_irq(&rtc->lock); - rtc_time_to_tm(rtc->alarm_time, &alrm->time); - alrm->enabled = rtc_readl(rtc, IMR) & RTC_BIT(IMR_TOPI) ? 1 : 0; - alrm->pending = rtc_readl(rtc, ISR) & RTC_BIT(ISR_TOPI) ? 1 : 0; - spin_unlock_irq(&rtc->lock); - - return 0; -} - -static int at32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); - unsigned long rtc_unix_time; - unsigned long alarm_unix_time; - int ret; - - rtc_unix_time = rtc_readl(rtc, VAL); - - ret = rtc_tm_to_time(&alrm->time, &alarm_unix_time); - if (ret) - return ret; - - if (alarm_unix_time < rtc_unix_time) - return -EINVAL; - - spin_lock_irq(&rtc->lock); - rtc->alarm_time = alarm_unix_time; - rtc_writel(rtc, TOP, rtc->alarm_time); - if (alrm->enabled) - rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) - | RTC_BIT(CTRL_TOPEN)); - else - rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) - & ~RTC_BIT(CTRL_TOPEN)); - spin_unlock_irq(&rtc->lock); - - return ret; -} - -static int at32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); - int ret = 0; - - spin_lock_irq(&rtc->lock); - - if (enabled) { - if (rtc_readl(rtc, VAL) > rtc->alarm_time) { - ret = -EINVAL; - goto out; - } - rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) - | RTC_BIT(CTRL_TOPEN)); - rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); - rtc_writel(rtc, IER, RTC_BIT(IER_TOPI)); - } else { - rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) - & ~RTC_BIT(CTRL_TOPEN)); - rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); - rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); - } -out: - spin_unlock_irq(&rtc->lock); - - return ret; -} - -static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id) -{ - struct rtc_at32ap700x *rtc = (struct rtc_at32ap700x *)dev_id; - unsigned long isr = rtc_readl(rtc, ISR); - unsigned long events = 0; - int ret = IRQ_NONE; - - spin_lock(&rtc->lock); - - if (isr & RTC_BIT(ISR_TOPI)) { - rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); - rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); - rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) - & ~RTC_BIT(CTRL_TOPEN)); - rtc_writel(rtc, VAL, rtc->alarm_time); - events = RTC_AF | RTC_IRQF; - rtc_update_irq(rtc->rtc, 1, events); - ret = IRQ_HANDLED; - } - - spin_unlock(&rtc->lock); - - return ret; -} - -static const struct rtc_class_ops at32_rtc_ops = { - .read_time = at32_rtc_readtime, - .set_time = at32_rtc_settime, - .read_alarm = at32_rtc_readalarm, - .set_alarm = at32_rtc_setalarm, - .alarm_irq_enable = at32_rtc_alarm_irq_enable, -}; - -static int __init at32_rtc_probe(struct platform_device *pdev) -{ - struct resource *regs; - struct rtc_at32ap700x *rtc; - int irq; - int ret; - - rtc = devm_kzalloc(&pdev->dev, sizeof(struct rtc_at32ap700x), - GFP_KERNEL); - if (!rtc) - return -ENOMEM; - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "no mmio resource defined\n"); - return -ENXIO; - } - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_dbg(&pdev->dev, "could not get irq\n"); - return -ENXIO; - } - - rtc->irq = irq; - rtc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); - if (!rtc->regs) { - dev_dbg(&pdev->dev, "could not map I/O memory\n"); - return -ENOMEM; - } - spin_lock_init(&rtc->lock); - - /* - * Maybe init RTC: count from zero at 1 Hz, disable wrap irq. - * - * Do not reset VAL register, as it can hold an old time - * from last JTAG reset. - */ - if (!(rtc_readl(rtc, CTRL) & RTC_BIT(CTRL_EN))) { - rtc_writel(rtc, CTRL, RTC_BIT(CTRL_PCLR)); - rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); - rtc_writel(rtc, CTRL, RTC_BF(CTRL_PSEL, 0xe) - | RTC_BIT(CTRL_EN)); - } - - ret = devm_request_irq(&pdev->dev, irq, at32_rtc_interrupt, IRQF_SHARED, - "rtc", rtc); - if (ret) { - dev_dbg(&pdev->dev, "could not request irq %d\n", irq); - return ret; - } - - platform_set_drvdata(pdev, rtc); - - rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &at32_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc->rtc)) { - dev_dbg(&pdev->dev, "could not register rtc device\n"); - return PTR_ERR(rtc->rtc); - } - - device_init_wakeup(&pdev->dev, 1); - - dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n", - (unsigned long)rtc->regs, rtc->irq); - - return 0; -} - -static int __exit at32_rtc_remove(struct platform_device *pdev) -{ - device_init_wakeup(&pdev->dev, 0); - - return 0; -} - -MODULE_ALIAS("platform:at32ap700x_rtc"); - -static struct platform_driver at32_rtc_driver = { - .remove = __exit_p(at32_rtc_remove), - .driver = { - .name = "at32ap700x_rtc", - }, -}; - -module_platform_driver_probe(at32_rtc_driver, at32_rtc_probe); - -MODULE_AUTHOR("Hans-Christian Egtvedt "); -MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x"); -MODULE_LICENSE("GPL");