Merge tag 'rtc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Apr 2022 16:37:18 +0000 (09:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Apr 2022 16:37:18 +0000 (09:37 -0700)
Pull RTC updates from Alexandre Belloni:
 "The bulk of the patches are about replacing the uie_unsupported struct
  rtc_device member by a feature bit.

  Subsystem:

   - remove uie_unsupported, all users have been converted to clear
     RTC_FEATURE_UPDATE_INTERRUPT and provide a reason

   - RTCs with an alarm with a resolution of a minute are now letting
     the core handle rounding down the alarm time

   - fix use-after-free on device removal

  New driver:

   - OP-TEE RTC PTA

  Drivers:

   - sun6i: Add H616 support

   - cmos: Fix the AltCentury for AMD platforms

   - spear: set range"

* tag 'rtc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (56 commits)
  rtc: check if __rtc_read_time was successful
  rtc: gamecube: Fix refcount leak in gamecube_rtc_read_offset_from_sram
  rtc: mc146818-lib: Fix the AltCentury for AMD platforms
  rtc: optee: add RTC driver for OP-TEE RTC PTA
  rtc: pm8xxx: Return -ENODEV if set_time disallowed
  rtc: pm8xxx: Attach wake irq to device
  clk: sunxi-ng: sun6i-rtc: include clk/sunxi-ng.h
  rtc: remove uie_unsupported
  rtc: xgene: stop using uie_unsupported
  rtc: hym8563: switch to RTC_FEATURE_UPDATE_INTERRUPT
  rtc: hym8563: let the core handle the alarm resolution
  rtc: hym8563: switch to devm_rtc_allocate_device
  rtc: efi: switch to RTC_FEATURE_UPDATE_INTERRUPT
  rtc: efi: switch to devm_rtc_allocate_device
  rtc: add new RTC_FEATURE_ALARM_WAKEUP_ONLY feature
  rtc: spear: fix spear_rtc_read_time
  rtc: spear: drop uie_unsupported
  rtc: spear: set range
  rtc: spear: switch to devm_rtc_allocate_device
  rtc: pcf8563: switch to RTC_FEATURE_UPDATE_INTERRUPT
  ...

41 files changed:
Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt [deleted file]
Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml [new file with mode: 0644]
MAINTAINERS
drivers/clk/sunxi-ng/Kconfig
drivers/clk/sunxi-ng/Makefile
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c [new file with mode: 0644]
drivers/clk/sunxi-ng/ccu-sun6i-rtc.h [new file with mode: 0644]
drivers/clk/sunxi-ng/ccu_common.h
drivers/clk/sunxi-ng/ccu_mux.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/class.c
drivers/rtc/interface.c
drivers/rtc/rtc-ds1307.c
drivers/rtc/rtc-ds1685.c
drivers/rtc/rtc-efi.c
drivers/rtc/rtc-gamecube.c
drivers/rtc/rtc-hym8563.c
drivers/rtc/rtc-m41t80.c
drivers/rtc/rtc-mc146818-lib.c
drivers/rtc/rtc-mpc5121.c
drivers/rtc/rtc-opal.c
drivers/rtc/rtc-optee.c [new file with mode: 0644]
drivers/rtc/rtc-pcf2123.c
drivers/rtc/rtc-pcf2127.c
drivers/rtc/rtc-pcf85063.c
drivers/rtc/rtc-pcf8523.c
drivers/rtc/rtc-pcf8563.c
drivers/rtc/rtc-pl031.c
drivers/rtc/rtc-pm8xxx.c
drivers/rtc/rtc-spear.c
drivers/rtc/rtc-sun6i.c
drivers/rtc/rtc-wm8350.c
drivers/rtc/rtc-xgene.c
include/dt-bindings/clock/sun6i-rtc.h [new file with mode: 0644]
include/linux/clk/sunxi-ng.h
include/linux/mc146818rtc.h
include/linux/rtc.h
include/linux/rtc/ds1685.h
include/uapi/linux/rtc.h

index beeb90e557278d45f57860783f3c27d020d9d223..0b767fec39d87f48665fde70f1461afb9911faa7 100644 (file)
@@ -16,16 +16,22 @@ properties:
 
   compatible:
     oneOf:
-      - const: allwinner,sun6i-a31-rtc
-      - const: allwinner,sun8i-a23-rtc
-      - const: allwinner,sun8i-h3-rtc
-      - const: allwinner,sun8i-r40-rtc
-      - const: allwinner,sun8i-v3-rtc
-      - const: allwinner,sun50i-h5-rtc
+      - enum:
+          - allwinner,sun6i-a31-rtc
+          - allwinner,sun8i-a23-rtc
+          - allwinner,sun8i-h3-rtc
+          - allwinner,sun8i-r40-rtc
+          - allwinner,sun8i-v3-rtc
+          - allwinner,sun50i-h5-rtc
+          - allwinner,sun50i-h6-rtc
+          - allwinner,sun50i-h616-rtc
+          - allwinner,sun50i-r329-rtc
       - items:
           - const: allwinner,sun50i-a64-rtc
           - const: allwinner,sun8i-h3-rtc
-      - const: allwinner,sun50i-h6-rtc
+      - items:
+          - const: allwinner,sun20i-d1-rtc
+          - const: allwinner,sun50i-r329-rtc
 
   reg:
     maxItems: 1
@@ -37,7 +43,12 @@ properties:
       - description: RTC Alarm 1
 
   clocks:
-    maxItems: 1
+    minItems: 1
+    maxItems: 4
+
+  clock-names:
+    minItems: 1
+    maxItems: 4
 
   clock-output-names:
     minItems: 1
@@ -85,6 +96,7 @@ allOf:
             enum:
               - allwinner,sun8i-h3-rtc
               - allwinner,sun50i-h5-rtc
+              - allwinner,sun50i-h6-rtc
 
     then:
       properties:
@@ -96,19 +108,68 @@ allOf:
       properties:
         compatible:
           contains:
-            const: allwinner,sun50i-h6-rtc
+            const: allwinner,sun50i-h616-rtc
 
     then:
       properties:
-        clock-output-names:
+        clocks:
           minItems: 3
           maxItems: 3
+          items:
+            - description: Bus clock for register access
+            - description: 24 MHz oscillator
+            - description: 32 kHz clock from the CCU
+
+        clock-names:
+          minItems: 3
+          maxItems: 3
+          items:
+            - const: bus
+            - const: hosc
+            - const: pll-32k
+
+      required:
+        - clocks
+        - clock-names
 
   - if:
       properties:
         compatible:
           contains:
-            const: allwinner,sun8i-r40-rtc
+            const: allwinner,sun50i-r329-rtc
+
+    then:
+      properties:
+        clocks:
+          minItems: 3
+          maxItems: 4
+          items:
+            - description: Bus clock for register access
+            - description: 24 MHz oscillator
+            - description: AHB parent for internal SPI clock
+            - description: External 32768 Hz oscillator
+
+        clock-names:
+          minItems: 3
+          maxItems: 4
+          items:
+            - const: bus
+            - const: hosc
+            - const: ahb
+            - const: ext-osc32k
+
+      required:
+        - clocks
+        - clock-names
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - allwinner,sun8i-r40-rtc
+              - allwinner,sun50i-h616-rtc
+              - allwinner,sun50i-r329-rtc
 
     then:
       properties:
@@ -127,7 +188,6 @@ required:
   - compatible
   - reg
   - interrupts
-  - clock-output-names
 
 additionalProperties: false
 
diff --git a/Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt b/Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt
deleted file mode 100644 (file)
index 3f0e2a5..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-Atmel AT91SAM9260 Real Time Timer
-
-Required properties:
-- compatible: should be one of the following:
-       - "atmel,at91sam9260-rtt"
-       - "microchip,sam9x60-rtt", "atmel,at91sam9260-rtt"
-- reg: should encode the memory region of the RTT controller
-- interrupts: rtt alarm/event interrupt
-- clocks: should contain the 32 KHz slow clk that will drive the RTT block.
-- atmel,rtt-rtc-time-reg: should encode the GPBR register used to store
-       the time base when the RTT is used as an RTC.
-       The first cell should point to the GPBR node and the second one
-       encode the offset within the GPBR block (or in other words, the
-       GPBR register used to store the time base).
-
-
-Example:
-
-rtt@fffffd20 {
-       compatible = "atmel,at91sam9260-rtt";
-       reg = <0xfffffd20 0x10>;
-       interrupts = <1 4 7>;
-       clocks = <&clk32k>;
-       atmel,rtt-rtc-time-reg = <&gpbr 0x0>;
-};
diff --git a/Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml b/Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml
new file mode 100644 (file)
index 0000000..0ef1b7f
--- /dev/null
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2022 Microchip Technology, Inc. and its subsidiaries
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/atmel,at91sam9260-rtt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Atmel AT91 RTT Device Tree Bindings
+
+allOf:
+  - $ref: "rtc.yaml#"
+
+maintainers:
+  - Alexandre Belloni <alexandre.belloni@bootlin.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - const: atmel,at91sam9260-rtt
+      - items:
+          - const: microchip,sam9x60-rtt
+          - const: atmel,at91sam9260-rtt
+      - items:
+          - const: microchip,sama7g5-rtt
+          - const: microchip,sam9x60-rtt
+          - const: atmel,at91sam9260-rtt
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  atmel,rtt-rtc-time-reg:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: Phandle to the GPBR node.
+          - description: Offset within the GPBR block.
+    description:
+      Should encode the GPBR register used to store the time base when the
+      RTT is used as an RTC. The first cell should point to the GPBR node
+      and the second one encodes the offset within the GPBR block (or in
+      other words, the GPBR register used to store the time base).
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - atmel,rtt-rtc-time-reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    rtc@fffffd20 {
+        compatible = "atmel,at91sam9260-rtt";
+        reg = <0xfffffd20 0x10>;
+        interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+        clocks = <&clk32k>;
+        atmel,rtt-rtc-time-reg = <&gpbr 0x0>;
+    };
index cbbd3ce7e0c2a191a032d44c0caf5eeebf6bafe4..1f6f4a509d6a01c999226643b86c93f15d8ffa84 100644 (file)
@@ -14622,6 +14622,12 @@ L:     op-tee@lists.trustedfirmware.org
 S:     Maintained
 F:     drivers/char/hw_random/optee-rng.c
 
+OP-TEE RTC DRIVER
+M:     Clément Léger <clement.leger@bootlin.com>
+L:     linux-rtc@vger.kernel.org
+S:     Maintained
+F:     drivers/rtc/rtc-optee.c
+
 OPA-VNIC DRIVER
 M:     Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
 M:     Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>
index 68a94e5af8eda1a8d5d4ce81dc8ebfc4339ee344..461537679c04593b0dd6f6d219cfce59ddba7985 100644 (file)
@@ -69,6 +69,11 @@ config SUN6I_A31_CCU
        default MACH_SUN6I
        depends on MACH_SUN6I || COMPILE_TEST
 
+config SUN6I_RTC_CCU
+       tristate "Support for the Allwinner H616/R329 RTC CCU"
+       default ARCH_SUNXI
+       depends on ARCH_SUNXI || COMPILE_TEST
+
 config SUN8I_A23_CCU
        tristate "Support for the Allwinner A23 CCU"
        default MACH_SUN8I
index ec931cb7aa143976b7339b1cd2f5042cdc7c91a8..6b3ae2b620db69534535872fa20da00ed1fee7b5 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
 obj-$(CONFIG_SUN4I_A10_CCU)    += sun4i-a10-ccu.o
 obj-$(CONFIG_SUN5I_CCU)                += sun5i-ccu.o
 obj-$(CONFIG_SUN6I_A31_CCU)    += sun6i-a31-ccu.o
+obj-$(CONFIG_SUN6I_RTC_CCU)    += sun6i-rtc-ccu.o
 obj-$(CONFIG_SUN8I_A23_CCU)    += sun8i-a23-ccu.o
 obj-$(CONFIG_SUN8I_A33_CCU)    += sun8i-a33-ccu.o
 obj-$(CONFIG_SUN8I_A83T_CCU)   += sun8i-a83t-ccu.o
@@ -60,6 +61,7 @@ sun50i-h616-ccu-y             += ccu-sun50i-h616.o
 sun4i-a10-ccu-y                        += ccu-sun4i-a10.o
 sun5i-ccu-y                    += ccu-sun5i.o
 sun6i-a31-ccu-y                        += ccu-sun6i-a31.o
+sun6i-rtc-ccu-y                        += ccu-sun6i-rtc.o
 sun8i-a23-ccu-y                        += ccu-sun8i-a23.o
 sun8i-a33-ccu-y                        += ccu-sun8i-a33.o
 sun8i-a83t-ccu-y               += ccu-sun8i-a83t.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
new file mode 100644 (file)
index 0000000..8a10bad
--- /dev/null
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
+//
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <linux/clk/sunxi-ng.h>
+
+#include "ccu_common.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mux.h"
+
+#include "ccu-sun6i-rtc.h"
+
+#define IOSC_ACCURACY                  300000000 /* 30% */
+#define IOSC_RATE                      16000000
+
+#define LOSC_RATE                      32768
+#define LOSC_RATE_SHIFT                        15
+
+#define LOSC_CTRL_REG                  0x0
+#define LOSC_CTRL_KEY                  0x16aa0000
+
+#define IOSC_32K_CLK_DIV_REG           0x8
+#define IOSC_32K_CLK_DIV               GENMASK(4, 0)
+#define IOSC_32K_PRE_DIV               32
+
+#define IOSC_CLK_CALI_REG              0xc
+#define IOSC_CLK_CALI_DIV_ONES         22
+#define IOSC_CLK_CALI_EN               BIT(1)
+#define IOSC_CLK_CALI_SRC_SEL          BIT(0)
+
+#define LOSC_OUT_GATING_REG            0x60
+
+#define DCXO_CTRL_REG                  0x160
+#define DCXO_CTRL_CLK16M_RC_EN         BIT(0)
+
+struct sun6i_rtc_match_data {
+       bool                            have_ext_osc32k         : 1;
+       bool                            have_iosc_calibration   : 1;
+       bool                            rtc_32k_single_parent   : 1;
+       const struct clk_parent_data    *osc32k_fanout_parents;
+       u8                              osc32k_fanout_nparents;
+};
+
+static bool have_iosc_calibration;
+
+static int ccu_iosc_enable(struct clk_hw *hw)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+
+       return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
+}
+
+static void ccu_iosc_disable(struct clk_hw *hw)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+
+       return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
+}
+
+static int ccu_iosc_is_enabled(struct clk_hw *hw)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+
+       return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
+}
+
+static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
+                                         unsigned long parent_rate)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+
+       if (have_iosc_calibration) {
+               u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
+
+               /*
+                * Recover the IOSC frequency by shifting the ones place of
+                * (fixed-point divider * 32768) into bit zero.
+                */
+               if (reg & IOSC_CLK_CALI_EN)
+                       return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
+       }
+
+       return IOSC_RATE;
+}
+
+static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
+                                             unsigned long parent_accuracy)
+{
+       return IOSC_ACCURACY;
+}
+
+static const struct clk_ops ccu_iosc_ops = {
+       .enable                 = ccu_iosc_enable,
+       .disable                = ccu_iosc_disable,
+       .is_enabled             = ccu_iosc_is_enabled,
+       .recalc_rate            = ccu_iosc_recalc_rate,
+       .recalc_accuracy        = ccu_iosc_recalc_accuracy,
+};
+
+static struct ccu_common iosc_clk = {
+       .reg            = DCXO_CTRL_REG,
+       .hw.init        = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
+                                               CLK_GET_RATE_NOCACHE),
+};
+
+static int ccu_iosc_32k_prepare(struct clk_hw *hw)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+       u32 val;
+
+       if (!have_iosc_calibration)
+               return 0;
+
+       val = readl(cm->base + IOSC_CLK_CALI_REG);
+       writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
+              cm->base + IOSC_CLK_CALI_REG);
+
+       return 0;
+}
+
+static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+       u32 val;
+
+       if (!have_iosc_calibration)
+               return;
+
+       val = readl(cm->base + IOSC_CLK_CALI_REG);
+       writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
+              cm->base + IOSC_CLK_CALI_REG);
+}
+
+static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
+                                             unsigned long parent_rate)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+       u32 val;
+
+       if (have_iosc_calibration) {
+               val = readl(cm->base + IOSC_CLK_CALI_REG);
+
+               /* Assume the calibrated 32k clock is accurate. */
+               if (val & IOSC_CLK_CALI_SRC_SEL)
+                       return LOSC_RATE;
+       }
+
+       val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
+
+       return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
+}
+
+static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
+                                                 unsigned long parent_accuracy)
+{
+       struct ccu_common *cm = hw_to_ccu_common(hw);
+       u32 val;
+
+       if (have_iosc_calibration) {
+               val = readl(cm->base + IOSC_CLK_CALI_REG);
+
+               /* Assume the calibrated 32k clock is accurate. */
+               if (val & IOSC_CLK_CALI_SRC_SEL)
+                       return 0;
+       }
+
+       return parent_accuracy;
+}
+
+static const struct clk_ops ccu_iosc_32k_ops = {
+       .prepare                = ccu_iosc_32k_prepare,
+       .unprepare              = ccu_iosc_32k_unprepare,
+       .recalc_rate            = ccu_iosc_32k_recalc_rate,
+       .recalc_accuracy        = ccu_iosc_32k_recalc_accuracy,
+};
+
+static struct ccu_common iosc_32k_clk = {
+       .hw.init        = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
+                                        &ccu_iosc_32k_ops,
+                                        CLK_GET_RATE_NOCACHE),
+};
+
+static const struct clk_hw *ext_osc32k[] = { NULL }; /* updated during probe */
+
+static SUNXI_CCU_GATE_HWS(ext_osc32k_gate_clk, "ext-osc32k-gate",
+                         ext_osc32k, 0x0, BIT(4), 0);
+
+static const struct clk_hw *osc32k_parents[] = {
+       &iosc_32k_clk.hw,
+       &ext_osc32k_gate_clk.common.hw
+};
+
+static struct clk_init_data osc32k_init_data = {
+       .name           = "osc32k",
+       .ops            = &ccu_mux_ops,
+       .parent_hws     = osc32k_parents,
+       .num_parents    = ARRAY_SIZE(osc32k_parents), /* updated during probe */
+};
+
+static struct ccu_mux osc32k_clk = {
+       .mux    = _SUNXI_CCU_MUX(0, 1),
+       .common = {
+               .reg            = LOSC_CTRL_REG,
+               .features       = CCU_FEATURE_KEY_FIELD,
+               .hw.init        = &osc32k_init_data,
+       },
+};
+
+/* This falls back to the global name for fwnodes without a named reference. */
+static const struct clk_parent_data osc24M[] = {
+       { .fw_name = "hosc", .name = "osc24M" }
+};
+
+static struct ccu_gate osc24M_32k_clk = {
+       .enable = BIT(16),
+       .common = {
+               .reg            = LOSC_OUT_GATING_REG,
+               .prediv         = 750,
+               .features       = CCU_FEATURE_ALL_PREDIV,
+               .hw.init        = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M,
+                                                          &ccu_gate_ops, 0),
+       },
+};
+
+static const struct clk_hw *rtc_32k_parents[] = {
+       &osc32k_clk.common.hw,
+       &osc24M_32k_clk.common.hw
+};
+
+static struct clk_init_data rtc_32k_init_data = {
+       .name           = "rtc-32k",
+       .ops            = &ccu_mux_ops,
+       .parent_hws     = rtc_32k_parents,
+       .num_parents    = ARRAY_SIZE(rtc_32k_parents), /* updated during probe */
+};
+
+static struct ccu_mux rtc_32k_clk = {
+       .mux    = _SUNXI_CCU_MUX(1, 1),
+       .common = {
+               .reg            = LOSC_CTRL_REG,
+               .features       = CCU_FEATURE_KEY_FIELD,
+               .hw.init        = &rtc_32k_init_data,
+       },
+};
+
+static struct clk_init_data osc32k_fanout_init_data = {
+       .name           = "osc32k-fanout",
+       .ops            = &ccu_mux_ops,
+       /* parents are set during probe */
+};
+
+static struct ccu_mux osc32k_fanout_clk = {
+       .enable = BIT(0),
+       .mux    = _SUNXI_CCU_MUX(1, 2),
+       .common = {
+               .reg            = LOSC_OUT_GATING_REG,
+               .hw.init        = &osc32k_fanout_init_data,
+       },
+};
+
+static struct ccu_common *sun6i_rtc_ccu_clks[] = {
+       &iosc_clk,
+       &iosc_32k_clk,
+       &ext_osc32k_gate_clk.common,
+       &osc32k_clk.common,
+       &osc24M_32k_clk.common,
+       &rtc_32k_clk.common,
+       &osc32k_fanout_clk.common,
+};
+
+static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
+       .num = CLK_NUMBER,
+       .hws = {
+               [CLK_OSC32K]            = &osc32k_clk.common.hw,
+               [CLK_OSC32K_FANOUT]     = &osc32k_fanout_clk.common.hw,
+               [CLK_IOSC]              = &iosc_clk.hw,
+               [CLK_IOSC_32K]          = &iosc_32k_clk.hw,
+               [CLK_EXT_OSC32K_GATE]   = &ext_osc32k_gate_clk.common.hw,
+               [CLK_OSC24M_32K]        = &osc24M_32k_clk.common.hw,
+               [CLK_RTC_32K]           = &rtc_32k_clk.common.hw,
+       },
+};
+
+static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = {
+       .ccu_clks       = sun6i_rtc_ccu_clks,
+       .num_ccu_clks   = ARRAY_SIZE(sun6i_rtc_ccu_clks),
+
+       .hw_clks        = &sun6i_rtc_ccu_hw_clks,
+};
+
+static const struct clk_parent_data sun50i_h6_osc32k_fanout_parents[] = {
+       { .hw = &osc32k_clk.common.hw },
+};
+
+static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = {
+       { .hw = &osc32k_clk.common.hw },
+       { .fw_name = "pll-32k" },
+       { .hw = &osc24M_32k_clk.common.hw }
+};
+
+static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = {
+       { .hw = &osc32k_clk.common.hw },
+       { .hw = &ext_osc32k_gate_clk.common.hw },
+       { .hw = &osc24M_32k_clk.common.hw }
+};
+
+static const struct sun6i_rtc_match_data sun50i_h6_rtc_ccu_data = {
+       .have_ext_osc32k        = true,
+       .have_iosc_calibration  = true,
+       .osc32k_fanout_parents  = sun50i_h6_osc32k_fanout_parents,
+       .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h6_osc32k_fanout_parents),
+};
+
+static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = {
+       .have_iosc_calibration  = true,
+       .rtc_32k_single_parent  = true,
+       .osc32k_fanout_parents  = sun50i_h616_osc32k_fanout_parents,
+       .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h616_osc32k_fanout_parents),
+};
+
+static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = {
+       .have_ext_osc32k        = true,
+       .osc32k_fanout_parents  = sun50i_r329_osc32k_fanout_parents,
+       .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
+};
+
+static const struct of_device_id sun6i_rtc_ccu_match[] = {
+       {
+               .compatible     = "allwinner,sun50i-h6-rtc",
+               .data           = &sun50i_h6_rtc_ccu_data,
+       },
+       {
+               .compatible     = "allwinner,sun50i-h616-rtc",
+               .data           = &sun50i_h616_rtc_ccu_data,
+       },
+       {
+               .compatible     = "allwinner,sun50i-r329-rtc",
+               .data           = &sun50i_r329_rtc_ccu_data,
+       },
+};
+
+int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
+{
+       const struct sun6i_rtc_match_data *data;
+       struct clk *ext_osc32k_clk = NULL;
+       const struct of_device_id *match;
+
+       /* This driver is only used for newer variants of the hardware. */
+       match = of_match_device(sun6i_rtc_ccu_match, dev);
+       if (!match)
+               return 0;
+
+       data = match->data;
+       have_iosc_calibration = data->have_iosc_calibration;
+
+       if (data->have_ext_osc32k) {
+               const char *fw_name;
+
+               /* ext-osc32k was the only input clock in the old binding. */
+               fw_name = of_property_read_bool(dev->of_node, "clock-names")
+                       ? "ext-osc32k" : NULL;
+               ext_osc32k_clk = devm_clk_get_optional(dev, fw_name);
+               if (IS_ERR(ext_osc32k_clk))
+                       return PTR_ERR(ext_osc32k_clk);
+       }
+
+       if (ext_osc32k_clk) {
+               /* Link ext-osc32k-gate to its parent. */
+               *ext_osc32k = __clk_get_hw(ext_osc32k_clk);
+       } else {
+               /* ext-osc32k-gate is an orphan, so do not register it. */
+               sun6i_rtc_ccu_hw_clks.hws[CLK_EXT_OSC32K_GATE] = NULL;
+               osc32k_init_data.num_parents = 1;
+       }
+
+       if (data->rtc_32k_single_parent)
+               rtc_32k_init_data.num_parents = 1;
+
+       osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents;
+       osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents;
+
+       return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
+}
+
+MODULE_IMPORT_NS(SUNXI_CCU);
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
new file mode 100644 (file)
index 0000000..9ae821f
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _CCU_SUN6I_RTC_H
+#define _CCU_SUN6I_RTC_H
+
+#include <dt-bindings/clock/sun6i-rtc.h>
+
+#define CLK_IOSC_32K           3
+#define CLK_EXT_OSC32K_GATE    4
+#define CLK_OSC24M_32K         5
+#define CLK_RTC_32K            6
+
+#define CLK_NUMBER             (CLK_RTC_32K + 1)
+
+#endif /* _CCU_SUN6I_RTC_H */
index 98a1834b58bb4f6b545a6a9877d2d98680cb8dcb..fbf16c6b896d4e3b06d0407e8636a55c79967aab 100644 (file)
@@ -17,6 +17,7 @@
 #define CCU_FEATURE_LOCK_REG           BIT(5)
 #define CCU_FEATURE_MMC_TIMING_SWITCH  BIT(6)
 #define CCU_FEATURE_SIGMA_DELTA_MOD    BIT(7)
+#define CCU_FEATURE_KEY_FIELD          BIT(8)
 
 /* MMC timing mode switch bit */
 #define CCU_MMC_NEW_TIMING_MODE                BIT(30)
index 2306a1cd83e4615ac038a78076dcbb304bcc1783..1d557e32316915787819b65959f1633e48e45d60 100644 (file)
@@ -12,6 +12,8 @@
 #include "ccu_gate.h"
 #include "ccu_mux.h"
 
+#define CCU_MUX_KEY_VALUE              0x16aa0000
+
 static u16 ccu_mux_get_prediv(struct ccu_common *common,
                              struct ccu_mux_internal *cm,
                              int parent_index)
@@ -191,6 +193,11 @@ int ccu_mux_helper_set_parent(struct ccu_common *common,
        spin_lock_irqsave(common->lock, flags);
 
        reg = readl(common->base + common->reg);
+
+       /* The key field always reads as zero. */
+       if (common->features & CCU_FEATURE_KEY_FIELD)
+               reg |= CCU_MUX_KEY_VALUE;
+
        reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift);
        writel(reg | (index << cm->shift), common->base + common->reg);
 
index f6d6d4c26361e87c1cf9cfa0f3eec44b82b06979..41c65b4d2baf63b26b886b1f524697c027d9eabd 100644 (file)
@@ -1293,6 +1293,16 @@ config RTC_DRV_OPAL
          This driver can also be built as a module. If so, the module
          will be called rtc-opal.
 
+config RTC_DRV_OPTEE
+       tristate "OP-TEE based RTC driver"
+       depends on OPTEE
+       help
+         Select this to get support for OP-TEE based RTC control on SoCs where
+         RTC are not accessible to the normal world (Linux).
+
+         This driver can also be built as a module. If so, the module
+         will be called rtc-optee.
+
 config RTC_DRV_ZYNQMP
        tristate "Xilinx Zynq Ultrascale+ MPSoC RTC"
        depends on OF && HAS_IOMEM
index e92f3e94324511895d8053e92800d2168c7fa17b..2d827d8261d55892f99c1ba891208bcdb8f2ce3b 100644 (file)
@@ -115,6 +115,7 @@ obj-$(CONFIG_RTC_DRV_GAMECUBE)      += rtc-gamecube.o
 obj-$(CONFIG_RTC_DRV_NTXEC)    += rtc-ntxec.o
 obj-$(CONFIG_RTC_DRV_OMAP)     += rtc-omap.o
 obj-$(CONFIG_RTC_DRV_OPAL)     += rtc-opal.o
+obj-$(CONFIG_RTC_DRV_OPTEE)    += rtc-optee.o
 obj-$(CONFIG_RTC_DRV_PALMAS)   += rtc-palmas.o
 obj-$(CONFIG_RTC_DRV_PCAP)     += rtc-pcap.o
 obj-$(CONFIG_RTC_DRV_PCF2123)  += rtc-pcf2123.o
index 4b460c61f1d8c478090d4681143f906652759bdb..3c8eec2218dfb478be5b3981a9a183a4708f4f99 100644 (file)
@@ -26,6 +26,15 @@ struct class *rtc_class;
 static void rtc_device_release(struct device *dev)
 {
        struct rtc_device *rtc = to_rtc_device(dev);
+       struct timerqueue_head *head = &rtc->timerqueue;
+       struct timerqueue_node *node;
+
+       mutex_lock(&rtc->ops_lock);
+       while ((node = timerqueue_getnext(head)))
+               timerqueue_del(head, node);
+       mutex_unlock(&rtc->ops_lock);
+
+       cancel_work_sync(&rtc->irqwork);
 
        ida_simple_remove(&rtc_ida, rtc->id);
        mutex_destroy(&rtc->ops_lock);
@@ -390,9 +399,6 @@ int __devm_rtc_register_device(struct module *owner, struct rtc_device *rtc)
        if (!rtc->ops->set_alarm)
                clear_bit(RTC_FEATURE_ALARM, rtc->features);
 
-       if (rtc->uie_unsupported)
-               clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
-
        if (rtc->ops->set_offset)
                set_bit(RTC_FEATURE_CORRECTION, rtc->features);
 
index d8e835798153730ff76531aee9c9ac9e90eaeb4c..9edd662c69ace4da09ef528e6b8216114987be9e 100644 (file)
@@ -804,9 +804,13 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
        struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
        struct rtc_time tm;
        ktime_t now;
+       int err;
+
+       err = __rtc_read_time(rtc, &tm);
+       if (err)
+               return err;
 
        timer->enabled = 1;
-       __rtc_read_time(rtc, &tm);
        now = rtc_tm_to_ktime(tm);
 
        /* Skip over expired timers */
@@ -820,7 +824,6 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
        trace_rtc_timer_enqueue(timer);
        if (!next || ktime_before(timer->node.expires, next->expires)) {
                struct rtc_wkalrm alarm;
-               int err;
 
                alarm.time = rtc_ktime_to_tm(timer->node.expires);
                alarm.enabled = 1;
index 336cb9aa5e336d74889343a7e53bcbeb269683fb..d51565bcc189690a754ba2879cc0b62c058e8bf2 100644 (file)
@@ -1955,7 +1955,7 @@ static int ds1307_probe(struct i2c_client *client,
                dev_info(ds1307->dev,
                         "'wakeup-source' is set, request for an IRQ is disabled!\n");
                /* We cannot support UIE mode if we do not have an IRQ line */
-               ds1307->rtc->uie_unsupported = 1;
+               clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, ds1307->rtc->features);
        }
 
        if (want_irq) {
index 75db7ab654a5ae163855bd8805af975962cc8d91..a24331ba8a5fc9bbce488b3e0f2810a6a6f90c2b 100644 (file)
@@ -1273,7 +1273,7 @@ ds1685_rtc_probe(struct platform_device *pdev)
 
        /* See if the platform doesn't support UIE. */
        if (pdata->uie_unsupported)
-               rtc_dev->uie_unsupported = 1;
+               clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc_dev->features);
 
        rtc->dev = rtc_dev;
 
@@ -1285,13 +1285,10 @@ ds1685_rtc_probe(struct platform_device *pdev)
         * there won't be an automatic way of notifying the kernel about it,
         * unless ctrlc is explicitly polled.
         */
-       if (!pdata->no_irq) {
-               ret = platform_get_irq(pdev, 0);
-               if (ret <= 0)
-                       return ret;
-
-               rtc->irq_num = ret;
-
+       rtc->irq_num = platform_get_irq(pdev, 0);
+       if (rtc->irq_num <= 0) {
+               clear_bit(RTC_FEATURE_ALARM, rtc_dev->features);
+       } else {
                /* Request an IRQ. */
                ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_num,
                                       NULL, ds1685_rtc_irq_handler,
@@ -1305,7 +1302,6 @@ ds1685_rtc_probe(struct platform_device *pdev)
                        rtc->irq_num = 0;
                }
        }
-       rtc->no_irq = pdata->no_irq;
 
        /* Setup complete. */
        ds1685_rtc_switch_to_bank0(rtc);
@@ -1394,7 +1390,7 @@ ds1685_rtc_poweroff(struct platform_device *pdev)
                 * have been taken care of by the shutdown scripts and this
                 * is the final function call.
                 */
-               if (!rtc->no_irq)
+               if (rtc->irq_num)
                        disable_irq_nosync(rtc->irq_num);
 
                /* Oscillator must be on and the countdown chain enabled. */
index 138c5e0046c8dbcfe24a2a7bae295960d6f82941..11850c2880ad487d3e8b3306fe6057e766e40c1e 100644 (file)
@@ -261,15 +261,17 @@ static int __init efi_rtc_probe(struct platform_device *dev)
        if (efi.get_time(&eft, &cap) != EFI_SUCCESS)
                return -ENODEV;
 
-       rtc = devm_rtc_device_register(&dev->dev, "rtc-efi", &efi_rtc_ops,
-                                       THIS_MODULE);
+       rtc = devm_rtc_allocate_device(&dev->dev);
        if (IS_ERR(rtc))
                return PTR_ERR(rtc);
 
-       rtc->uie_unsupported = 1;
        platform_set_drvdata(dev, rtc);
 
-       return 0;
+       rtc->ops = &efi_rtc_ops;
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
+       set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features);
+
+       return devm_rtc_register_device(rtc);
 }
 
 static struct platform_driver efi_rtc_driver = {
index f717b36f4738cf2513cf127c54653166a26d8056..18ca3b38b2d0474f58f4a56b3ae94d5afb3f86d9 100644 (file)
@@ -235,6 +235,7 @@ static int gamecube_rtc_read_offset_from_sram(struct priv *d)
        }
 
        ret = of_address_to_resource(np, 0, &res);
+       of_node_put(np);
        if (ret) {
                pr_err("no io memory range found\n");
                return -1;
index 0751cae2728595ad899031dcf7280cc65ec0f3d9..90e602e99d03a88448a71623ce41619959afed28 100644 (file)
@@ -220,24 +220,6 @@ static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
        u8 buf[4];
        int ret;
 
-       /*
-        * The alarm has no seconds so deal with it
-        */
-       if (alm_tm->tm_sec) {
-               alm_tm->tm_sec = 0;
-               alm_tm->tm_min++;
-               if (alm_tm->tm_min >= 60) {
-                       alm_tm->tm_min = 0;
-                       alm_tm->tm_hour++;
-                       if (alm_tm->tm_hour >= 24) {
-                               alm_tm->tm_hour = 0;
-                               alm_tm->tm_mday++;
-                               if (alm_tm->tm_mday > 31)
-                                       alm_tm->tm_mday = 0;
-                       }
-               }
-       }
-
        ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
        if (ret < 0)
                return ret;
@@ -523,6 +505,10 @@ static int hym8563_probe(struct i2c_client *client,
        if (!hym8563)
                return -ENOMEM;
 
+       hym8563->rtc = devm_rtc_allocate_device(&client->dev);
+       if (IS_ERR(hym8563->rtc))
+               return PTR_ERR(hym8563->rtc);
+
        hym8563->client = client;
        i2c_set_clientdata(client, hym8563);
 
@@ -557,19 +543,15 @@ static int hym8563_probe(struct i2c_client *client,
        dev_dbg(&client->dev, "rtc information is %s\n",
                (ret & HYM8563_SEC_VL) ? "invalid" : "valid");
 
-       hym8563->rtc = devm_rtc_device_register(&client->dev, client->name,
-                                               &hym8563_rtc_ops, THIS_MODULE);
-       if (IS_ERR(hym8563->rtc))
-               return PTR_ERR(hym8563->rtc);
-
-       /* the hym8563 alarm only supports a minute accuracy */
-       hym8563->rtc->uie_unsupported = 1;
+       hym8563->rtc->ops = &hym8563_rtc_ops;
+       set_bit(RTC_FEATURE_ALARM_RES_MINUTE, hym8563->rtc->features);
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, hym8563->rtc->features);
 
 #ifdef CONFIG_COMMON_CLK
        hym8563_clkout_register_clk(hym8563);
 #endif
 
-       return 0;
+       return devm_rtc_register_device(hym8563->rtc);
 }
 
 static const struct i2c_device_id hym8563_id[] = {
index 6d383b629d20a2e65a977468ca6ca36121ef4ece..d868458cd40ed05c200cfe456267df385a5886a5 100644 (file)
@@ -932,10 +932,8 @@ static int m41t80_probe(struct i2c_client *client,
        m41t80_data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
        m41t80_data->rtc->range_max = RTC_TIMESTAMP_END_2099;
 
-       if (client->irq <= 0) {
-               /* We cannot support UIE mode if we do not have an IRQ line */
-               m41t80_data->rtc->uie_unsupported = 1;
-       }
+       if (client->irq <= 0)
+               clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, m41t80_data->rtc->features);
 
        /* Make sure HT (Halt Update) bit is cleared */
        rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);
index ae9f131b43c0c69586061bef1de46f56bbbf7f56..522449b25921efd0ac23f6d3c6bac0173654d3ba 100644 (file)
@@ -176,6 +176,17 @@ int mc146818_get_time(struct rtc_time *time)
 }
 EXPORT_SYMBOL_GPL(mc146818_get_time);
 
+/* AMD systems don't allow access to AltCentury with DV1 */
+static bool apply_amd_register_a_behavior(void)
+{
+#ifdef CONFIG_X86
+       if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
+           boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
+               return true;
+#endif
+       return false;
+}
+
 /* Set the current date and time in the real time clock. */
 int mc146818_set_time(struct rtc_time *time)
 {
@@ -232,8 +243,10 @@ int mc146818_set_time(struct rtc_time *time)
        if (yrs >= 100)
                yrs -= 100;
 
-       if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
-           || RTC_ALWAYS_BCD) {
+       spin_lock_irqsave(&rtc_lock, flags);
+       save_control = CMOS_READ(RTC_CONTROL);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
                sec = bin2bcd(sec);
                min = bin2bcd(min);
                hrs = bin2bcd(hrs);
@@ -247,7 +260,10 @@ int mc146818_set_time(struct rtc_time *time)
        save_control = CMOS_READ(RTC_CONTROL);
        CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
        save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
-       CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+       if (apply_amd_register_a_behavior())
+               CMOS_WRITE((save_freq_select & ~RTC_AMD_BANK_SELECT), RTC_FREQ_SELECT);
+       else
+               CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 
 #ifdef CONFIG_MACH_DECSTATION
        CMOS_WRITE(real_yrs, RTC_DEC_YEAR);
index bb2ea9bc56f2c51cebdb238b3b5f327fc459169c..6d7656a75cae50cb8cf90071492201a5acc8bb2a 100644 (file)
@@ -210,20 +210,6 @@ static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 
-       /*
-        * the alarm has no seconds so deal with it
-        */
-       if (alarm->time.tm_sec) {
-               alarm->time.tm_sec = 0;
-               alarm->time.tm_min++;
-               if (alarm->time.tm_min >= 60) {
-                       alarm->time.tm_min = 0;
-                       alarm->time.tm_hour++;
-                       if (alarm->time.tm_hour >= 24)
-                               alarm->time.tm_hour = 0;
-               }
-       }
-
        alarm->time.tm_mday = -1;
        alarm->time.tm_mon = -1;
        alarm->time.tm_year = -1;
@@ -349,7 +335,8 @@ static int mpc5121_rtc_probe(struct platform_device *op)
        }
 
        rtc->rtc->ops = &mpc5200_rtc_ops;
-       rtc->rtc->uie_unsupported = 1;
+       set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtc->features);
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc->features);
        rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
        rtc->rtc->range_max = 65733206399ULL; /* 4052-12-31 23:59:59 */
 
index f8f49a969c231a333487a77ee395c81b9d1ea958..ad41aaf8a17f5397e8b535cd9e3c7c5e14683842 100644 (file)
@@ -250,7 +250,7 @@ static int opal_rtc_probe(struct platform_device *pdev)
        rtc->ops = &opal_rtc_ops;
        rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
        rtc->range_max = RTC_TIMESTAMP_END_9999;
-       rtc->uie_unsupported = 1;
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
 
        return devm_rtc_register_device(rtc);
 }
diff --git a/drivers/rtc/rtc-optee.c b/drivers/rtc/rtc-optee.c
new file mode 100644 (file)
index 0000000..9f8b5d4
--- /dev/null
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Microchip.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/tee_drv.h>
+
+#define RTC_INFO_VERSION       0x1
+
+#define TA_CMD_RTC_GET_INFO            0x0
+#define TA_CMD_RTC_GET_TIME            0x1
+#define TA_CMD_RTC_SET_TIME            0x2
+#define TA_CMD_RTC_GET_OFFSET          0x3
+#define TA_CMD_RTC_SET_OFFSET          0x4
+
+#define TA_RTC_FEATURE_CORRECTION      BIT(0)
+
+struct optee_rtc_time {
+       u32 tm_sec;
+       u32 tm_min;
+       u32 tm_hour;
+       u32 tm_mday;
+       u32 tm_mon;
+       u32 tm_year;
+       u32 tm_wday;
+};
+
+struct optee_rtc_info {
+       u64 version;
+       u64 features;
+       struct optee_rtc_time range_min;
+       struct optee_rtc_time range_max;
+};
+
+/**
+ * struct optee_rtc - OP-TEE RTC private data
+ * @dev:               OP-TEE based RTC device.
+ * @ctx:               OP-TEE context handler.
+ * @session_id:                RTC TA session identifier.
+ * @shm:               Memory pool shared with RTC device.
+ * @features:          Bitfield of RTC features
+ */
+struct optee_rtc {
+       struct device *dev;
+       struct tee_context *ctx;
+       u32 session_id;
+       struct tee_shm *shm;
+       u64 features;
+};
+
+static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+       struct optee_rtc *priv = dev_get_drvdata(dev);
+       struct tee_ioctl_invoke_arg inv_arg = {0};
+       struct optee_rtc_time *optee_tm;
+       struct tee_param param[4] = {0};
+       int ret;
+
+       inv_arg.func = TA_CMD_RTC_GET_TIME;
+       inv_arg.session = priv->session_id;
+       inv_arg.num_params = 4;
+
+       /* Fill invoke cmd params */
+       param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+       param[0].u.memref.shm = priv->shm;
+       param[0].u.memref.size = sizeof(struct optee_rtc_time);
+
+       ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
+       if (ret < 0 || inv_arg.ret != 0)
+               return ret ? ret : -EPROTO;
+
+       optee_tm = tee_shm_get_va(priv->shm, 0);
+       if (IS_ERR(optee_tm))
+               return PTR_ERR(optee_tm);
+
+       if (param[0].u.memref.size != sizeof(*optee_tm))
+               return -EPROTO;
+
+       tm->tm_sec = optee_tm->tm_sec;
+       tm->tm_min = optee_tm->tm_min;
+       tm->tm_hour = optee_tm->tm_hour;
+       tm->tm_mday = optee_tm->tm_mday;
+       tm->tm_mon = optee_tm->tm_mon;
+       tm->tm_year = optee_tm->tm_year - 1900;
+       tm->tm_wday = optee_tm->tm_wday;
+       tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+       return 0;
+}
+
+static int optee_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+       struct optee_rtc *priv = dev_get_drvdata(dev);
+       struct tee_ioctl_invoke_arg inv_arg = {0};
+       struct tee_param param[4] = {0};
+       struct optee_rtc_time optee_tm;
+       void *rtc_data;
+       int ret;
+
+       optee_tm.tm_sec = tm->tm_sec;
+       optee_tm.tm_min = tm->tm_min;
+       optee_tm.tm_hour = tm->tm_hour;
+       optee_tm.tm_mday = tm->tm_mday;
+       optee_tm.tm_mon = tm->tm_mon;
+       optee_tm.tm_year = tm->tm_year + 1900;
+       optee_tm.tm_wday = tm->tm_wday;
+
+       inv_arg.func = TA_CMD_RTC_SET_TIME;
+       inv_arg.session = priv->session_id;
+       inv_arg.num_params = 4;
+
+       param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+       param[0].u.memref.shm = priv->shm;
+       param[0].u.memref.size = sizeof(struct optee_rtc_time);
+
+       rtc_data = tee_shm_get_va(priv->shm, 0);
+       if (IS_ERR(rtc_data))
+               return PTR_ERR(rtc_data);
+
+       memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time));
+
+       ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
+       if (ret < 0 || inv_arg.ret != 0)
+               return ret ? ret : -EPROTO;
+
+       return 0;
+}
+
+static int optee_rtc_readoffset(struct device *dev, long *offset)
+{
+       struct optee_rtc *priv = dev_get_drvdata(dev);
+       struct tee_ioctl_invoke_arg inv_arg = {0};
+       struct tee_param param[4] = {0};
+       int ret;
+
+       if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
+               return -EOPNOTSUPP;
+
+       inv_arg.func = TA_CMD_RTC_GET_OFFSET;
+       inv_arg.session = priv->session_id;
+       inv_arg.num_params = 4;
+
+       param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+
+       ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
+       if (ret < 0 || inv_arg.ret != 0)
+               return ret ? ret : -EPROTO;
+
+       *offset = param[0].u.value.a;
+
+       return 0;
+}
+
+static int optee_rtc_setoffset(struct device *dev, long offset)
+{
+       struct optee_rtc *priv = dev_get_drvdata(dev);
+       struct tee_ioctl_invoke_arg inv_arg = {0};
+       struct tee_param param[4] = {0};
+       int ret;
+
+       if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
+               return -EOPNOTSUPP;
+
+       inv_arg.func = TA_CMD_RTC_SET_OFFSET;
+       inv_arg.session = priv->session_id;
+       inv_arg.num_params = 4;
+
+       param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+       param[0].u.value.a = offset;
+
+       ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
+       if (ret < 0 || inv_arg.ret != 0)
+               return ret ? ret : -EPROTO;
+
+       return 0;
+}
+
+static const struct rtc_class_ops optee_rtc_ops = {
+       .read_time      = optee_rtc_readtime,
+       .set_time       = optee_rtc_settime,
+       .set_offset     = optee_rtc_setoffset,
+       .read_offset    = optee_rtc_readoffset,
+};
+
+static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,
+                              u64 *features)
+{
+       struct optee_rtc *priv = dev_get_drvdata(dev);
+       struct tee_ioctl_invoke_arg inv_arg = {0};
+       struct tee_param param[4] = {0};
+       struct optee_rtc_info *info;
+       struct optee_rtc_time *tm;
+       int ret;
+
+       inv_arg.func = TA_CMD_RTC_GET_INFO;
+       inv_arg.session = priv->session_id;
+       inv_arg.num_params = 4;
+
+       param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+       param[0].u.memref.shm = priv->shm;
+       param[0].u.memref.size = sizeof(*info);
+
+       ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
+       if (ret < 0 || inv_arg.ret != 0)
+               return ret ? ret : -EPROTO;
+
+       info = tee_shm_get_va(priv->shm, 0);
+       if (IS_ERR(info))
+               return PTR_ERR(info);
+
+       if (param[0].u.memref.size != sizeof(*info))
+               return -EPROTO;
+
+       if (info->version != RTC_INFO_VERSION)
+               return -EPROTO;
+
+       *features = info->features;
+
+       tm = &info->range_min;
+       rtc->range_min = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
+                                 tm->tm_sec);
+       tm = &info->range_max;
+       rtc->range_max = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
+                                 tm->tm_sec);
+
+       return 0;
+}
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+       if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+               return 1;
+       else
+               return 0;
+}
+
+static int optee_rtc_probe(struct device *dev)
+{
+       struct tee_client_device *rtc_device = to_tee_client_device(dev);
+       struct tee_ioctl_open_session_arg sess_arg;
+       struct optee_rtc *priv;
+       struct rtc_device *rtc;
+       struct tee_shm *shm;
+       int ret, err;
+
+       memset(&sess_arg, 0, sizeof(sess_arg));
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       rtc = devm_rtc_allocate_device(dev);
+       if (IS_ERR(rtc))
+               return PTR_ERR(rtc);
+
+       /* Open context with TEE driver */
+       priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
+       if (IS_ERR(priv->ctx))
+               return -ENODEV;
+
+       /* Open session with rtc Trusted App */
+       export_uuid(sess_arg.uuid, &rtc_device->id.uuid);
+       sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
+
+       ret = tee_client_open_session(priv->ctx, &sess_arg, NULL);
+       if (ret < 0 || sess_arg.ret != 0) {
+               dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
+               err = -EINVAL;
+               goto out_ctx;
+       }
+       priv->session_id = sess_arg.session;
+
+       shm = tee_shm_alloc_kernel_buf(priv->ctx, sizeof(struct optee_rtc_info));
+       if (IS_ERR(shm)) {
+               dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n");
+               err = PTR_ERR(shm);
+               goto out_sess;
+       }
+
+       priv->shm = shm;
+       priv->dev = dev;
+       dev_set_drvdata(dev, priv);
+
+       rtc->ops = &optee_rtc_ops;
+
+       err = optee_rtc_read_info(dev, rtc, &priv->features);
+       if (err) {
+               dev_err(dev, "Failed to get RTC features from OP-TEE\n");
+               goto out_shm;
+       }
+
+       err = devm_rtc_register_device(rtc);
+       if (err)
+               goto out_shm;
+
+       /*
+        * We must clear this bit after registering because rtc_register_device
+        * will set it if it sees that .set_offset is provided.
+        */
+       if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
+               clear_bit(RTC_FEATURE_CORRECTION, rtc->features);
+
+       return 0;
+
+out_shm:
+       tee_shm_free(priv->shm);
+out_sess:
+       tee_client_close_session(priv->ctx, priv->session_id);
+out_ctx:
+       tee_client_close_context(priv->ctx);
+
+       return err;
+}
+
+static int optee_rtc_remove(struct device *dev)
+{
+       struct optee_rtc *priv = dev_get_drvdata(dev);
+
+       tee_client_close_session(priv->ctx, priv->session_id);
+       tee_client_close_context(priv->ctx);
+
+       return 0;
+}
+
+static const struct tee_client_device_id optee_rtc_id_table[] = {
+       {UUID_INIT(0xf389f8c8, 0x845f, 0x496c,
+                  0x8b, 0xbe, 0xd6, 0x4b, 0xd2, 0x4c, 0x92, 0xfd)},
+       {}
+};
+
+MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);
+
+static struct tee_client_driver optee_rtc_driver = {
+       .id_table       = optee_rtc_id_table,
+       .driver         = {
+               .name           = "optee_rtc",
+               .bus            = &tee_bus_type,
+               .probe          = optee_rtc_probe,
+               .remove         = optee_rtc_remove,
+       },
+};
+
+static int __init optee_rtc_mod_init(void)
+{
+       return driver_register(&optee_rtc_driver.driver);
+}
+
+static void __exit optee_rtc_mod_exit(void)
+{
+       driver_unregister(&optee_rtc_driver.driver);
+}
+
+module_init(optee_rtc_mod_init);
+module_exit(optee_rtc_mod_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
+MODULE_DESCRIPTION("OP-TEE based RTC driver");
index 7473e6c8a183bf84a20848b31e3e846b7ac3bc20..e13b5e695d06a725ef6edb18972dc95ac8c6d84e 100644 (file)
@@ -427,7 +427,8 @@ static int pcf2123_probe(struct spi_device *spi)
         * support to this driver to generate interrupts more than once
         * per minute.
         */
-       rtc->uie_unsupported = 1;
+       set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features);
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
        rtc->ops = &pcf2123_rtc_ops;
        rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
        rtc->range_max = RTC_TIMESTAMP_END_2099;
index 81a5b1f2e68c3bd121e715a4307ab3c9951426e9..63b275b014bd613a95b42dea4940f4d3c3035dfb 100644 (file)
@@ -374,7 +374,8 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
 static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
        struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
-       unsigned int buf[5], ctrl2;
+       u8 buf[5];
+       unsigned int ctrl2;
        int ret;
 
        ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
@@ -655,13 +656,25 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
        pcf2127->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
        pcf2127->rtc->range_max = RTC_TIMESTAMP_END_2099;
        pcf2127->rtc->set_start_time = true; /* Sets actual start to 1970 */
-       pcf2127->rtc->uie_unsupported = 1;
+       set_bit(RTC_FEATURE_ALARM_RES_2S, pcf2127->rtc->features);
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features);
        clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
 
        if (alarm_irq > 0) {
+               unsigned long flags;
+
+               /*
+                * If flags = 0, devm_request_threaded_irq() will use IRQ flags
+                * obtained from device tree.
+                */
+               if (dev_fwnode(dev))
+                       flags = 0;
+               else
+                       flags = IRQF_TRIGGER_LOW;
+
                ret = devm_request_threaded_irq(dev, alarm_irq, NULL,
                                                pcf2127_rtc_irq,
-                                               IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                               flags | IRQF_ONESHOT,
                                                dev_name(dev), dev);
                if (ret) {
                        dev_err(dev, "failed to request alarm irq\n");
index df2b072c394d8ccea8a137669708ebb45b27a5f9..9760824ec199e8dda5a583e0dddd45eb06d4a6ca 100644 (file)
@@ -616,7 +616,8 @@ static int pcf85063_probe(struct i2c_client *client)
        pcf85063->rtc->ops = &pcf85063_rtc_ops;
        pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
        pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
-       pcf85063->rtc->uie_unsupported = 1;
+       set_bit(RTC_FEATURE_ALARM_RES_2S, pcf85063->rtc->features);
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features);
        clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
 
        if (config->has_alarms && client->irq > 0) {
index c93acade720575449a6f1e43cafeaf2e9f1e2f40..b1b1943de84478f15198537fabd4d7335ea47964 100644 (file)
@@ -212,14 +212,6 @@ static int pcf8523_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
        if (err < 0)
                return err;
 
-       /* The alarm has no seconds, round up to nearest minute */
-       if (tm->time.tm_sec) {
-               time64_t alarm_time = rtc_tm_to_time64(&tm->time);
-
-               alarm_time += 60 - tm->time.tm_sec;
-               rtc_time64_to_tm(alarm_time, &tm->time);
-       }
-
        regs[0] = bin2bcd(tm->time.tm_min);
        regs[1] = bin2bcd(tm->time.tm_hour);
        regs[2] = bin2bcd(tm->time.tm_mday);
@@ -240,9 +232,9 @@ static int pcf8523_param_get(struct device *dev, struct rtc_param *param)
 {
        struct pcf8523 *pcf8523 = dev_get_drvdata(dev);
        int ret;
+       u32 value;
 
        switch(param->param) {
-               u32 value;
 
        case RTC_PARAM_BACKUP_SWITCH_MODE:
                ret = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value);
@@ -279,9 +271,9 @@ static int pcf8523_param_get(struct device *dev, struct rtc_param *param)
 static int pcf8523_param_set(struct device *dev, struct rtc_param *param)
 {
        struct pcf8523 *pcf8523 = dev_get_drvdata(dev);
+       u8 mode;
 
        switch(param->param) {
-               u8 mode;
        case RTC_PARAM_BACKUP_SWITCH_MODE:
                switch (param->uvalue) {
                case RTC_BSM_DISABLED:
@@ -450,7 +442,8 @@ static int pcf8523_probe(struct i2c_client *client,
        rtc->ops = &pcf8523_rtc_ops;
        rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
        rtc->range_max = RTC_TIMESTAMP_END_2099;
-       rtc->uie_unsupported = 1;
+       set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features);
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
 
        if (client->irq > 0) {
                err = regmap_write(pcf8523->regmap, PCF8523_TMR_CLKOUT_CTRL, 0x38);
index c8bddfb941290cfc89e1f8f3cdd8d10529f41446..9d06813e2e6d4b4f1863c7b1ca0f538fedb72761 100644 (file)
@@ -330,19 +330,6 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
        unsigned char buf[4];
        int err;
 
-       /* The alarm has no seconds, round up to nearest minute */
-       if (tm->time.tm_sec) {
-               time64_t alarm_time = rtc_tm_to_time64(&tm->time);
-
-               alarm_time += 60 - tm->time.tm_sec;
-               rtc_time64_to_tm(alarm_time, &tm->time);
-       }
-
-       dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d "
-               "enabled=%d pending=%d\n", __func__,
-               tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday,
-               tm->time.tm_mday, tm->enabled, tm->pending);
-
        buf[0] = bin2bcd(tm->time.tm_min);
        buf[1] = bin2bcd(tm->time.tm_hour);
        buf[2] = bin2bcd(tm->time.tm_mday);
@@ -565,7 +552,8 @@ static int pcf8563_probe(struct i2c_client *client,
 
        pcf8563->rtc->ops = &pcf8563_rtc_ops;
        /* the pcf8563 alarm only supports a minute accuracy */
-       pcf8563->rtc->uie_unsupported = 1;
+       set_bit(RTC_FEATURE_ALARM_RES_MINUTE, pcf8563->rtc->features);
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf8563->rtc->features);
        pcf8563->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
        pcf8563->rtc->range_max = RTC_TIMESTAMP_END_2099;
        pcf8563->rtc->set_start_time = true;
index e38ee8848385505743d900cb1eed6bbfc5ff54c5..bad6a5d9c6839ca70905e3d46286b9729c1fd435 100644 (file)
@@ -350,9 +350,6 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
                }
        }
 
-       if (!adev->irq[0])
-               clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features);
-
        device_init_wakeup(&adev->dev, true);
        ldata->rtc = devm_rtc_allocate_device(&adev->dev);
        if (IS_ERR(ldata->rtc)) {
@@ -360,6 +357,9 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
                goto out;
        }
 
+       if (!adev->irq[0])
+               clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features);
+
        ldata->rtc->ops = ops;
        ldata->rtc->range_min = vendor->range_min;
        ldata->rtc->range_max = vendor->range_max;
index 29a1c65661e99d21a4c11754bb94aa379baba181..dc6d1476baa597374b396a81cba382d027dbea20 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/rtc.h>
 #include <linux/platform_device.h>
 #include <linux/pm.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
@@ -83,7 +84,7 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
        const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
 
        if (!rtc_dd->allow_set_time)
-               return -EACCES;
+               return -ENODEV;
 
        secs = rtc_tm_to_time64(tm);
 
@@ -527,40 +528,28 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
                return rc;
        }
 
-       return devm_rtc_register_device(rtc_dd->rtc);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int pm8xxx_rtc_resume(struct device *dev)
-{
-       struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+       rc = devm_rtc_register_device(rtc_dd->rtc);
+       if (rc)
+               return rc;
 
-       if (device_may_wakeup(dev))
-               disable_irq_wake(rtc_dd->rtc_alarm_irq);
+       rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->rtc_alarm_irq);
+       if (rc)
+               return rc;
 
        return 0;
 }
 
-static int pm8xxx_rtc_suspend(struct device *dev)
+static int pm8xxx_remove(struct platform_device *pdev)
 {
-       struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
-
-       if (device_may_wakeup(dev))
-               enable_irq_wake(rtc_dd->rtc_alarm_irq);
-
+       dev_pm_clear_wake_irq(&pdev->dev);
        return 0;
 }
-#endif
-
-static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops,
-                        pm8xxx_rtc_suspend,
-                        pm8xxx_rtc_resume);
 
 static struct platform_driver pm8xxx_rtc_driver = {
        .probe          = pm8xxx_rtc_probe,
+       .remove         = pm8xxx_remove,
        .driver = {
                .name           = "rtc-pm8xxx",
-               .pm             = &pm8xxx_rtc_pm_ops,
                .of_match_table = pm8xxx_id_table,
        },
 };
index b4a520056b1ab482caead3d02b07fe05fd6dab44..d4777b01ab220a5eb940684567cfdb8027d97e34 100644 (file)
@@ -204,8 +204,10 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
        /* we don't report wday/yday/isdst ... */
        rtc_wait_not_busy(config);
 
-       time = readl(config->ioaddr + TIME_REG);
-       date = readl(config->ioaddr + DATE_REG);
+       do {
+               time = readl(config->ioaddr + TIME_REG);
+               date = readl(config->ioaddr + DATE_REG);
+       } while (time == readl(config->ioaddr + TIME_REG));
        tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
        tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
        tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
@@ -352,6 +354,10 @@ static int spear_rtc_probe(struct platform_device *pdev)
        if (!config)
                return -ENOMEM;
 
+       config->rtc = devm_rtc_allocate_device(&pdev->dev);
+       if (IS_ERR(config->rtc))
+               return PTR_ERR(config->rtc);
+
        /* alarm irqs */
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
@@ -380,16 +386,13 @@ static int spear_rtc_probe(struct platform_device *pdev)
        spin_lock_init(&config->lock);
        platform_set_drvdata(pdev, config);
 
-       config->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
-                                       &spear_rtc_ops, THIS_MODULE);
-       if (IS_ERR(config->rtc)) {
-               dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
-                               PTR_ERR(config->rtc));
-               status = PTR_ERR(config->rtc);
-               goto err_disable_clock;
-       }
+       config->rtc->ops = &spear_rtc_ops;
+       config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
+       config->rtc->range_min = RTC_TIMESTAMP_END_9999;
 
-       config->rtc->uie_unsupported = 1;
+       status = devm_rtc_register_device(config->rtc);
+       if (status)
+               goto err_disable_clock;
 
        if (!device_can_wakeup(&pdev->dev))
                device_init_wakeup(&pdev->dev, 1);
index 711832c758aeaef535c42a9f8d0eb0811cddc75d..5b3e4da6340612f78eec1e691966b60cde6661ec 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/clk/sunxi-ng.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/fs.h>
@@ -48,7 +49,8 @@
 
 /* Alarm 0 (counter) */
 #define SUN6I_ALRM_COUNTER                     0x0020
-#define SUN6I_ALRM_CUR_VAL                     0x0024
+/* This holds the remaining alarm seconds on older SoCs (current value) */
+#define SUN6I_ALRM_COUNTER_HMS                 0x0024
 #define SUN6I_ALRM_EN                          0x0028
 #define SUN6I_ALRM_EN_CNT_EN                   BIT(0)
 #define SUN6I_ALRM_IRQ_EN                      0x002c
 #define SUN6I_YEAR_MIN                         1970
 #define SUN6I_YEAR_OFF                         (SUN6I_YEAR_MIN - 1900)
 
+#define SECS_PER_DAY                           (24 * 3600ULL)
+
 /*
  * There are other differences between models, including:
  *
@@ -133,12 +137,15 @@ struct sun6i_rtc_clk_data {
        unsigned int has_auto_swt : 1;
 };
 
+#define RTC_LINEAR_DAY BIT(0)
+
 struct sun6i_rtc_dev {
        struct rtc_device *rtc;
        const struct sun6i_rtc_clk_data *data;
        void __iomem *base;
        int irq;
-       unsigned long alarm;
+       time64_t alarm;
+       unsigned long flags;
 
        struct clk_hw hw;
        struct clk_hw *int_osc;
@@ -363,23 +370,6 @@ CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc",
 CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc",
                      sun8i_h3_rtc_clk_init);
 
-static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = {
-       .rc_osc_rate = 16000000,
-       .fixed_prescaler = 32,
-       .has_prescaler = 1,
-       .has_out_clk = 1,
-       .export_iosc = 1,
-       .has_losc_en = 1,
-       .has_auto_swt = 1,
-};
-
-static void __init sun50i_h6_rtc_clk_init(struct device_node *node)
-{
-       sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data);
-}
-CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc",
-                     sun50i_h6_rtc_clk_init);
-
 /*
  * The R40 user manual is self-conflicting on whether the prescaler is
  * fixed or configurable. The clock diagram shows it as fixed, but there
@@ -467,22 +457,30 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
        } while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
                 (time != readl(chip->base + SUN6I_RTC_HMS)));
 
+       if (chip->flags & RTC_LINEAR_DAY) {
+               /*
+                * Newer chips store a linear day number, the manual
+                * does not mandate any epoch base. The BSP driver uses
+                * the UNIX epoch, let's just copy that, as it's the
+                * easiest anyway.
+                */
+               rtc_time64_to_tm((date & 0xffff) * SECS_PER_DAY, rtc_tm);
+       } else {
+               rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
+               rtc_tm->tm_mon  = SUN6I_DATE_GET_MON_VALUE(date) - 1;
+               rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
+
+               /*
+                * switch from (data_year->min)-relative offset to
+                * a (1900)-relative one
+                */
+               rtc_tm->tm_year += SUN6I_YEAR_OFF;
+       }
+
        rtc_tm->tm_sec  = SUN6I_TIME_GET_SEC_VALUE(time);
        rtc_tm->tm_min  = SUN6I_TIME_GET_MIN_VALUE(time);
        rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
 
-       rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
-       rtc_tm->tm_mon  = SUN6I_DATE_GET_MON_VALUE(date);
-       rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
-
-       rtc_tm->tm_mon  -= 1;
-
-       /*
-        * switch from (data_year->min)-relative offset to
-        * a (1900)-relative one
-        */
-       rtc_tm->tm_year += SUN6I_YEAR_OFF;
-
        return 0;
 }
 
@@ -510,36 +508,54 @@ static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
        struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
        struct rtc_time *alrm_tm = &wkalrm->time;
        struct rtc_time tm_now;
-       unsigned long time_now = 0;
-       unsigned long time_set = 0;
-       unsigned long time_gap = 0;
-       int ret = 0;
-
-       ret = sun6i_rtc_gettime(dev, &tm_now);
-       if (ret < 0) {
-               dev_err(dev, "Error in getting time\n");
-               return -EINVAL;
-       }
+       time64_t time_set;
+       u32 counter_val, counter_val_hms;
+       int ret;
 
        time_set = rtc_tm_to_time64(alrm_tm);
-       time_now = rtc_tm_to_time64(&tm_now);
-       if (time_set <= time_now) {
-               dev_err(dev, "Date to set in the past\n");
-               return -EINVAL;
-       }
-
-       time_gap = time_set - time_now;
 
-       if (time_gap > U32_MAX) {
-               dev_err(dev, "Date too far in the future\n");
-               return -EINVAL;
+       if (chip->flags & RTC_LINEAR_DAY) {
+               /*
+                * The alarm registers hold the actual alarm time, encoded
+                * in the same way (linear day + HMS) as the current time.
+                */
+               counter_val_hms = SUN6I_TIME_SET_SEC_VALUE(alrm_tm->tm_sec)  |
+                                 SUN6I_TIME_SET_MIN_VALUE(alrm_tm->tm_min)  |
+                                 SUN6I_TIME_SET_HOUR_VALUE(alrm_tm->tm_hour);
+               /* The division will cut off the H:M:S part of alrm_tm. */
+               counter_val = div_u64(rtc_tm_to_time64(alrm_tm), SECS_PER_DAY);
+       } else {
+               /* The alarm register holds the number of seconds left. */
+               time64_t time_now;
+
+               ret = sun6i_rtc_gettime(dev, &tm_now);
+               if (ret < 0) {
+                       dev_err(dev, "Error in getting time\n");
+                       return -EINVAL;
+               }
+
+               time_now = rtc_tm_to_time64(&tm_now);
+               if (time_set <= time_now) {
+                       dev_err(dev, "Date to set in the past\n");
+                       return -EINVAL;
+               }
+               if ((time_set - time_now) > U32_MAX) {
+                       dev_err(dev, "Date too far in the future\n");
+                       return -EINVAL;
+               }
+
+               counter_val = time_set - time_now;
        }
 
        sun6i_rtc_setaie(0, chip);
        writel(0, chip->base + SUN6I_ALRM_COUNTER);
+       if (chip->flags & RTC_LINEAR_DAY)
+               writel(0, chip->base + SUN6I_ALRM_COUNTER_HMS);
        usleep_range(100, 300);
 
-       writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
+       writel(counter_val, chip->base + SUN6I_ALRM_COUNTER);
+       if (chip->flags & RTC_LINEAR_DAY)
+               writel(counter_val_hms, chip->base + SUN6I_ALRM_COUNTER_HMS);
        chip->alarm = time_set;
 
        sun6i_rtc_setaie(wkalrm->enabled, chip);
@@ -571,20 +587,25 @@ static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
        u32 date = 0;
        u32 time = 0;
 
-       rtc_tm->tm_year -= SUN6I_YEAR_OFF;
-       rtc_tm->tm_mon += 1;
-
-       date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
-               SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon)  |
-               SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
-
-       if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN))
-               date |= SUN6I_LEAP_SET_VALUE(1);
-
        time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec)  |
                SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min)  |
                SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
 
+       if (chip->flags & RTC_LINEAR_DAY) {
+               /* The division will cut off the H:M:S part of rtc_tm. */
+               date = div_u64(rtc_tm_to_time64(rtc_tm), SECS_PER_DAY);
+       } else {
+               rtc_tm->tm_year -= SUN6I_YEAR_OFF;
+               rtc_tm->tm_mon += 1;
+
+               date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
+                       SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon)  |
+                       SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
+
+               if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN))
+                       date |= SUN6I_LEAP_SET_VALUE(1);
+       }
+
        /* Check whether registers are writable */
        if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
                           SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
@@ -668,11 +689,35 @@ static int sun6i_rtc_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops,
        sun6i_rtc_suspend, sun6i_rtc_resume);
 
+static void sun6i_rtc_bus_clk_cleanup(void *data)
+{
+       struct clk *bus_clk = data;
+
+       clk_disable_unprepare(bus_clk);
+}
+
 static int sun6i_rtc_probe(struct platform_device *pdev)
 {
        struct sun6i_rtc_dev *chip = sun6i_rtc;
+       struct device *dev = &pdev->dev;
+       struct clk *bus_clk;
        int ret;
 
+       bus_clk = devm_clk_get_optional(dev, "bus");
+       if (IS_ERR(bus_clk))
+               return PTR_ERR(bus_clk);
+
+       if (bus_clk) {
+               ret = clk_prepare_enable(bus_clk);
+               if (ret)
+                       return ret;
+
+               ret = devm_add_action_or_reset(dev, sun6i_rtc_bus_clk_cleanup,
+                                              bus_clk);
+               if (ret)
+                       return ret;
+       }
+
        if (!chip) {
                chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
                if (!chip)
@@ -683,10 +728,18 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
                chip->base = devm_platform_ioremap_resource(pdev, 0);
                if (IS_ERR(chip->base))
                        return PTR_ERR(chip->base);
+
+               if (IS_REACHABLE(CONFIG_SUN6I_RTC_CCU)) {
+                       ret = sun6i_rtc_ccu_probe(dev, chip->base);
+                       if (ret)
+                               return ret;
+               }
        }
 
        platform_set_drvdata(pdev, chip);
 
+       chip->flags = (unsigned long)of_device_get_match_data(&pdev->dev);
+
        chip->irq = platform_get_irq(pdev, 0);
        if (chip->irq < 0)
                return chip->irq;
@@ -733,7 +786,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
                return PTR_ERR(chip->rtc);
 
        chip->rtc->ops = &sun6i_rtc_ops;
-       chip->rtc->range_max = 2019686399LL; /* 2033-12-31 23:59:59 */
+       if (chip->flags & RTC_LINEAR_DAY)
+               chip->rtc->range_max = (65536 * SECS_PER_DAY) - 1;
+       else
+               chip->rtc->range_max = 2019686399LL; /* 2033-12-31 23:59:59 */
 
        ret = devm_rtc_register_device(chip->rtc);
        if (ret)
@@ -758,6 +814,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
        { .compatible = "allwinner,sun8i-v3-rtc" },
        { .compatible = "allwinner,sun50i-h5-rtc" },
        { .compatible = "allwinner,sun50i-h6-rtc" },
+       { .compatible = "allwinner,sun50i-h616-rtc",
+               .data = (void *)RTC_LINEAR_DAY },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
index 2018614f258f6f83cf9897897500022a49fd20ae..6eaa9321c074102c58fdf3aa8069a82936cab8be 100644 (file)
@@ -432,14 +432,21 @@ static int wm8350_rtc_probe(struct platform_device *pdev)
                return ret;
        }
 
-       wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
+       ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
                            wm8350_rtc_update_handler, 0,
                            "RTC Seconds", wm8350);
+       if (ret)
+               return ret;
+
        wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
 
-       wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
+       ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
                            wm8350_rtc_alarm_handler, 0,
                            "RTC Alarm", wm8350);
+       if (ret) {
+               wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350);
+               return ret;
+       }
 
        return 0;
 }
index cf68a9b1c9ebbc64cb37620ecc0331dfb2481d76..d3d0054e21fd02cf633b23d1071ad4a3450e19e5 100644 (file)
@@ -180,8 +180,6 @@ static int xgene_rtc_probe(struct platform_device *pdev)
                return ret;
        }
 
-       /* HW does not support update faster than 1 seconds */
-       pdata->rtc->uie_unsupported = 1;
        pdata->rtc->ops = &xgene_rtc_ops;
        pdata->rtc->range_max = U32_MAX;
 
diff --git a/include/dt-bindings/clock/sun6i-rtc.h b/include/dt-bindings/clock/sun6i-rtc.h
new file mode 100644 (file)
index 0000000..c845493
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
+
+#ifndef _DT_BINDINGS_CLK_SUN6I_RTC_H_
+#define _DT_BINDINGS_CLK_SUN6I_RTC_H_
+
+#define CLK_OSC32K             0
+#define CLK_OSC32K_FANOUT      1
+#define CLK_IOSC               2
+
+#endif /* _DT_BINDINGS_CLK_SUN6I_RTC_H_ */
index cf32123b39f5af4113af5393a9666f5f0c42c17b..57c8ec44ab4e69ca5489a04b3da6bdc5ec05ab21 100644 (file)
@@ -9,4 +9,6 @@
 int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode);
 int sunxi_ccu_get_mmc_timing_mode(struct clk *clk);
 
+int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg);
+
 #endif
index 808bb4cee2300ecf21611f492cb1fcbf1e519717..b0da04fe087bb8cb32de8f62a4a52abbaa4d5b80 100644 (file)
@@ -86,6 +86,8 @@ struct cmos_rtc_board_info {
    /* 2 values for divider stage reset, others for "testing purposes only" */
 #  define RTC_DIV_RESET1       0x60
 #  define RTC_DIV_RESET2       0x70
+   /* In AMD BKDG bit 5 and 6 are reserved, bit 4 is for select dv0 bank */
+#  define RTC_AMD_BANK_SELECT  0x10
   /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
 # define RTC_RATE_SELECT       0x0F
 
index 47fd1c2d3a579f019a2e3c5af39bbf229d1d3908..1fd9c6a21ebe85d8474867ba344c3451eb99bc99 100644 (file)
@@ -110,8 +110,6 @@ struct rtc_device {
        struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
        int pie_enabled;
        struct work_struct irqwork;
-       /* Some hardware can't support UIE mode */
-       int uie_unsupported;
 
        /*
         * This offset specifies the update timing of the RTC.
index 67ee9d20cc5aa76aaefaab949010e4afc67e76e2..5a41c3bbcbe3821ebc854976e3bfeea685689ee9 100644 (file)
@@ -46,7 +46,6 @@ struct ds1685_priv {
        u32 regstep;
        int irq_num;
        bool bcd_mode;
-       bool no_irq;
        u8 (*read)(struct ds1685_priv *, int);
        void (*write)(struct ds1685_priv *, int, u8);
        void (*prepare_poweroff)(void);
index 03e5b776e59764c69ca95997f153df9111391050..97aca4503a6a390319ca7a53e8b19748912b8b19 100644 (file)
@@ -133,7 +133,8 @@ struct rtc_param {
 #define RTC_FEATURE_UPDATE_INTERRUPT   4
 #define RTC_FEATURE_CORRECTION         5
 #define RTC_FEATURE_BACKUP_SWITCH_MODE 6
-#define RTC_FEATURE_CNT                        7
+#define RTC_FEATURE_ALARM_WAKEUP_ONLY  7
+#define RTC_FEATURE_CNT                        8
 
 /* parameter list */
 #define RTC_PARAM_FEATURES             0