diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-02 14:19:19 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-02 14:19:19 -0800 |
commit | bf644990a796bd72d6f3977f3307169919a23f85 (patch) | |
tree | 812913216ec1ff6c8223169a743cbf0a0504291a /drivers/rtc | |
parent | b89e32ccd1be92a3643df3908d3026b09e271616 (diff) | |
parent | 4402be2b4ca7f53b6213a9e6b3abe9cdcc683e9b (diff) |
Merge tag 'rtc-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni:
"Not much this cycle. I've pushed the at32ap700x removal late but it is
unlikely to cause any issues.
Summary:
Subsystem:
- Move ABI documentation to Documentation/ABI
New driver:
- NXP i.MX53 SRTC
- Chrome OS EC RTC
Drivers:
- Remove at32ap700x
- Many fixes in various error paths"
* tag 'rtc-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux:
rtc: remove rtc-at32ap700x
Documentation: rtc: move iotcl interface documentation to ABI
Documentation: rtc: add sysfs file permissions
Documentation: rtc: move sysfs documentation to ABI
rtc: mxc_v2: remove __exit annotation
rtc: mxc_v2: Remove unnecessary platform_get_resource() error check
rtc: add mxc driver for i.MX53 SRTC
dt-bindings: rtc: add bindings for i.MX53 SRTC
rtc: r7301: Fix a possible sleep-in-atomic bug in rtc7301_set_time
rtc: r7301: Fix a possible sleep-in-atomic bug in rtc7301_read_time
rtc: omap: fix unbalanced clk_prepare_enable/clk_disable_unprepare
rtc: ac100: Fix multiple race conditions
rtc: sun6i: ensure rtc is kfree'd on error
rtc: cros-ec: add cros-ec-rtc driver.
mfd: cros_ec: Introduce RTC commands and events definitions.
rtc: stm32: Fix copyright
rtc: Remove unused RTC_DEVICE_NAME_SIZE
rtc: r9701: Remove r9701_remove function
rtc: brcmstb-waketimer: fix error handling in brcmstb_waketmr_probe()
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 27 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 3 | ||||
-rw-r--r-- | drivers/rtc/rtc-ac100.c | 19 | ||||
-rw-r--r-- | drivers/rtc/rtc-at32ap700x.c | 287 | ||||
-rw-r--r-- | drivers/rtc/rtc-brcmstb-waketimer.c | 15 | ||||
-rw-r--r-- | drivers/rtc/rtc-cros-ec.c | 413 | ||||
-rw-r--r-- | drivers/rtc/rtc-mxc_v2.c | 419 | ||||
-rw-r--r-- | drivers/rtc/rtc-omap.c | 5 | ||||
-rw-r--r-- | drivers/rtc/rtc-r7301.c | 4 | ||||
-rw-r--r-- | drivers/rtc/rtc-r9701.c | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-stm32.c | 4 | ||||
-rw-r--r-- | drivers/rtc/rtc-sun6i.c | 4 |
12 files changed, 889 insertions, 317 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b59a31b079a5..8ab5f0a5d323 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 @@ -1392,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 @@ -1689,6 +1692,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 f2f50c11dc38..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 @@ -44,6 +43,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 @@ -106,6 +106,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-ac100.c b/drivers/rtc/rtc-ac100.c index 0282ccc6181c..8ff9dc3fe5bf 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -569,6 +569,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, @@ -588,17 +594,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; 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 <linux/module.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/rtc.h> -#include <linux/io.h> - -/* - * 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 <egtvedt@samfundet.no>"); -MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x"); -MODULE_LICENSE("GPL"); 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; } 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 <smbarber@chromium.org> + * + * 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 <linux/kernel.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#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 <smbarber@chromium.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c new file mode 100644 index 000000000000..784221dfc9c7 --- /dev/null +++ b/drivers/rtc/rtc-mxc_v2.c @@ -0,0 +1,419 @@ +// 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 <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#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 *t |