diff options
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 | 25 | ||||
-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-opal.c | 12 | ||||
-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 |
13 files changed, 903 insertions, 321 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 9e336184491c..8ff9dc3fe5bf 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -137,13 +137,15 @@ static unsigned long ac100_clkout_recalc_rate(struct clk_hw *hw, div = (reg >> AC100_CLKOUT_PRE_DIV_SHIFT) & ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1); prate = divider_recalc_rate(hw, prate, div, - ac100_clkout_prediv, 0); + ac100_clkout_prediv, 0, + AC100_CLKOUT_PRE_DIV_WIDTH); } div = (reg >> AC100_CLKOUT_DIV_SHIFT) & (BIT(AC100_CLKOUT_DIV_WIDTH) - 1); return divider_recalc_rate(hw, prate, div, NULL, - CLK_DIVIDER_POWER_OF_TWO); + CLK_DIVIDER_POWER_OF_TWO, + AC100_CLKOUT_DIV_WIDTH); } static long ac100_clkout_round_rate(struct clk_hw *hw, unsigned long rate, @@ -567,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, @@ -586,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 *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); + 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 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"); 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); diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c index e2a946c0e667..304e891e35fc 100644 --- a/drivers/rtc/rtc-opal.c +++ b/drivers/rtc/rtc-opal.c @@ -58,6 +58,7 @@ static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms) static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm) { long rc = OPAL_BUSY; + int retries = 10; u32 y_m_d; u64 h_m_s_ms; __be32 __y_m_d; @@ -67,8 +68,11 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm) rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); if (rc == OPAL_BUSY_EVENT) opal_poll_events(NULL); - else + else if (retries-- && (rc == OPAL_HARDWARE + || rc == OPAL_INTERNAL_ERROR)) msleep(10); + else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT) + break; } if (rc != OPAL_SUCCESS) @@ -84,6 +88,7 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm) static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm) { long rc = OPAL_BUSY; + int retries = 10; u32 y_m_d = 0; u64 h_m_s_ms = 0; @@ -92,8 +97,11 @@ static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm) rc = opal_rtc_write(y_m_d, h_m_s_ms); if (rc == OPAL_BUSY_EVENT) opal_poll_events(NULL); - else + else if (retries-- && (rc == OPAL_HARDWARE + || rc == OPAL_INTERNAL_ERROR)) msleep(10); + else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT) + break; } return rc == OPAL_SUCCESS ? 0 : -EIO; diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c index 28d540885f3d..500e8c8a2605 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; @@ -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); 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); 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 <amelie.delaunay@st.com> + * Copyright (C) STMicroelectronics SA 2017 + * Author: Amelie Delaunay <amelie.delaunay@st.com> for STMicroelectronics. * License terms: GNU General Public License (GPL), version 2 */ 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); |