Merge tag 'rtc-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 2 Feb 2018 22:19:19 +0000 (14:19 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 2 Feb 2018 22:19:19 +0000 (14:19 -0800)
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()

18 files changed:
Documentation/ABI/testing/rtc-cdev [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-rtc [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/rtc-mxc_v2.txt [new file with mode: 0644]
Documentation/rtc.txt
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-ac100.c
drivers/rtc/rtc-at32ap700x.c [deleted file]
drivers/rtc/rtc-brcmstb-waketimer.c
drivers/rtc/rtc-cros-ec.c [new file with mode: 0644]
drivers/rtc/rtc-mxc_v2.c [new file with mode: 0644]
drivers/rtc/rtc-omap.c
drivers/rtc/rtc-r7301.c
drivers/rtc/rtc-r9701.c
drivers/rtc/rtc-stm32.c
drivers/rtc/rtc-sun6i.c
include/linux/mfd/cros_ec_commands.h
include/linux/rtc.h

diff --git a/Documentation/ABI/testing/rtc-cdev b/Documentation/ABI/testing/rtc-cdev
new file mode 100644 (file)
index 0000000..9744728
--- /dev/null
@@ -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/ABI/testing/sysfs-class-rtc b/Documentation/ABI/testing/sysfs-class-rtc
new file mode 100644 (file)
index 0000000..cf60412
--- /dev/null
@@ -0,0 +1,91 @@
+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:
+               (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:
+               (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
+Date:          October 2007
+KernelVersion: 2.6.24
+Contact:       linux-rtc@vger.kernel.org
+Description:
+               (RW) 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:
+               (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:
+               (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:
+               (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:
+               (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:
+               (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:
+               (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.
diff --git a/Documentation/devicetree/bindings/rtc/rtc-mxc_v2.txt b/Documentation/devicetree/bindings/rtc/rtc-mxc_v2.txt
new file mode 100644 (file)
index 0000000..79d7e87
--- /dev/null
@@ -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>;
+};
index c0c9774..a129acf 100644 (file)
@@ -136,82 +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.
 
-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
----------------
-
-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.
index b59a31b..8ab5f0a 100644 (file)
@@ -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
index f2f50c1..4fbf87e 100644 (file)
@@ -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
index 0282ccc..8ff9dc3 100644 (file)
@@ -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 (file)
index de8bf56..0000000
+++ /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");
index 796ac79..6cee612 100644 (file)
@@ -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 (file)
index 0000000..f0ea689
--- /dev/null
@@ -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, &current_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, &current_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, &current_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 (file)
index 0000000..784221d
--- /dev/null
@@ -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");
index 1d666ac..09ef802 100644 (file)
@@ -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);
index 28d5408..500e8c8 100644 (file)
@@ -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);
index 83d2bcc..b6c5eb9 100644 (file)
@@ -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);
index 3a5c3d7..f25dabe 100644 (file)
@@ -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
  */
 
index 3d2216c..5bc28ee 100644 (file)
@@ -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);
 
index a83f649..2b96e63 100644 (file)
@@ -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 */
 
index 41319a2..fc6c90b 100644 (file)
@@ -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;