Merge tag 'rtc-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Mar 2019 17:54:55 +0000 (09:54 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Mar 2019 17:54:55 +0000 (09:54 -0800)
Pull RTC updates from Alexandre Belloni:
 "There is an unusual amount of new drivers this cycle, and this
  explains the number of insertions.

  Other than that, the changes are the usual fixes and feature addition.

  Subsystem updates:
   - new quartz-load-femtofarads DT property for quartz load capacitance
   - remove rtc_class_ops.read_callback

  New drivers:
   - Abracon AB-RTCMC-32.768kHz-EOZ9
   - Amlogic Meson RTC
   - Cadence RTC IP
   - Microcrystal RV3028
   - Whwave sd3078

  Driver updates:
   - cmos: ignore bogus century byte
   - ds1307: rework rx8130 support
   - isl1208: add isl1209 support, nvmem support
   - rs5C372: report invalid time when the oscillator stopped
   - rx8581: add rx8571 support"

* tag 'rtc-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (66 commits)
  rtc: pic32: convert to SPDX identifier
  rtc: pic32: let the core handle range
  rtc: pic32: convert to devm_rtc_allocate_device
  rtc: update my email address
  rtc: rv8803: convert to SPDX identifier
  rtc: rv8803: let the core handle range
  rtc: tx4939: convert to SPDX identifier
  rtc: tx4939: use .set_time
  rtc: tx4939: switch to rtc_time64_to_tm/rtc_tm_to_time64
  rtc: tx4939: set range
  rtc: tx4939: remove useless test
  rtc: zynqmp: let the core handle range
  rtc: zynqmp: fix possible race condition
  rtc: imx-sc: use rtc_time64_to_tm
  rtc: rx8581: Add support for Epson rx8571 RTC
  dt-bindings: rtc: add rx8571 compatible
  rtc: pcf85063: remove dead code
  rtc: remove rtc_class_ops.read_callback
  rtc: add AB-RTCMC-32.768kHz-EOZ9 RTC support
  dt-bindings: rtc: add ABEOZ9
  ...

42 files changed:
Documentation/devicetree/bindings/property-units.txt
Documentation/devicetree/bindings/rtc/abracon,abx80x.txt
Documentation/devicetree/bindings/rtc/cdns,rtc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/isil,isl1208.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/isil,isl1219.txt [deleted file]
Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/rtc-meson.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/rtc.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
MAINTAINERS
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/dev.c
drivers/rtc/lib.c
drivers/rtc/rtc-88pm80x.c
drivers/rtc/rtc-88pm860x.c
drivers/rtc/rtc-ab-eoz9.c [new file with mode: 0644]
drivers/rtc/rtc-abx80x.c
drivers/rtc/rtc-cadence.c [new file with mode: 0644]
drivers/rtc/rtc-coh901331.c
drivers/rtc/rtc-ds1307.c
drivers/rtc/rtc-ds1672.c
drivers/rtc/rtc-hym8563.c
drivers/rtc/rtc-imx-sc.c
drivers/rtc/rtc-isl1208.c
drivers/rtc/rtc-mc146818-lib.c
drivers/rtc/rtc-meson.c [new file with mode: 0644]
drivers/rtc/rtc-pcf85063.c
drivers/rtc/rtc-pcf8523.c
drivers/rtc/rtc-pic32.c
drivers/rtc/rtc-pm8xxx.c
drivers/rtc/rtc-rs5c372.c
drivers/rtc/rtc-rv3028.c [new file with mode: 0644]
drivers/rtc/rtc-rv8803.c
drivers/rtc/rtc-rx8581.c
drivers/rtc/rtc-s3c.c
drivers/rtc/rtc-sd3078.c [new file with mode: 0644]
drivers/rtc/rtc-snvs.c
drivers/rtc/rtc-tx4939.c
drivers/rtc/rtc-zynqmp.c
include/linux/rtc.h

index 45ce054..bfd3373 100644 (file)
@@ -31,6 +31,7 @@ Electricity
 -microwatt-hours: micro Watt-hours
 -microvolt     : micro volts
 -picofarads    : picofarads
+-femtofarads   : femtofarads
 
 Temperature
 ----------------------------------------
index 18b892d..2405e35 100644 (file)
@@ -16,6 +16,7 @@ Required properties:
         "abracon,ab1803"
         "abracon,ab1804"
         "abracon,ab1805"
+        "microcrystal,rv1805"
        Using "abracon,abx80x" will enable chip autodetection.
  - "reg": I2C bus address of the device
 
diff --git a/Documentation/devicetree/bindings/rtc/cdns,rtc.txt b/Documentation/devicetree/bindings/rtc/cdns,rtc.txt
new file mode 100644 (file)
index 0000000..14a0448
--- /dev/null
@@ -0,0 +1,25 @@
+Cadence Real Time Clock
+
+The Cadence RTC controller with date, time and alarm capabilities.
+The alarm may wake the system from low-power state.
+
+Required properties:
+- compatible: Should be "cdns,rtc-r109v3"
+- reg: Specifies base physical address and size of the register area.
+- interrupts: A single interrupt specifier.
+- clocks: Must contain two entries:
+       - pclk: APB registers clock
+       - ref_clk: reference 1Hz or 100Hz clock, depending on IP configuration
+       See ../clocks/clock-bindings.txt for details.
+
+Example:
+        rtc0: rtc@fd080000 {
+               compatible = "cdns,rtc-r109v3";
+               reg = <0xfd080000 0x1000>;
+
+               clock-names = "pclk", "ref_clk";
+               clocks = <&sysclock>, <&refclock>;
+
+               interrupt-parent = <&gic>;
+               interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>;
+        };
diff --git a/Documentation/devicetree/bindings/rtc/isil,isl1208.txt b/Documentation/devicetree/bindings/rtc/isil,isl1208.txt
new file mode 100644 (file)
index 0000000..51f0030
--- /dev/null
@@ -0,0 +1,38 @@
+Intersil ISL1209/19 I2C RTC/Alarm chip with event in
+
+ISL12X9 have additional pins EVIN and #EVDET for tamper detection, while the
+ISL1208 and ISL1218 do not.  They are all use the same driver with the bindings
+described here, with chip specific properties as noted.
+
+Required properties supported by the device:
+ - "compatible": Should be one of the following:
+               - "isil,isl1208"
+               - "isil,isl1209"
+               - "isil,isl1218"
+               - "isil,isl1219"
+ - "reg": I2C bus address of the device
+
+Optional properties:
+ - "interrupt-names": list which may contains "irq" and "evdet"
+       evdet applies to isl1209 and isl1219 only
+ - "interrupts": list of interrupts for "irq" and "evdet"
+       evdet applies to isl1209 and isl1219 only
+ - "isil,ev-evienb": Enable or disable internal pull on EVIN pin
+       Applies to isl1209 and isl1219 only
+       Possible values are 0 and 1
+       Value 0 enables internal pull-up on evin pin, 1 disables it.
+       Default will leave the non-volatile configuration of the pullup
+       as is.
+
+Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12 and #EVDET pin
+connected to SoC gpio2 pin 24 and internal pull-up enabled in EVIN pin.
+
+       isl1219: rtc@68 {
+               compatible = "isil,isl1219";
+               reg = <0x68>;
+               interrupt-names = "irq", "evdet";
+               interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>,
+                       <&gpio2 24 IRQ_TYPE_EDGE_FALLING>;
+               isil,ev-evienb = <1>;
+       };
+
diff --git a/Documentation/devicetree/bindings/rtc/isil,isl1219.txt b/Documentation/devicetree/bindings/rtc/isil,isl1219.txt
deleted file mode 100644 (file)
index c3efd48..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-Intersil ISL1219 I2C RTC/Alarm chip with event in
-
-ISL1219 has additional pins EVIN and #EVDET for tamper detection.
-
-Required properties supported by the device:
-
- - "compatible": must be "isil,isl1219"
- - "reg": I2C bus address of the device
-
-Optional properties:
-
- - "interrupt-names": list which may contains "irq" and "evdet"
- - "interrupts": list of interrupts for "irq" and "evdet"
- - "isil,ev-evienb": if present EV.EVIENB bit is set to the specified
-                     value for proper operation.
-
-
-Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12
- and #EVDET pin connected to SoC gpio2 pin 24:
-
-       isl1219: rtc@68 {
-               compatible = "isil,isl1219";
-               reg = <0x68>;
-               interrupt-names = "irq", "evdet";
-               interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>,
-                       <&gpio2 24 IRQ_TYPE_EDGE_FALLING>;
-               isil,ev-evienb = <1>;
-       };
-
diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt
new file mode 100644 (file)
index 0000000..d3e380a
--- /dev/null
@@ -0,0 +1,18 @@
+* NXP PCF85063 Real Time Clock
+
+Required properties:
+- compatible: Should contain "nxp,pcf85063".
+- reg: I2C address for chip.
+
+Optional property:
+- quartz-load-femtofarads: The capacitive load of the quartz(x-tal),
+  expressed in femto Farad (fF). Valid values are 7000 and 12500.
+  Default value (if no value is specified) is 7000fF.
+
+Example:
+
+pcf85063: rtc@51 {
+       compatible = "nxp,pcf85063";
+       reg = <0x51>;
+       quartz-load-femtofarads = <12500>;
+};
diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt
new file mode 100644 (file)
index 0000000..0b1080c
--- /dev/null
@@ -0,0 +1,18 @@
+* NXP PCF8523 Real Time Clock
+
+Required properties:
+- compatible: Should contain "nxp,pcf8523".
+- reg: I2C address for chip.
+
+Optional property:
+- quartz-load-femtofarads: The capacitive load of the quartz(x-tal),
+  expressed in femto Farad (fF). Valid values are 7000 and 12500.
+  Default value (if no value is specified) is 12500fF.
+
+Example:
+
+pcf8523: rtc@68 {
+       compatible = "nxp,pcf8523";
+       reg = <0x68>;
+       quartz-load-femtofarads = <7000>;
+};
diff --git a/Documentation/devicetree/bindings/rtc/rtc-meson.txt b/Documentation/devicetree/bindings/rtc/rtc-meson.txt
new file mode 100644 (file)
index 0000000..e921fe6
--- /dev/null
@@ -0,0 +1,35 @@
+* Amlogic Meson6, Meson8, Meson8b and Meson8m2 RTC
+
+Required properties:
+- compatible: should be one of the following describing the hardware:
+       * "amlogic,meson6-rtc"
+       * "amlogic,meson8-rtc"
+       * "amlogic,meson8b-rtc"
+       * "amlogic,meson8m2-rtc"
+
+- reg: physical register space for the controller's memory mapped registers.
+- interrupts: the interrupt line of the RTC block.
+- clocks: reference to the external 32.768kHz crystal oscillator.
+- vdd-supply: reference to the power supply of the RTC block.
+- resets: reset controller reference to allow reset of the controller
+
+Optional properties for the battery-backed non-volatile memory:
+- #address-cells: should be 1 to address the battery-backed non-volatile memory
+- #size-cells: should be 1 to reference the battery-backed non-volatile memory
+
+Optional child nodes:
+- see ../nvmem/nvmem.txt
+
+Example:
+
+       rtc: rtc@740 {
+               compatible = "amlogic,meson6-rtc";
+               reg = <0x740 0x14>;
+               interrupts = <GIC_SPI 72 IRQ_TYPE_EDGE_RISING>;
+               clocks = <&rtc32k_xtal>;
+               vdd-supply = <&rtc_vdd>;
+               resets = <&reset RESET_RTC>;
+
+               #address-cells = <1>;
+               #size-cells = <1>;
+       };
index 7c8da69..f4687c6 100644 (file)
@@ -21,12 +21,16 @@ Optional properties
 The following properties may not be supported by all drivers. However, if a
 driver wants to support one of the below features, it should adapt the bindings
 below.
-- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given
-                          if trickle charger should be enabled
-- trickle-diode-disable : Do not use internal trickle charger diode Should be
-                          given if internal trickle charger diode should be
-                          disabled
-- wakeup-source :         Enables wake up of host system on alarm
+- trickle-resistor-ohms :   Selected resistor for trickle charger. Should be given
+                            if trickle charger should be enabled
+- trickle-diode-disable :   Do not use internal trickle charger diode Should be
+                            given if internal trickle charger diode should be
+                            disabled
+- wakeup-source :           Enables wake up of host system on alarm
+- quartz-load-femtofarads : The capacitive load of the quartz(x-tal),
+                            expressed in femto Farad (fF).
+                            The default value shall be listed (if optional),
+                            and likewise all valid values.
 
 Trivial RTCs
 ------------
@@ -39,21 +43,23 @@ possibly an interrupt line.
 Compatible             Vendor / Chip
 ==========             =============
 abracon,abb5zes3       AB-RTCMC-32.768kHz-B5ZE-S3: Real Time Clock/Calendar Module with I2C Interface
+abracon,abeoz9         AB-RTCMC-32.768kHz-EOZ9: Real Time Clock/Calendar Module with I2C Interface
 dallas,ds1374          I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
 dallas,ds1672          Dallas DS1672 Real-time Clock
 dallas,ds3232          Extremely Accurate I²C RTC with Integrated Crystal and SRAM
 epson,rx8010           I2C-BUS INTERFACE REAL TIME CLOCK MODULE
+epson,rx8571           I2C-BUS INTERFACE REAL TIME CLOCK MODULE with Battery Backed RAM
 epson,rx8581           I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 emmicro,em3027         EM Microelectronic EM3027 Real-time Clock
 isil,isl1208           Intersil ISL1208 Low Power RTC with Battery Backed SRAM
 isil,isl1218           Intersil ISL1218 Low Power RTC with Battery Backed SRAM
 isil,isl12022          Intersil ISL12022 Real-time Clock
+microcrystal,rv3028    Real Time Clock Module with I2C-Bus
 microcrystal,rv3029    Real Time Clock Module with I2C-Bus
+microcrystal,rv8523    Real Time Clock
 nxp,pcf2127            Real-time clock
 nxp,pcf2129            Real-time clock
-nxp,pcf8523            Real-time Clock
 nxp,pcf8563            Real-time clock/calendar
-nxp,pcf85063           Tiny Real-Time Clock
 pericom,pt7c4338       Real-time Clock Module
 ricoh,r2025sd          I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 ricoh,r2221tl          I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
@@ -62,3 +68,4 @@ ricoh,rs5c372b                I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 ricoh,rv5c386          I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 ricoh,rv5c387a         I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 sii,s35390a            2-wire CMOS real-time clock
+whwave,sd3078          I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
index 98f83ed..aaa18bb 100644 (file)
@@ -439,6 +439,7 @@ vot Vision Optical Technology Co., Ltd.
 wd     Western Digital Corp.
 wetek  WeTek Electronics, limited.
 wexler Wexler
+whwave  Shenzhen whwave Electronics, Inc.
 wi2wi  Wi2Wi, Inc.
 winbond Winbond Electronics corp.
 winstar        Winstar Display Corp.
index a5f9d57..cd4fd96 100644 (file)
@@ -16697,6 +16697,12 @@ L:     linux-gpio@vger.kernel.org
 S:     Maintained
 F:     drivers/gpio/gpio-wcove.c
 
+WHWAVE RTC DRIVER
+M:     Dianlong Li <long17.cool@163.com>
+L:     linux-rtc@vger.kernel.org
+S:     Maintained
+F:     drivers/rtc/rtc-sd3078.c
+
 WIIMOTE HID DRIVER
 M:     David Herrmann <dh.herrmann@googlemail.com>
 L:     linux-input@vger.kernel.org
index 225b0b8..e1a1f2b 100644 (file)
@@ -185,6 +185,16 @@ config RTC_DRV_ABB5ZES3
          This driver can also be built as a module. If so, the module
          will be called rtc-ab-b5ze-s3.
 
+config RTC_DRV_ABEOZ9
+       select REGMAP_I2C
+       tristate "Abracon AB-RTCMC-32.768kHz-EOZ9"
+       help
+         If you say yes here you get support for the Abracon
+         AB-RTCMC-32.768kHz-EOA9 I2C RTC chip.
+
+         This driver can also be built as a module. If so, the module
+         will be called rtc-ab-e0z9.
+
 config RTC_DRV_ABX80X
        tristate "Abracon ABx80x"
        select WATCHDOG_CORE if WATCHDOG
@@ -601,9 +611,10 @@ config RTC_DRV_RX8010
          will be called rtc-rx8010.
 
 config RTC_DRV_RX8581
-       tristate "Epson RX-8581"
+       tristate "Epson RX-8571/RX-8581"
        help
-         If you say yes here you will get support for the Epson RX-8581.
+         If you say yes here you will get support for the Epson RX-8571/
+         RX-8581.
 
          This driver can also be built as a module. If so the module
          will be called rtc-rx8581.
@@ -626,6 +637,15 @@ config RTC_DRV_EM3027
          This driver can also be built as a module. If so, the module
          will be called rtc-em3027.
 
+config RTC_DRV_RV3028
+       tristate "Micro Crystal RV3028"
+       help
+         If you say yes here you get support for the Micro Crystal
+         RV3028.
+
+         This driver can also be built as a module. If so, the module
+         will be called rtc-rv3028.
+
 config RTC_DRV_RV8803
        tristate "Micro Crystal RV8803, Epson RX8900"
        help
@@ -646,6 +666,15 @@ config RTC_DRV_S5M
          This driver can also be built as a module. If so, the module
          will be called rtc-s5m.
 
+config RTC_DRV_SD3078
+    tristate "ZXW Crystal SD3078"
+    help
+      If you say yes here you get support for the ZXW Crystal
+      SD3078 RTC chips.
+
+      This driver can also be built as a module. If so, the module
+      will be called rtc-sd3078
+
 endif # I2C
 
 comment "SPI RTC drivers"
@@ -1285,6 +1314,17 @@ config RTC_DRV_IMXDI
           This driver can also be built as a module, if so, the module
           will be called "rtc-imxdi".
 
+config RTC_DRV_MESON
+       tristate "Amlogic Meson RTC"
+       depends on (ARM && ARCH_MESON) || COMPILE_TEST
+       select REGMAP_MMIO
+       help
+          Support for the RTC block on the Amlogic Meson6, Meson8, Meson8b
+          and Meson8m2 SoCs.
+
+          This driver can also be built as a module, if so, the module
+          will be called "rtc-meson".
+
 config RTC_DRV_OMAP
        tristate "TI OMAP Real Time Clock"
        depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST
@@ -1508,6 +1548,16 @@ config RTC_DRV_ARMADA38X
          This driver can also be built as a module. If so, the module
          will be called armada38x-rtc.
 
+config RTC_DRV_CADENCE
+       tristate "Cadence RTC driver"
+       depends on OF && HAS_IOMEM
+       help
+         If you say Y here you will get access to Cadence RTC IP
+         found on certain SOCs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called rtc-cadence.
+
 config RTC_DRV_FTRTC010
        tristate "Faraday Technology FTRTC010 RTC"
        depends on HAS_IOMEM
@@ -1679,6 +1729,7 @@ config RTC_DRV_SNVS
 
 config RTC_DRV_IMX_SC
        depends on IMX_SCU
+       depends on HAVE_ARM_SMCCC
        tristate "NXP i.MX System Controller RTC support"
        help
           If you say yes here you get support for the NXP i.MX System
@@ -1795,8 +1846,7 @@ comment "HID Sensor RTC drivers"
 config RTC_DRV_HID_SENSOR_TIME
        tristate "HID Sensor Time"
        depends on USB_HID
-       select IIO
-       select HID_SENSOR_HUB
+       depends on HID_SENSOR_HUB && IIO
        select HID_SENSOR_IIO_COMMON
        help
          Say yes here to build support for the HID Sensors of type Time.
index df022d8..1bf9f75 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_88PM860X)        += rtc-88pm860x.o
 obj-$(CONFIG_RTC_DRV_AB3100)   += rtc-ab3100.o
 obj-$(CONFIG_RTC_DRV_AB8500)   += rtc-ab8500.o
 obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
+obj-$(CONFIG_RTC_DRV_ABEOZ9)   += rtc-ab-eoz9.o
 obj-$(CONFIG_RTC_DRV_ABX80X)   += rtc-abx80x.o
 obj-$(CONFIG_RTC_DRV_AC100)    += rtc-ac100.o
 obj-$(CONFIG_RTC_DRV_ARMADA38X)        += rtc-armada38x.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_AU1XXX)  += rtc-au1xxx.o
 obj-$(CONFIG_RTC_DRV_BQ32K)    += rtc-bq32k.o
 obj-$(CONFIG_RTC_DRV_BQ4802)   += rtc-bq4802.o
 obj-$(CONFIG_RTC_DRV_BRCMSTB)  += rtc-brcmstb-waketimer.o
+obj-$(CONFIG_RTC_DRV_CADENCE)  += rtc-cadence.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
@@ -100,6 +102,7 @@ obj-$(CONFIG_RTC_DRV_MAX8997)       += rtc-max8997.o
 obj-$(CONFIG_RTC_DRV_MAX8998)  += rtc-max8998.o
 obj-$(CONFIG_RTC_DRV_MC13XXX)  += rtc-mc13xxx.o
 obj-$(CONFIG_RTC_DRV_MCP795)   += rtc-mcp795.o
+obj-$(CONFIG_RTC_DRV_MESON)    += rtc-meson.o
 obj-$(CONFIG_RTC_DRV_MOXART)   += rtc-moxart.o
 obj-$(CONFIG_RTC_DRV_MPC5121)  += rtc-mpc5121.o
 obj-$(CONFIG_RTC_DRV_MSM6242)  += rtc-msm6242.o
@@ -137,6 +140,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313)       += rtc-rs5c313.o
 obj-$(CONFIG_RTC_DRV_RS5C348)  += rtc-rs5c348.o
 obj-$(CONFIG_RTC_DRV_RS5C372)  += rtc-rs5c372.o
 obj-$(CONFIG_RTC_DRV_RTD119X)  += rtc-rtd119x.o
+obj-$(CONFIG_RTC_DRV_RV3028)   += rtc-rv3028.o
 obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
 obj-$(CONFIG_RTC_DRV_RV8803)   += rtc-rv8803.o
 obj-$(CONFIG_RTC_DRV_RX4581)   += rtc-rx4581.o
@@ -149,6 +153,7 @@ obj-$(CONFIG_RTC_DRV_S3C)   += rtc-s3c.o
 obj-$(CONFIG_RTC_DRV_S5M)      += rtc-s5m.o
 obj-$(CONFIG_RTC_DRV_SA1100)   += rtc-sa1100.o
 obj-$(CONFIG_RTC_DRV_SC27XX)   += rtc-sc27xx.o
+obj-$(CONFIG_RTC_DRV_SD3078)   += rtc-sd3078.o
 obj-$(CONFIG_RTC_DRV_SH)       += rtc-sh.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)  += rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_SNVS)     += rtc-snvs.o
index 43d962a..1d006ef 100644 (file)
@@ -178,11 +178,6 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        remove_wait_queue(&rtc->irq_queue, &wait);
 
        if (ret == 0) {
-               /* Check for any data updates */
-               if (rtc->ops->read_callback)
-                       data = rtc->ops->read_callback(rtc->dev.parent,
-                                                      data);
-
                if (sizeof(int) != sizeof(long) &&
                    count == sizeof(unsigned int))
                        ret = put_user(data, (unsigned int __user *)buf) ?:
index ef160da..9714cb3 100644 (file)
@@ -100,7 +100,7 @@ int rtc_valid_tm(struct rtc_time *tm)
        if (tm->tm_year < 70
                || ((unsigned)tm->tm_mon) >= 12
                || tm->tm_mday < 1
-               || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
+               || tm->tm_mday > rtc_month_days(tm->tm_mon, ((unsigned)tm->tm_year + 1900))
                || ((unsigned)tm->tm_hour) >= 24
                || ((unsigned)tm->tm_min) >= 60
                || ((unsigned)tm->tm_sec) >= 60)
@@ -116,8 +116,8 @@ EXPORT_SYMBOL(rtc_valid_tm);
  */
 time64_t rtc_tm_to_time64(struct rtc_time *tm)
 {
-       return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
-                       tm->tm_hour, tm->tm_min, tm->tm_sec);
+       return mktime64(((unsigned)tm->tm_year + 1900), tm->tm_mon + 1,
+                       tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
 }
 EXPORT_SYMBOL(rtc_tm_to_time64);
 
index cab293c..1fc48eb 100644 (file)
@@ -114,12 +114,14 @@ static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
        unsigned char buf[4];
        unsigned long ticks, base, data;
        regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
-       base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
 
        /* load 32-bit read-only counter */
        regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        ticks = base + data;
        dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
@@ -137,7 +139,8 @@ static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
 
        /* load 32-bit read-only counter */
        regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        base = ticks - data;
        dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
@@ -158,11 +161,13 @@ static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        int ret;
 
        regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
-       base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
 
        regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        ticks = base + data;
        dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
@@ -185,12 +190,14 @@ static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
 
        regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
-       base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
 
        /* load 32-bit read-only counter */
        regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        ticks = base + data;
        dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
index 01ffc0e..d25282b 100644 (file)
@@ -115,11 +115,13 @@ static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm)
        pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
        dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
                buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
-       base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+       base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) |
+               (buf[5] << 8) | buf[7];
 
        /* load 32-bit read-only counter */
        pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        ticks = base + data;
        dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
@@ -145,7 +147,8 @@ static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm)
 
        /* load 32-bit read-only counter */
        pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        base = ticks - data;
        dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
@@ -170,10 +173,12 @@ static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
        dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
                buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
-       base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+       base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) |
+               (buf[5] << 8) | buf[7];
 
        pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        ticks = base + data;
        dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
@@ -198,11 +203,13 @@ static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
        dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
                buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
-       base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+       base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) |
+               (buf[5] << 8) | buf[7];
 
        /* load 32-bit read-only counter */
        pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
-       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+               (buf[1] << 8) | buf[0];
        ticks = base + data;
        dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
                base, data, ticks);
diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c
new file mode 100644 (file)
index 0000000..e4f6e00
--- /dev/null
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock driver for AB-RTCMC-32.768kHz-EOZ9 chip.
+ * Copyright (C) 2019 Orolia
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#define ABEOZ9_REG_CTRL1               0x00
+#define ABEOZ9_REG_CTRL1_MASK          GENMASK(7, 0)
+#define ABEOZ9_REG_CTRL1_WE            BIT(0)
+#define ABEOZ9_REG_CTRL1_TE            BIT(1)
+#define ABEOZ9_REG_CTRL1_TAR           BIT(2)
+#define ABEOZ9_REG_CTRL1_EERE          BIT(3)
+#define ABEOZ9_REG_CTRL1_SRON          BIT(4)
+#define ABEOZ9_REG_CTRL1_TD0           BIT(5)
+#define ABEOZ9_REG_CTRL1_TD1           BIT(6)
+#define ABEOZ9_REG_CTRL1_CLKINT                BIT(7)
+
+#define ABEOZ9_REG_CTRL_INT            0x01
+#define ABEOZ9_REG_CTRL_INT_AIE                BIT(0)
+#define ABEOZ9_REG_CTRL_INT_TIE                BIT(1)
+#define ABEOZ9_REG_CTRL_INT_V1IE       BIT(2)
+#define ABEOZ9_REG_CTRL_INT_V2IE       BIT(3)
+#define ABEOZ9_REG_CTRL_INT_SRIE       BIT(4)
+
+#define ABEOZ9_REG_CTRL_INT_FLAG       0x02
+#define ABEOZ9_REG_CTRL_INT_FLAG_AF    BIT(0)
+#define ABEOZ9_REG_CTRL_INT_FLAG_TF    BIT(1)
+#define ABEOZ9_REG_CTRL_INT_FLAG_V1IF  BIT(2)
+#define ABEOZ9_REG_CTRL_INT_FLAG_V2IF  BIT(3)
+#define ABEOZ9_REG_CTRL_INT_FLAG_SRF   BIT(4)
+
+#define ABEOZ9_REG_CTRL_STATUS         0x03
+#define ABEOZ9_REG_CTRL_STATUS_V1F     BIT(2)
+#define ABEOZ9_REG_CTRL_STATUS_V2F     BIT(3)
+#define ABEOZ9_REG_CTRL_STATUS_SR      BIT(4)
+#define ABEOZ9_REG_CTRL_STATUS_PON     BIT(5)
+#define ABEOZ9_REG_CTRL_STATUS_EEBUSY  BIT(7)
+
+#define ABEOZ9_REG_SEC                 0x08
+#define ABEOZ9_REG_MIN                 0x09
+#define ABEOZ9_REG_HOURS               0x0A
+#define ABEOZ9_HOURS_PM                        BIT(6)
+#define ABEOZ9_REG_DAYS                        0x0B
+#define ABEOZ9_REG_WEEKDAYS            0x0C
+#define ABEOZ9_REG_MONTHS              0x0D
+#define ABEOZ9_REG_YEARS               0x0E
+
+#define ABEOZ9_SEC_LEN                 7
+
+#define ABEOZ9_REG_REG_TEMP            0x20
+#define ABEOZ953_TEMP_MAX              120
+#define ABEOZ953_TEMP_MIN              -60
+
+#define ABEOZ9_REG_EEPROM              0x30
+#define ABEOZ9_REG_EEPROM_MASK         GENMASK(8, 0)
+#define ABEOZ9_REG_EEPROM_THP          BIT(0)
+#define ABEOZ9_REG_EEPROM_THE          BIT(1)
+#define ABEOZ9_REG_EEPROM_FD0          BIT(2)
+#define ABEOZ9_REG_EEPROM_FD1          BIT(3)
+#define ABEOZ9_REG_EEPROM_R1K          BIT(4)
+#define ABEOZ9_REG_EEPROM_R5K          BIT(5)
+#define ABEOZ9_REG_EEPROM_R20K         BIT(6)
+#define ABEOZ9_REG_EEPROM_R80K         BIT(7)
+
+struct abeoz9_rtc_data {
+       struct rtc_device *rtc;
+       struct regmap *regmap;
+       struct device *hwmon_dev;
+};
+
+static int abeoz9_check_validity(struct device *dev)
+{
+       struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       int ret;
+       int val;
+
+       ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val);
+       if (ret < 0) {
+               dev_err(dev,
+                       "unable to get CTRL_STATUS register (%d)\n", ret);
+               return ret;
+       }
+
+       if (val & ABEOZ9_REG_CTRL_STATUS_PON) {
+               dev_warn(dev, "power-on reset detected, date is invalid\n");
+               return -EINVAL;
+       }
+
+       if (val & ABEOZ9_REG_CTRL_STATUS_V1F) {
+               dev_warn(dev,
+                        "voltage drops below VLOW1 threshold, date is invalid\n");
+               return -EINVAL;
+       }
+
+       if ((val & ABEOZ9_REG_CTRL_STATUS_V2F)) {
+               dev_warn(dev,
+                        "voltage drops below VLOW2 threshold, date is invalid\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int abeoz9_reset_validity(struct regmap *regmap)
+{
+       return regmap_update_bits(regmap, ABEOZ9_REG_CTRL_STATUS,
+                                 ABEOZ9_REG_CTRL_STATUS_V1F |
+                                 ABEOZ9_REG_CTRL_STATUS_V2F |
+                                 ABEOZ9_REG_CTRL_STATUS_PON,
+                                 0);
+}
+
+static int abeoz9_rtc_get_time(struct device *dev, struct rtc_time *tm)
+{
+       struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+       u8 regs[ABEOZ9_SEC_LEN];
+       int ret;
+
+       ret = abeoz9_check_validity(dev);
+       if (ret)
+               return ret;
+
+       ret = regmap_bulk_read(data->regmap, ABEOZ9_REG_SEC,
+                              regs,
+                              sizeof(regs));
+       if (ret) {
+               dev_err(dev, "reading RTC time failed (%d)\n", ret);
+               return ret;
+       }
+
+       tm->tm_sec = bcd2bin(regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] & 0x7F);
+       tm->tm_min = bcd2bin(regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] & 0x7F);
+
+       if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM) {
+               tm->tm_hour =
+                       bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & 0x1f);
+               if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM)
+                       tm->tm_hour += 12;
+       } else {
+               tm->tm_hour = bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC]);
+       }
+
+       tm->tm_mday = bcd2bin(regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC]);
+       tm->tm_wday = bcd2bin(regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC]);
+       tm->tm_mon  = bcd2bin(regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC]) - 1;
+       tm->tm_year = bcd2bin(regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC]) + 100;
+
+       return ret;
+}
+
+static int abeoz9_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       u8 regs[ABEOZ9_SEC_LEN];
+       int ret;
+
+       regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_sec);
+       regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_min);
+       regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_hour);
+       regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mday);
+       regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_wday);
+       regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mon + 1);
+       regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_year - 100);
+
+       ret = regmap_bulk_write(data->regmap, ABEOZ9_REG_SEC,
+                               regs,
+                               sizeof(regs));
+
+       if (ret) {
+               dev_err(dev, "set RTC time failed (%d)\n", ret);
+               return ret;
+       }
+
+       return abeoz9_reset_validity(regmap);
+}
+
+static int abeoz9_trickle_parse_dt(struct device_node *node)
+{
+       u32 ohms = 0;
+
+       if (of_property_read_u32(node, "trickle-resistor-ohms", &ohms))
+               return 0;
+
+       switch (ohms) {
+       case 1000:
+               return ABEOZ9_REG_EEPROM_R1K;
+       case 5000:
+               return ABEOZ9_REG_EEPROM_R5K;
+       case 20000:
+               return ABEOZ9_REG_EEPROM_R20K;
+       case 80000:
+               return ABEOZ9_REG_EEPROM_R80K;
+       default:
+               return 0;
+       }
+}
+
+static int abeoz9_rtc_setup(struct device *dev, struct device_node *node)
+{
+       struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       int ret;
+
+       /* Enable Self Recovery, Clock for Watch and EEPROM refresh functions */
+       ret = regmap_update_bits(regmap, ABEOZ9_REG_CTRL1,
+                                ABEOZ9_REG_CTRL1_MASK,
+                                ABEOZ9_REG_CTRL1_WE |
+                                ABEOZ9_REG_CTRL1_EERE |
+                                ABEOZ9_REG_CTRL1_SRON);
+       if (ret < 0) {
+               dev_err(dev, "unable to set CTRL_1 register (%d)\n", ret);
+               return ret;
+       }
+
+       ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT, 0);
+       if (ret < 0) {
+               dev_err(dev,
+                       "unable to set control CTRL_INT register (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT_FLAG, 0);
+       if (ret < 0) {
+               dev_err(dev,
+                       "unable to set control CTRL_INT_FLAG register (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       ret = abeoz9_trickle_parse_dt(node);
+
+       /* Enable built-in termometer */
+       ret |= ABEOZ9_REG_EEPROM_THE;
+
+       ret = regmap_update_bits(regmap, ABEOZ9_REG_EEPROM,
+                                ABEOZ9_REG_EEPROM_MASK,
+                                ret);
+       if (ret < 0) {
+               dev_err(dev, "unable to set EEPROM register (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static const struct rtc_class_ops rtc_ops = {
+       .read_time = abeoz9_rtc_get_time,
+       .set_time  = abeoz9_rtc_set_time,
+};
+
+static const struct regmap_config abeoz9_rtc_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+#if IS_REACHABLE(CONFIG_HWMON)
+
+static int abeoz9z3_temp_read(struct device *dev,
+                             enum hwmon_sensor_types type,
+                             u32 attr, int channel, long *temp)
+{
+       struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val);
+       if (ret < 0)
+               return ret;
+
+       if ((val & ABEOZ9_REG_CTRL_STATUS_V1F) ||
+           (val & ABEOZ9_REG_CTRL_STATUS_V2F)) {
+               dev_err(dev,
+                       "thermometer might be disabled due to low voltage\n");
+               return -EINVAL;
+       }
+
+       switch (attr) {
+       case hwmon_temp_input:
+               ret = regmap_read(regmap, ABEOZ9_REG_REG_TEMP, &val);
+               if (ret < 0)
+                       return ret;
+               *temp = 1000 * (val + ABEOZ953_TEMP_MIN);
+               return 0;
+       case hwmon_temp_max:
+               *temp = 1000 * ABEOZ953_TEMP_MAX;
+               return 0;
+       case hwmon_temp_min:
+               *temp = 1000 * ABEOZ953_TEMP_MIN;
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static umode_t abeoz9_is_visible(const void *data,
+                                enum hwmon_sensor_types type,
+                                u32 attr, int channel)
+{
+       switch (attr) {
+       case hwmon_temp_input:
+       case hwmon_temp_max:
+       case hwmon_temp_min:
+               return 0444;
+       default:
+               return 0;
+       }
+}
+
+static const u32 abeoz9_chip_config[] = {
+       HWMON_C_REGISTER_TZ,
+       0
+};
+
+static const struct hwmon_channel_info abeoz9_chip = {
+       .type = hwmon_chip,
+       .config = abeoz9_chip_config,
+};
+
+static const u32 abeoz9_temp_config[] = {
+       HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN,
+       0
+};
+
+static const struct hwmon_channel_info abeoz9_temp = {
+       .type = hwmon_temp,
+       .config = abeoz9_temp_config,
+};
+
+static const struct hwmon_channel_info *abeoz9_info[] = {
+       &abeoz9_chip,
+       &abeoz9_temp,
+       NULL
+};
+
+static const struct hwmon_ops abeoz9_hwmon_ops = {
+       .is_visible = abeoz9_is_visible,
+       .read = abeoz9z3_temp_read,
+};
+
+static const struct hwmon_chip_info abeoz9_chip_info = {
+       .ops = &abeoz9_hwmon_ops,
+       .info = abeoz9_info,
+};
+
+static void abeoz9_hwmon_register(struct device *dev,
+                                 struct abeoz9_rtc_data *data)
+{
+       data->hwmon_dev =
+               devm_hwmon_device_register_with_info(dev,
+                                                    "abeoz9",
+                                                    data,
+                                                    &abeoz9_chip_info,
+                                                    NULL);
+       if (IS_ERR(data->hwmon_dev)) {
+               dev_warn(dev, "unable to register hwmon device %ld\n",
+                        PTR_ERR(data->hwmon_dev));
+       }
+}
+
+#else
+
+static void abeoz9_hwmon_register(struct device *dev,
+                                 struct abeoz9_rtc_data *data)
+{
+}
+
+#endif
+
+static int abeoz9_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct abeoz9_rtc_data *data = NULL;
+       struct device *dev = &client->dev;
+       struct regmap *regmap;
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+                                    I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_I2C_BLOCK)) {
+               ret = -ENODEV;
+               goto err;
+       }
+
+       regmap = devm_regmap_init_i2c(client, &abeoz9_rtc_regmap_config);
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
+               dev_err(dev, "regmap allocation failed: %d\n", ret);
+               goto err;
+       }
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       data->regmap = regmap;
+       dev_set_drvdata(dev, data);
+
+       ret = abeoz9_rtc_setup(dev, client->dev.of_node);
+       if (ret)
+               goto err;
+
+       data->rtc = devm_rtc_allocate_device(dev);
+       ret = PTR_ERR_OR_ZERO(data->rtc);
+       if (ret)
+               goto err;
+
+       data->rtc->ops = &rtc_ops;
+       data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+       data->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+       ret = rtc_register_device(data->rtc);
+       if (ret)
+               goto err;
+
+       abeoz9_hwmon_register(dev, data);
+       return 0;
+
+err:
+       dev_err(dev, "unable to register RTC device (%d)\n", ret);
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id abeoz9_dt_match[] = {
+       { .compatible = "abracon,abeoz9" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, abeoz9_dt_match);
+#endif
+
+static const struct i2c_device_id abeoz9_id[] = {
+       { "abeoz9", 0 },
+       { }
+};
+
+static struct i2c_driver abeoz9_driver = {
+       .driver = {
+               .name = "rtc-ab-eoz9",
+               .of_match_table = of_match_ptr(abeoz9_dt_match),
+       },
+       .probe    = abeoz9_probe,
+       .id_table = abeoz9_id,
+};
+
+module_i2c_driver(abeoz9_driver);
+
+MODULE_AUTHOR("Artem Panfilov <panfilov.artyom@gmail.com>");
+MODULE_DESCRIPTION("Abracon AB-RTCMC-32.768kHz-EOZ9 RTC driver");
+MODULE_LICENSE("GPL");
index 4d24f72..6ddcad6 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2014-2015 Macq S.A.
  *
  * Author: Philippe De Muyter <phdm@macqel.be>
- * Author: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
  *
  * 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
@@ -46,6 +46,9 @@
 #define ABX8XX_CTRL_ARST       BIT(2)
 #define ABX8XX_CTRL_12_24      BIT(6)
 
+#define ABX8XX_REG_CTRL2       0x11
+#define ABX8XX_CTRL2_RSVD      BIT(5)
+
 #define ABX8XX_REG_IRQ         0x12
 #define ABX8XX_IRQ_AIE         BIT(2)
 #define ABX8XX_IRQ_IM_1_4      (0x3 << 5)
@@ -78,6 +81,9 @@
 
 #define ABX8XX_REG_ID0         0x28
 
+#define ABX8XX_REG_OUT_CTRL    0x30
+#define ABX8XX_OUT_CTRL_EXDS   BIT(4)
+
 #define ABX8XX_REG_TRICKLE     0x20
 #define ABX8XX_TRICKLE_CHARGE_ENABLE   0xa0
 #define ABX8XX_TRICKLE_STANDARD_DIODE  0x8
@@ -86,7 +92,7 @@
 static u8 trickle_resistors[] = {0, 3, 6, 11};
 
 enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
-       AB1801, AB1803, AB1804, AB1805, ABX80X};
+       AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X};
 
 struct abx80x_cap {
        u16 pn;
@@ -103,6 +109,7 @@ static struct abx80x_cap abx80x_caps[] = {
        [AB1803] = {.pn = 0x1803},
        [AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true},
        [AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
+       [RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
        [ABX80X] = {.pn = 0}
 };
 
@@ -723,6 +730,62 @@ static int abx80x_probe(struct i2c_client *client,
                return -EIO;
        }
 
+       /* Configure RV1805 specifics */
+       if (part == RV1805) {
+               /*
+                * Avoid accidentally entering test mode. This can happen
+                * on the RV1805 in case the reserved bit 5 in control2
+                * register is set. RV-1805-C3 datasheet indicates that
+                * the bit should be cleared in section 11h - Control2.
+                */
+               data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL2);
+               if (data < 0) {
+                       dev_err(&client->dev,
+                               "Unable to read control2 register\n");
+                       return -EIO;
+               }
+
+               err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL2,
+                                               data & ~ABX8XX_CTRL2_RSVD);
+               if (err < 0) {
+                       dev_err(&client->dev,
+                               "Unable to write control2 register\n");
+                       return -EIO;
+               }
+
+               /*
+                * Avoid extra power leakage. The RV1805 uses smaller
+                * 10pin package and the EXTI input is not present.
+                * Disable it to avoid leakage.
+                */
+               data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OUT_CTRL);
+               if (data < 0) {
+                       dev_err(&client->dev,
+                               "Unable to read output control register\n");
+                       return -EIO;
+               }
+
+               /*
+                * Write the configuration key register to enable access to
+                * the config2 register
+                */
+               err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY,
+                                               ABX8XX_CFG_KEY_MISC);
+               if (err < 0) {
+                       dev_err(&client->dev,
+                               "Unable to write configuration key\n");
+                       return -EIO;
+               }
+
+               err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OUT_CTRL,
+                                               data | ABX8XX_OUT_CTRL_EXDS);
+               if (err < 0) {
+                       dev_err(&client->dev,
+                               "Unable to write output control register\n");
+                       return -EIO;
+               }
+       }
+
        /* part autodetection */
        if (part == ABX80X) {
                for (i = 0; abx80x_caps[i].pn; i++)
@@ -826,7 +889,7 @@ static const struct i2c_device_id abx80x_id[] = {
        { "ab1803", AB1803 },
        { "ab1804", AB1804 },
        { "ab1805", AB1805 },
-       { "rv1805", AB1805 },
+       { "rv1805", RV1805 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, abx80x_id);
@@ -843,6 +906,6 @@ static struct i2c_driver abx80x_driver = {
 module_i2c_driver(abx80x_driver);
 
 MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
-MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
 MODULE_DESCRIPTION("Abracon ABX80X RTC driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-cadence.c b/drivers/rtc/rtc-cadence.c
new file mode 100644 (file)
index 0000000..3b7d643
--- /dev/null
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2019 Cadence
+ *
+ * Authors:
+ *  Jan Kotas <jank@cadence.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/rtc.h>
+#include <linux/clk.h>
+#include <linux/bcd.h>
+#include <linux/bitfield.h>
+#include <linux/interrupt.h>
+#include <linux/pm_wakeirq.h>
+
+/* Registers */
+#define CDNS_RTC_CTLR          0x00
+#define CDNS_RTC_HMR           0x04
+#define CDNS_RTC_TIMR          0x08
+#define CDNS_RTC_CALR          0x0C
+#define CDNS_RTC_TIMAR         0x10
+#define CDNS_RTC_CALAR         0x14
+#define CDNS_RTC_AENR          0x18
+#define CDNS_RTC_EFLR          0x1C
+#define CDNS_RTC_IENR          0x20
+#define CDNS_RTC_IDISR         0x24
+#define CDNS_RTC_IMSKR         0x28
+#define CDNS_RTC_STSR          0x2C
+#define CDNS_RTC_KRTCR         0x30
+
+/* Control */
+#define CDNS_RTC_CTLR_TIME     BIT(0)
+#define CDNS_RTC_CTLR_CAL      BIT(1)
+#define CDNS_RTC_CTLR_TIME_CAL (CDNS_RTC_CTLR_TIME | CDNS_RTC_CTLR_CAL)
+
+/* Status */
+#define CDNS_RTC_STSR_VT       BIT(0)
+#define CDNS_RTC_STSR_VC       BIT(1)
+#define CDNS_RTC_STSR_VTA      BIT(2)
+#define CDNS_RTC_STSR_VCA      BIT(3)
+#define CDNS_RTC_STSR_VT_VC    (CDNS_RTC_STSR_VT | CDNS_RTC_STSR_VC)
+#define CDNS_RTC_STSR_VTA_VCA  (CDNS_RTC_STSR_VTA | CDNS_RTC_STSR_VCA)
+
+/* Keep RTC */
+#define CDNS_RTC_KRTCR_KRTC    BIT(0)
+
+/* Alarm, Event, Interrupt */
+#define CDNS_RTC_AEI_HOS       BIT(0)
+#define CDNS_RTC_AEI_SEC       BIT(1)
+#define CDNS_RTC_AEI_MIN       BIT(2)
+#define CDNS_RTC_AEI_HOUR      BIT(3)
+#define CDNS_RTC_AEI_DATE      BIT(4)
+#define CDNS_RTC_AEI_MNTH      BIT(5)
+#define CDNS_RTC_AEI_ALRM      BIT(6)
+
+/* Time */
+#define CDNS_RTC_TIME_H                GENMASK(7, 0)
+#define CDNS_RTC_TIME_S                GENMASK(14, 8)
+#define CDNS_RTC_TIME_M                GENMASK(22, 16)
+#define CDNS_RTC_TIME_HR       GENMASK(29, 24)
+#define CDNS_RTC_TIME_PM       BIT(30)
+#define CDNS_RTC_TIME_CH       BIT(31)
+
+/* Calendar */
+#define CDNS_RTC_CAL_DAY       GENMASK(2, 0)
+#define CDNS_RTC_CAL_M         GENMASK(7, 3)
+#define CDNS_RTC_CAL_D         GENMASK(13, 8)
+#define CDNS_RTC_CAL_Y         GENMASK(23, 16)
+#define CDNS_RTC_CAL_C         GENMASK(29, 24)
+#define CDNS_RTC_CAL_CH                BIT(31)
+
+#define CDNS_RTC_MAX_REGS_TRIES        3
+
+struct cdns_rtc {
+       struct rtc_device *rtc_dev;
+       struct clk *pclk;
+       struct clk *ref_clk;
+       void __iomem *regs;
+       int irq;
+};
+
+static void cdns_rtc_set_enabled(struct cdns_rtc *crtc, bool enabled)
+{
+       u32 reg = enabled ? 0x0 : CDNS_RTC_CTLR_TIME_CAL;
+
+       writel(reg, crtc->regs + CDNS_RTC_CTLR);
+}
+
+static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc)
+{
+       return !(readl(crtc->regs + CDNS_RTC_CTLR) & CDNS_RTC_CTLR_TIME_CAL);
+}
+
+static irqreturn_t cdns_rtc_irq_handler(int irq, void *id)
+{
+       struct device *dev = id;
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+       /* Reading the register clears it */
+       if (!(readl(crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM))
+               return IRQ_NONE;
+
+       rtc_update_irq(crtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+       return IRQ_HANDLED;
+}
+
+static u32 cdns_rtc_time2reg(struct rtc_time *tm)
+{
+       return FIELD_PREP(CDNS_RTC_TIME_S,  bin2bcd(tm->tm_sec))
+            | FIELD_PREP(CDNS_RTC_TIME_M,  bin2bcd(tm->tm_min))
+            | FIELD_PREP(CDNS_RTC_TIME_HR, bin2bcd(tm->tm_hour));
+}
+
+static void cdns_rtc_reg2time(u32 reg, struct rtc_time *tm)
+{
+       tm->tm_sec  = bcd2bin(FIELD_GET(CDNS_RTC_TIME_S, reg));
+       tm->tm_min  = bcd2bin(FIELD_GET(CDNS_RTC_TIME_M, reg));
+       tm->tm_hour = bcd2bin(FIELD_GET(CDNS_RTC_TIME_HR, reg));
+}
+
+static int cdns_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+       u32 reg;
+
+       /* If the RTC is disabled, assume the values are invalid */
+       if (!cdns_rtc_get_enabled(crtc))
+               return -EINVAL;
+
+       cdns_rtc_set_enabled(crtc, false);
+
+       reg = readl(crtc->regs + CDNS_RTC_TIMR);
+       cdns_rtc_reg2time(reg, tm);
+
+       reg = readl(crtc->regs + CDNS_RTC_CALR);
+       tm->tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg));
+       tm->tm_mon  = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1;
+       tm->tm_year = bcd2bin(FIELD_GET(CDNS_RTC_CAL_Y, reg))
+                   + bcd2bin(FIELD_GET(CDNS_RTC_CAL_C, reg)) * 100 - 1900;
+       tm->tm_wday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_DAY, reg)) - 1;
+
+       cdns_rtc_set_enabled(crtc, true);
+       return 0;
+}
+
+static int cdns_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+       u32 timr, calr, stsr;
+       int ret = -EIO;
+       int year = tm->tm_year + 1900;
+       int tries;
+
+       cdns_rtc_set_enabled(crtc, false);
+
+       timr = cdns_rtc_time2reg(tm);
+
+       calr = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(tm->tm_mday))
+            | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(tm->tm_mon + 1))
+            | FIELD_PREP(CDNS_RTC_CAL_Y, bin2bcd(year % 100))
+            | FIELD_PREP(CDNS_RTC_CAL_C, bin2bcd(year / 100))
+            | FIELD_PREP(CDNS_RTC_CAL_DAY, tm->tm_wday + 1);
+
+       /* Update registers, check valid flags */
+       for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) {
+               writel(timr, crtc->regs + CDNS_RTC_TIMR);
+               writel(calr, crtc->regs + CDNS_RTC_CALR);
+               stsr = readl(crtc->regs + CDNS_RTC_STSR);
+
+               if ((stsr & CDNS_RTC_STSR_VT_VC) == CDNS_RTC_STSR_VT_VC) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+       cdns_rtc_set_enabled(crtc, true);
+       return ret;
+}
+
+static int cdns_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+       if (enabled) {
+               writel((CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR
+                       | CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH),
+                      crtc->regs + CDNS_RTC_AENR);
+               writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IENR);
+       } else {
+               writel(0, crtc->regs + CDNS_RTC_AENR);
+               writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IDISR);
+       }
+
+       return 0;
+}
+
+static int cdns_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+       u32 reg;
+
+       reg = readl(crtc->regs + CDNS_RTC_TIMAR);
+       cdns_rtc_reg2time(reg, &alarm->time);
+
+       reg = readl(crtc->regs + CDNS_RTC_CALAR);
+       alarm->time.tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg));
+       alarm->time.tm_mon  = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1;
+
+       return 0;
+}
+
+static int cdns_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+       int ret = -EIO;
+       int tries;
+       u32 timar, calar, stsr;
+
+       cdns_rtc_alarm_irq_enable(dev, 0);
+
+       timar = cdns_rtc_time2reg(&alarm->time);
+       calar = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(alarm->time.tm_mday))
+             | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(alarm->time.tm_mon + 1));
+
+       /* Update registers, check valid alarm flags */
+       for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) {
+               writel(timar, crtc->regs + CDNS_RTC_TIMAR);
+               writel(calar, crtc->regs + CDNS_RTC_CALAR);
+               stsr = readl(crtc->regs + CDNS_RTC_STSR);
+
+               if ((stsr & CDNS_RTC_STSR_VTA_VCA) == CDNS_RTC_STSR_VTA_VCA) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+       if (!ret)
+               cdns_rtc_alarm_irq_enable(dev, alarm->enabled);
+       return ret;
+}
+
+static const struct rtc_class_ops cdns_rtc_ops = {
+       .read_time      = cdns_rtc_read_time,
+       .set_time       = cdns_rtc_set_time,
+       .read_alarm     = cdns_rtc_read_alarm,
+       .set_alarm      = cdns_rtc_set_alarm,
+       .alarm_irq_enable = cdns_rtc_alarm_irq_enable,
+};
+
+static int cdns_rtc_probe(struct platform_device *pdev)
+{
+       struct cdns_rtc *crtc;
+       struct resource *res;
+       int ret;
+       unsigned long ref_clk_freq;
+
+       crtc = devm_kzalloc(&pdev->dev, sizeof(*crtc), GFP_KERNEL);
+       if (!crtc)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       crtc->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(crtc->regs))
+               return PTR_ERR(crtc->regs);
+
+       crtc->irq = platform_get_irq(pdev, 0);
+       if (crtc->irq < 0)
+               return -EINVAL;
+
+       crtc->pclk = devm_clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(crtc->pclk)) {
+               ret = PTR_ERR(crtc->pclk);
+               dev_err(&pdev->dev,
+                       "Failed to retrieve the peripheral clock, %d\n", ret);
+               return ret;
+       }
+
+       crtc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
+       if (IS_ERR(crtc->ref_clk)) {
+               ret = PTR_ERR(crtc->ref_clk);
+               dev_err(&pdev->dev,
+                       "Failed to retrieve the reference clock, %d\n", ret);
+               return ret;
+       }
+
+       crtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+       if (IS_ERR(crtc->rtc_dev)) {
+               ret = PTR_ERR(crtc->rtc_dev);
+               dev_err(&pdev->dev,
+                       "Failed to allocate the RTC device, %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, crtc);
+
+       ret = clk_prepare_enable(crtc->pclk);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Failed to enable the peripheral clock, %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(crtc->ref_clk);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Failed to enable the reference clock, %d\n", ret);
+               goto err_disable_pclk;
+       }
+
+       ref_clk_freq = clk_get_rate(crtc->ref_clk);
+       if ((ref_clk_freq != 1) && (ref_clk_freq != 100)) {
+               dev_err(&pdev->dev,
+                       "Invalid reference clock frequency %lu Hz.\n",
+                       ref_clk_freq);
+               ret = -EINVAL;
+               goto err_disable_ref_clk;
+       }
+
+       ret = devm_request_irq(&pdev->dev, crtc->irq,
+                              cdns_rtc_irq_handler, 0,
+                              dev_name(&pdev->dev), &pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Failed to request interrupt for the device, %d\n",
+                       ret);
+               goto err_disable_ref_clk;
+       }
+
+       /* The RTC supports 01.01.1900 - 31.12.2999 */
+       crtc->rtc_dev->range_min = mktime64(1900,  1,  1,  0,  0,  0);
+       crtc->rtc_dev->range_max = mktime64(2999, 12, 31, 23, 59, 59);
+
+       crtc->rtc_dev->ops = &cdns_rtc_ops;
+       device_init_wakeup(&pdev->dev, true);
+
+       /* Always use 24-hour mode and keep the RTC values */
+       writel(0, crtc->regs + CDNS_RTC_HMR);
+       writel(CDNS_RTC_KRTCR_KRTC, crtc->regs + CDNS_RTC_KRTCR);
+
+       ret = rtc_register_device(crtc->rtc_dev);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Failed to register the RTC device, %d\n", ret);
+               goto err_disable_wakeup;
+       }
+
+       return 0;
+
+err_disable_wakeup:
+       device_init_wakeup(&pdev->dev, false);
+
+err_disable_ref_clk:
+       clk_disable_unprepare(crtc->ref_clk);
+
+err_disable_pclk:
+       clk_disable_unprepare(crtc->pclk);
+
+       return ret;
+}
+
+static int cdns_rtc_remove(struct platform_device *pdev)
+{
+       struct cdns_rtc *crtc = platform_get_drvdata(pdev);
+
+       cdns_rtc_alarm_irq_enable(&pdev->dev, 0);
+       device_init_wakeup(&pdev->dev, 0);
+
+       clk_disable_unprepare(crtc->pclk);
+       clk_disable_unprepare(crtc->ref_clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cdns_rtc_suspend(struct device *dev)
+{
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(crtc->irq);
+
+       return 0;
+}
+
+static int cdns_rtc_resume(struct device *dev)
+{
+       struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(crtc->irq);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume);
+
+static const struct of_device_id cdns_rtc_of_match[] = {
+       { .compatible = "cdns,rtc-r109v3" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, cdns_rtc_of_match);
+
+static struct platform_driver cdns_rtc_driver = {
+       .driver = {
+               .name = "cdns-rtc",
+               .of_match_table = cdns_rtc_of_match,
+               .pm = &cdns_rtc_pm_ops,
+       },
+       .probe = cdns_rtc_probe,
+       .remove = cdns_rtc_remove,
+};
+module_platform_driver(cdns_rtc_driver);
+
+MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
+MODULE_DESCRIPTION("Cadence RTC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cdns-rtc");
index fc5cf5c..0b232c8 100644 (file)
@@ -235,9 +235,13 @@ static int coh901331_suspend(struct device *dev)
 
 static int coh901331_resume(struct device *dev)
 {
+       int ret;
        struct coh901331_port *rtap = dev_get_drvdata(dev);
 
-       clk_prepare(rtap->clk);
+       ret = clk_prepare(rtap->clk);
+       if (ret)
+               return ret;
+
        if (device_may_wakeup(dev)) {
                disable_irq_wake(rtap->irq);
        } else {
index 74b31dc..07530fe 100644 (file)
@@ -114,6 +114,33 @@ enum ds_type {
 #      define RX8025_BIT_VDET          0x40
 #      define RX8025_BIT_XST           0x20
 
+#define RX8130_REG_ALARM_MIN           0x17
+#define RX8130_REG_ALARM_HOUR          0x18
+#define RX8130_REG_ALARM_WEEK_OR_DAY   0x19
+#define RX8130_REG_EXTENSION           0x1c
+#define RX8130_REG_EXTENSION_WADA      BIT(3)
+#define RX8130_REG_FLAG                        0x1d
+#define RX8130_REG_FLAG_VLF            BIT(1)
+#define RX8130_REG_FLAG_AF             BIT(3)
+#define RX8130_REG_CONTROL0            0x1e
+#define RX8130_REG_CONTROL0_AIE                BIT(3)
+
+#define MCP794XX_REG_CONTROL           0x07
+#      define MCP794XX_BIT_ALM0_EN     0x10
+#      define MCP794XX_BIT_ALM1_EN     0x20
+#define MCP794XX_REG_ALARM0_BASE       0x0a
+#define MCP794XX_REG_ALARM0_CTRL       0x0d
+#define MCP794XX_REG_ALARM1_BASE       0x11
+#define MCP794XX_REG_ALARM1_CTRL       0x14
+#      define MCP794XX_BIT_ALMX_IF     BIT(3)
+#      define MCP794XX_BIT_ALMX_C0     BIT(4)
+#      define MCP794XX_BIT_ALMX_C1     BIT(5)
+#      define MCP794XX_BIT_ALMX_C2     BIT(6)
+#      define MCP794XX_BIT_ALMX_POL    BIT(7)
+#      define MCP794XX_MSK_ALMX_MATCH  (MCP794XX_BIT_ALMX_C0 | \
+                                        MCP794XX_BIT_ALMX_C1 | \
+                                        MCP794XX_BIT_ALMX_C2)
+
 #define M41TXX_REG_CONTROL     0x07
 #      define M41TXX_BIT_OUT           BIT(7)
 #      define M41TXX_BIT_FT            BIT(6)
@@ -158,313 +185,45 @@ struct chip_desc {
                                                    bool);
 };
 
-static int ds1307_get_time(struct device *dev, struct rtc_time *t);
-static int ds1307_set_time(struct device *dev, struct rtc_time *t);
-static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled);
-static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode);
-static irqreturn_t rx8130_irq(int irq, void *dev_id);
-static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled);
-static irqreturn_t mcp794xx_irq(int irq, void *dev_id);
-static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled);
-static int m41txx_rtc_read_offset(struct device *dev, long *offset);
-static int m41txx_rtc_set_offset(struct device *dev, long offset);
+static const struct chip_desc chips[last_ds_type];
 
-static const struct rtc_class_ops rx8130_rtc_ops = {
-       .read_time      = ds1307_get_time,
-       .set_time       = ds1307_set_time,
-       .read_alarm     = rx8130_read_alarm,
-       .set_alarm      = rx8130_set_alarm,
-       .alarm_irq_enable = rx8130_alarm_irq_enable,
-};
+static int ds1307_get_time(struct device *dev, struct rtc_time *t)
+{
+       struct ds1307   *ds1307 = dev_get_drvdata(dev);
+       int             tmp, ret;
+       const struct chip_desc *chip = &chips[ds1307->type];
+       u8 regs[7];
 
-static const struct rtc_class_ops mcp794xx_rtc_ops = {
-       .read_time      = ds1307_get_time,
-       .set_time       = ds1307_set_time,
-       .read_alarm     = mcp794xx_read_alarm,
-       .set_alarm      = mcp794xx_set_alarm,
-       .alarm_irq_enable = mcp794xx_alarm_irq_enable,
-};
+       if (ds1307->type == rx_8130) {
+               unsigned int regflag;
+               ret = regmap_read(ds1307->regmap, RX8130_REG_FLAG, &regflag);
+               if (ret) {
+                       dev_err(dev, "%s error %d\n", "read", ret);
+                       return ret;
+               }
 
-static const struct rtc_class_ops m41txx_rtc_ops = {
-       .read_time      = ds1307_get_time,
-       .set_time       = ds1307_set_time,
-       .read_alarm     = ds1337_read_alarm,
-       .set_alarm      = ds1337_set_alarm,
-       .alarm_irq_enable = ds1307_alarm_irq_enable,
-       .read_offset    = m41txx_rtc_read_offset,
-       .set_offset     = m41txx_rtc_set_offset,
-};
+               if (regflag & RX8130_REG_FLAG_VLF) {
+                       dev_warn_once(dev, "oscillator failed, set time!\n");
+                       return -EINVAL;
+               }
+       }
 
-static const struct chip_desc chips[last_ds_type] = {
-       [ds_1307] = {
-               .nvram_offset   = 8,
-               .nvram_size     = 56,
-       },
-       [ds_1308] = {
-               .nvram_offset   = 8,
-               .nvram_size     = 56,
-       },
-       [ds_1337] = {
-               .alarm          = 1,
-               .century_reg    = DS1307_REG_MONTH,
-               .century_bit    = DS1337_BIT_CENTURY,
-       },
-       [ds_1338] = {
-               .nvram_offset   = 8,
-               .nvram_size     = 56,
-       },
-       [ds_1339] = {
-               .alarm          = 1,
-               .century_reg    = DS1307_REG_MONTH,
-               .century_bit    = DS1337_BIT_CENTURY,
-               .bbsqi_bit      = DS1339_BIT_BBSQI,
-               .trickle_charger_reg = 0x10,
-               .do_trickle_setup = &do_trickle_setup_ds1339,
-       },
-       [ds_1340] = {
-               .century_reg    = DS1307_REG_HOUR,
-               .century_enable_bit = DS1340_BIT_CENTURY_EN,
-               .century_bit    = DS1340_BIT_CENTURY,
-               .do_trickle_setup = &do_trickle_setup_ds1339,
-               .trickle_charger_reg = 0x08,
-       },
-       [ds_1341] = {
-               .century_reg    = DS1307_REG_MONTH,
-               .century_bit    = DS1337_BIT_CENTURY,
-       },
-       [ds_1388] = {
-               .offset         = 1,
-               .trickle_charger_reg = 0x0a,
-       },
-       [ds_3231] = {
-               .alarm          = 1,
-               .century_reg    = DS1307_REG_MONTH,
-               .century_bit    = DS1337_BIT_CENTURY,
-               .bbsqi_bit      = DS3231_BIT_BBSQW,
-       },
-       [rx_8130] = {
-               .alarm          = 1,
-               /* this is battery backed SRAM */
-               .nvram_offset   = 0x20,
-               .nvram_size     = 4,    /* 32bit (4 word x 8 bit) */
-               .offset         = 0x10,
-               .irq_handler = rx8130_irq,
-               .rtc_ops = &rx8130_rtc_ops,
-       },
-       [m41t0] = {
-               .rtc_ops        = &m41txx_rtc_ops,
-       },
-       [m41t00] = {
-               .rtc_ops        = &m41txx_rtc_ops,
-       },
-       [m41t11] = {
-               /* this is battery backed SRAM */
-               .nvram_offset   = 8,
-               .nvram_size     = 56,
-               .rtc_ops        = &m41txx_rtc_ops,
-       },
-       [mcp794xx] = {
-               .alarm          = 1,
-               /* this is battery backed SRAM */
-               .nvram_offset   = 0x20,
-               .nvram_size     = 0x40,
-               .irq_handler = mcp794xx_irq,
-               .rtc_ops = &mcp794xx_rtc_ops,
-       },
-};
+       /* read the RTC date and time registers all at once */
+       ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs,
+                              sizeof(regs));
+       if (ret) {
+               dev_err(dev, "%s error %d\n", "read", ret);
+               return ret;
+       }
 
-static const struct i2c_device_id ds1307_id[] = {
-       { "ds1307", ds_1307 },
-       { "ds1308", ds_1308 },
-       { "ds1337", ds_1337 },
-       { "ds1338", ds_1338 },
-       { "ds1339", ds_1339 },
-       { "ds1388", ds_1388 },
-       { "ds1340", ds_1340 },
-       { "ds1341", ds_1341 },
-       { "ds3231", ds_3231 },
-       { "m41t0", m41t0 },
-       { "m41t00", m41t00 },
-       { "m41t11", m41t11 },
-       { "mcp7940x", mcp794xx },
-       { "mcp7941x", mcp794xx },
-       { "pt7c4338", ds_1307 },
-       { "rx8025", rx_8025 },
-       { "isl12057", ds_1337 },
-       { "rx8130", rx_8130 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, ds1307_id);
+       dev_dbg(dev, "%s: %7ph\n", "read", regs);
 
-#ifdef CONFIG_OF
-static const struct of_device_id ds1307_of_match[] = {
-       {
-               .compatible = "dallas,ds1307",
-               .data = (void *)ds_1307
-       },
-       {
-               .compatible = "dallas,ds1308",
-               .data = (void *)ds_1308
-       },
-       {
-               .compatible = "dallas,ds1337",
-               .data = (void *)ds_1337
-       },
-       {
-               .compatible = "dallas,ds1338",
-               .data = (void *)ds_1338
-       },
-       {
-               .compatible = "dallas,ds1339",
-               .data = (void *)ds_1339
-       },
-       {
-               .compatible = "dallas,ds1388",
-               .data = (void *)ds_1388
-       },
-       {
-               .compatible = "dallas,ds1340",
-               .data = (void *)ds_1340
-       },
-       {
-               .compatible = "dallas,ds1341",
-               .data = (void *)ds_1341
-       },
-       {
-               .compatible = "maxim,ds3231",
-               .data = (void *)ds_3231
-       },
-       {
-               .compatible = "st,m41t0",
-               .data = (void *)m41t0
-       },
-       {
-               .compatible = "st,m41t00",
-               .data = (void *)m41t00
-       },
-       {
-               .compatible = "st,m41t11",
-               .data = (void *)m41t11
-       },
-       {
-               .compatible = "microchip,mcp7940x",
-               .data = (void *)mcp794xx
-       },
-       {
-               .compatible = "microchip,mcp7941x",
-               .data = (void *)mcp794xx
-       },
-       {
-               .compatible = "pericom,pt7c4338",
-               .data = (void *)ds_1307
-       },
-       {
-               .compatible = "epson,rx8025",
-               .data = (void *)rx_8025
-       },
-       {
-               .compatible = "isil,isl12057",
-               .data = (void *)ds_1337
-       },
-       {
-               .compatible = "epson,rx8130",
-               .data = (void *)rx_8130
-       },
-       { }
-};
-MODULE_DEVICE_TABLE(of, ds1307_of_match);
-#endif
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id ds1307_acpi_ids[] = {
-       { .id = "DS1307", .driver_data = ds_1307 },
-       { .id = "DS1308", .driver_data = ds_1308 },
-       { .id = "DS1337", .driver_data = ds_1337 },
-       { .id = "DS1338", .driver_data = ds_1338 },
-       { .id = "DS1339", .driver_data = ds_1339 },
-       { .id = "DS1388", .driver_data = ds_1388 },
-       { .id = "DS1340", .driver_data = ds_1340 },
-       { .id = "DS1341", .driver_data = ds_1341 },
-       { .id = "DS3231", .driver_data = ds_3231 },
-       { .id = "M41T0", .driver_data = m41t0 },
-       { .id = "M41T00", .driver_data = m41t00 },
-       { .id = "M41T11", .driver_data = m41t11 },
-       { .id = "MCP7940X", .driver_data = mcp794xx },
-       { .id = "MCP7941X", .driver_data = mcp794xx },
-       { .id = "PT7C4338", .driver_data = ds_1307 },
-       { .id = "RX8025", .driver_data = rx_8025 },
-       { .id = "ISL12057", .driver_data = ds_1337 },
-       { .id = "RX8130", .driver_data = rx_8130 },
-       { }
-};
-MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
-#endif
-
-/*
- * The ds1337 and ds1339 both have two alarms, but we only use the first
- * one (with a "seconds" field).  For ds1337 we expect nINTA is our alarm
- * signal; ds1339 chips have only one alarm signal.
- */
-static irqreturn_t ds1307_irq(int irq, void *dev_id)
-{
-       struct ds1307           *ds1307 = dev_id;
-       struct mutex            *lock = &ds1307->rtc->ops_lock;
-       int                     stat, ret;
-
-       mutex_lock(lock);
-       ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat);
-       if (ret)
-               goto out;
-
-       if (stat & DS1337_BIT_A1I) {
-               stat &= ~DS1337_BIT_A1I;
-               regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat);
-
-               ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
-                                        DS1337_BIT_A1IE, 0);
-               if (ret)
-                       goto out;
-
-               rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
-       }
-
-out:
-       mutex_unlock(lock);
-
-       return IRQ_HANDLED;
-}
-
-/*----------------------------------------------------------------------*/
-
-static int ds1307_get_time(struct device *dev, struct rtc_time *t)
-{
-       struct ds1307   *ds1307 = dev_get_drvdata(dev);
-       int             tmp, ret;
-       const struct chip_desc *chip = &chips[ds1307->type];
-       u8 regs[7];
-
-       /* read the RTC date and time registers all at once */
-       ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs,
-                              sizeof(regs));
-       if (ret) {
-               dev_err(dev, "%s error %d\n", "read", ret);
-               return ret;
-       }
-
-       dev_dbg(dev, "%s: %7ph\n", "read", regs);
-
-       /* if oscillator fail bit is set, no data can be trusted */
-       if (ds1307->type == m41t0 &&
-           regs[DS1307_REG_MIN] & M41T0_BIT_OF) {
-               dev_warn_once(dev, "oscillator failed, set time!\n");
-               return -EINVAL;
-       }
+       /* if oscillator fail bit is set, no data can be trusted */
+       if (ds1307->type == m41t0 &&
+           regs[DS1307_REG_MIN] & M41T0_BIT_OF) {
+               dev_warn_once(dev, "oscillator failed, set time!\n");
+               return -EINVAL;
+       }
 
        t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f);
        t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f);
@@ -548,6 +307,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
                dev_err(dev, "%s error %d\n", "write", result);
                return result;
        }
+
+       if (ds1307->type == rx_8130) {
+               /* clear Voltage Loss Flag as data is available now */
+               result = regmap_write(ds1307->regmap, RX8130_REG_FLAG,
+                                     ~(u8)RX8130_REG_FLAG_VLF);
+               if (result) {
+                       dev_err(dev, "%s error %d\n", "write", result);
+                       return result;
+               }
+       }
+
        return 0;
 }
 
@@ -666,29 +436,28 @@ static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled)
                                  enabled ? DS1337_BIT_A1IE : 0);
 }
 
-static const struct rtc_class_ops ds13xx_rtc_ops = {
-       .read_time      = ds1307_get_time,
-       .set_time       = ds1307_set_time,
-       .read_alarm     = ds1337_read_alarm,
-       .set_alarm      = ds1337_set_alarm,
-       .alarm_irq_enable = ds1307_alarm_irq_enable,
-};
-
-/*----------------------------------------------------------------------*/
-
-/*
- * Alarm support for rx8130 devices.
- */
+static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode)
+{
+       u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
+               DS1307_TRICKLE_CHARGER_NO_DIODE;
 
-#define RX8130_REG_ALARM_MIN           0x07
-#define RX8130_REG_ALARM_HOUR          0x08
-#define RX8130_REG_ALARM_WEEK_OR_DAY   0x09
-#define RX8130_REG_EXTENSION           0x0c
-#define RX8130_REG_EXTENSION_WADA      BIT(3)
-#define RX8130_REG_FLAG                        0x0d
-#define RX8130_REG_FLAG_AF             BIT(3)
-#define RX8130_REG_CONTROL0            0x0e
-#define RX8130_REG_CONTROL0_AIE                BIT(3)
+       switch (ohms) {
+       case 250:
+               setup |= DS1307_TRICKLE_CHARGER_250_OHM;
+               break;
+       case 2000:
+               setup |= DS1307_TRICKLE_CHARGER_2K_OHM;
+               break;
+       case 4000:
+               setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
+               break;
+       default:
+               dev_warn(ds1307->dev,
+                        "Unsupported ohm value %u in dt\n", ohms);
+               return 0;
+       }
+       return setup;
+}
 
 static irqreturn_t rx8130_irq(int irq, void *dev_id)
 {
@@ -785,8 +554,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
        if (ret < 0)
                return ret;
 
-       ctl[0] &= ~RX8130_REG_EXTENSION_WADA;
-       ctl[1] |= RX8130_REG_FLAG_AF;
+       ctl[0] &= RX8130_REG_EXTENSION_WADA;
+       ctl[1] &= ~RX8130_REG_FLAG_AF;
        ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
 
        ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
@@ -809,8 +578,7 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 
        ctl[2] |= RX8130_REG_CONTROL0_AIE;
 
-       return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
-                                sizeof(ctl));
+       return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]);
 }
 
 static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
@@ -833,54 +601,459 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg);
 }
 
-/*----------------------------------------------------------------------*/
+static irqreturn_t mcp794xx_irq(int irq, void *dev_id)
+{
+       struct ds1307           *ds1307 = dev_id;
+       struct mutex            *lock = &ds1307->rtc->ops_lock;
+       int reg, ret;
+
+       mutex_lock(lock);
+
+       /* Check and clear alarm 0 interrupt flag. */
+       ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, &reg);
+       if (ret)
+               goto out;
+       if (!(reg & MCP794XX_BIT_ALMX_IF))
+               goto out;
+       reg &= ~MCP794XX_BIT_ALMX_IF;
+       ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg);
+       if (ret)
+               goto out;
+
+       /* Disable alarm 0. */
+       ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
+                                MCP794XX_BIT_ALM0_EN, 0);
+       if (ret)
+               goto out;
+
+       rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+
+out:
+       mutex_unlock(lock);
+
+       return IRQ_HANDLED;
+}
+
+static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct ds1307 *ds1307 = dev_get_drvdata(dev);
+       u8 regs[10];
+       int ret;
+
+       if (!test_bit(HAS_ALARM, &ds1307->flags))
+               return -EINVAL;
+
+       /* Read control and alarm 0 registers. */
+       ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
+                              sizeof(regs));
+       if (ret)
+               return ret;
+
+       t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN);
+
+       /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
+       t->time.tm_sec = bcd2bin(regs[3] & 0x7f);
+       t->time.tm_min = bcd2bin(regs[4] & 0x7f);
+       t->time.tm_hour = bcd2bin(regs[5] & 0x3f);
+       t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1;
+       t->time.tm_mday = bcd2bin(regs[7] & 0x3f);
+       t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1;
+       t->time.tm_year = -1;
+       t->time.tm_yday = -1;
+       t->time.tm_isdst = -1;
+
+       dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+               "enabled=%d polarity=%d irq=%d match=%lu\n", __func__,
+               t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+               t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled,
+               !!(regs[6] & MCP794XX_BIT_ALMX_POL),
+               !!(regs[6] & MCP794XX_BIT_ALMX_IF),
+               (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4);
+
+       return 0;
+}
+
+/*
+ * We may have a random RTC weekday, therefore calculate alarm weekday based
+ * on current weekday we read from the RTC timekeeping regs
+ */
+static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm)
+{
+       struct rtc_time tm_now;
+       int days_now, days_alarm, ret;
+
+       ret = ds1307_get_time(dev, &tm_now);
+       if (ret)
+               return ret;
+
+       days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60);
+       days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60);
+
+       return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1;
+}
+
+static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct ds1307 *ds1307 = dev_get_drvdata(dev);
+       unsigned char regs[10];
+       int wday, ret;
+
+       if (!test_bit(HAS_ALARM, &ds1307->flags))
+               return -EINVAL;
+
+       wday = mcp794xx_alm_weekday(dev, &t->time);
+       if (wday < 0)
+               return wday;
+
+       dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+               "enabled=%d pending=%d\n", __func__,
+               t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+               t->time.tm_wday, t->time.tm_mday, t->time.tm_mon,
+               t->enabled, t->pending);
+
+       /* Read control and alarm 0 registers. */
+       ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
+                              sizeof(regs));
+       if (ret)
+               return ret;
+
+       /* Set alarm 0, using 24-hour and day-of-month modes. */
+       regs[3] = bin2bcd(t->time.tm_sec);
+       regs[4] = bin2bcd(t->time.tm_min);
+       regs[5] = bin2bcd(t->time.tm_hour);
+       regs[6] = wday;
+       regs[7] = bin2bcd(t->time.tm_mday);
+       regs[8] = bin2bcd(t->time.tm_mon + 1);
+
+       /* Clear the alarm 0 interrupt flag. */
+       regs[6] &= ~MCP794XX_BIT_ALMX_IF;
+       /* Set alarm match: second, minute, hour, day, date, month. */
+       regs[6] |= MCP794XX_MSK_ALMX_MATCH;
+       /* Disable interrupt. We will not enable until completely programmed */
+       regs[0] &= ~MCP794XX_BIT_ALM0_EN;
+
+       ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
+                               sizeof(regs));
+       if (ret)
+               return ret;
+
+       if (!t->enabled)
+               return 0;
+       regs[0] |= MCP794XX_BIT_ALM0_EN;
+       return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]);
+}
+
+static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct ds1307 *ds1307 = dev_get_drvdata(dev);
+
+       if (!test_bit(HAS_ALARM, &ds1307->flags))
+               return -EINVAL;
+
+       return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
+                                 MCP794XX_BIT_ALM0_EN,
+                                 enabled ? MCP794XX_BIT_ALM0_EN : 0);
+}
+
+static int m41txx_rtc_read_offset(struct device *dev, long *offset)
+{
+       struct ds1307 *ds1307 = dev_get_drvdata(dev);
+       unsigned int ctrl_reg;
+       u8 val;
+
+       regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg);
+
+       val = ctrl_reg & M41TXX_M_CALIBRATION;
+
+       /* check if positive */
+       if (ctrl_reg & M41TXX_BIT_CALIB_SIGN)
+               *offset = (val * M41TXX_POS_OFFSET_STEP_PPB);
+       else
+               *offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB);
+
+       return 0;
+}
+
+static int m41txx_rtc_set_offset(struct device *dev, long offset)
+{
+       struct ds1307 *ds1307 = dev_get_drvdata(dev);
+       unsigned int ctrl_reg;
+
+       if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET))
+               return -ERANGE;
+
+       if (offset >= 0) {
+               ctrl_reg = DIV_ROUND_CLOSEST(offset,
+                                            M41TXX_POS_OFFSET_STEP_PPB);
+               ctrl_reg |= M41TXX_BIT_CALIB_SIGN;
+       } else {
+               ctrl_reg = DIV_ROUND_CLOSEST(abs(offset),
+                                            M41TXX_NEG_OFFSET_STEP_PPB);
+       }
+
+       return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL,
+                                 M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN,
+                                 ctrl_reg);
+}
+
+static const struct rtc_class_ops rx8130_rtc_ops = {
+       .read_time      = ds1307_get_time,
+       .set_time       = ds1307_set_time,
+       .read_alarm     = rx8130_read_alarm,
+       .set_alarm      = rx8130_set_alarm,
+       .alarm_irq_enable = rx8130_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops mcp794xx_rtc_ops = {
+       .read_time      = ds1307_get_time,
+       .set_time       = ds1307_set_time,
+       .read_alarm     = mcp794xx_read_alarm,
+       .set_alarm      = mcp794xx_set_alarm,
+       .alarm_irq_enable = mcp794xx_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops m41txx_rtc_ops = {
+       .read_time      = ds1307_get_time,
+       .set_time       = ds1307_set_time,
+       .read_alarm     = ds1337_read_alarm,
+       .set_alarm      = ds1337_set_alarm,
+       .alarm_irq_enable = ds1307_alarm_irq_enable,
+       .read_offset    = m41txx_rtc_read_offset,
+       .set_offset     = m41txx_rtc_set_offset,
+};
+
+static const struct chip_desc chips[last_ds_type] = {
+       [ds_1307] = {
+               .nvram_offset   = 8,
+               .nvram_size     = 56,
+       },
+       [ds_1308] = {
+               .nvram_offset   = 8,
+               .nvram_size     = 56,
+       },
+       [ds_1337] = {
+               .alarm          = 1,
+               .century_reg    = DS1307_REG_MONTH,
+               .century_bit    = DS1337_BIT_CENTURY,
+       },
+       [ds_1338] = {
+               .nvram_offset   = 8,
+               .nvram_size     = 56,
+       },
+       [ds_1339] = {
+               .alarm          = 1,
+               .century_reg    = DS1307_REG_MONTH,
+               .century_bit    = DS1337_BIT_CENTURY,
+               .bbsqi_bit      = DS1339_BIT_BBSQI,
+               .trickle_charger_reg = 0x10,
+               .do_trickle_setup = &do_trickle_setup_ds1339,
+       },
+       [ds_1340] = {
+               .century_reg    = DS1307_REG_HOUR,
+               .century_enable_bit = DS1340_BIT_CENTURY_EN,
+               .century_bit    = DS1340_BIT_CENTURY,
+               .do_trickle_setup = &do_trickle_setup_ds1339,
+               .trickle_charger_reg = 0x08,
+       },
+       [ds_1341] = {
+               .century_reg    = DS1307_REG_MONTH,
+               .century_bit    = DS1337_BIT_CENTURY,
+       },
+       [ds_1388] = {
+               .offset         = 1,
+               .trickle_charger_reg = 0x0a,
+       },
+       [ds_3231] = {
+               .alarm          = 1,
+               .century_reg    = DS1307_REG_MONTH,
+               .century_bit    = DS1337_BIT_CENTURY,
+               .bbsqi_bit      = DS3231_BIT_BBSQW,
+       },
+       [rx_8130] = {
+               .alarm          = 1,
+               /* this is battery backed SRAM */
+               .nvram_offset   = 0x20,
+               .nvram_size     = 4,    /* 32bit (4 word x 8 bit) */
+               .offset         = 0x10,
+               .irq_handler = rx8130_irq,
+               .rtc_ops = &rx8130_rtc_ops,
+       },
+       [m41t0] = {
+               .rtc_ops        = &m41txx_rtc_ops,
+       },
+       [m41t00] = {
+               .rtc_ops        = &m41txx_rtc_ops,
+       },
+       [m41t11] = {
+               /* this is battery backed SRAM */
+               .nvram_offset   = 8,
+               .nvram_size     = 56,
+               .rtc_ops        = &m41txx_rtc_ops,
+       },
+       [mcp794xx] = {
+               .alarm          = 1,
+               /* this is battery backed SRAM */
+               .nvram_offset   = 0x20,
+               .nvram_size     = 0x40,
+               .irq_handler = mcp794xx_irq,
+               .rtc_ops = &mcp794xx_rtc_ops,
+       },
+};
+
+static const struct i2c_device_id ds1307_id[] = {
+       { "ds1307", ds_1307 },
+       { "ds1308", ds_1308 },
+       { "ds1337", ds_1337 },
+       { "ds1338", ds_1338 },
+       { "ds1339", ds_1339 },
+       { "ds1388", ds_1388 },
+       { "ds1340", ds_1340 },
+       { "ds1341", ds_1341 },
+       { "ds3231", ds_3231 },
+       { "m41t0", m41t0 },
+       { "m41t00", m41t00 },
+       { "m41t11", m41t11 },
+       { "mcp7940x", mcp794xx },
+       { "mcp7941x", mcp794xx },
+       { "pt7c4338", ds_1307 },
+       { "rx8025", rx_8025 },
+       { "isl12057", ds_1337 },
+       { "rx8130", rx_8130 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ds1307_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id ds1307_of_match[] = {
+       {
+               .compatible = "dallas,ds1307",
+               .data = (void *)ds_1307
+       },
+       {
+               .compatible = "dallas,ds1308",
+               .data = (void *)ds_1308
+       },
+       {
+               .compatible = "dallas,ds1337",
+               .data = (void *)ds_1337
+       },
+       {
+               .compatible = "dallas,ds1338",
+               .data = (void *)ds_1338
+       },
+       {
+               .compatible = "dallas,ds1339",
+               .data = (void *)ds_1339
+       },
+       {
+               .compatible = "dallas,ds1388",
+               .data = (void *)ds_1388
+       },
+       {
+               .compatible = "dallas,ds1340",
+               .data = (void *)ds_1340
+       },
+       {
+               .compatible = "dallas,ds1341",
+               .data = (void *)ds_1341
+       },
+       {
+               .compatible = "maxim,ds3231",
+               .data = (void *)ds_3231
+       },
+       {
+               .compatible = "st,m41t0",
+               .data = (void *)m41t0
+       },
+       {
+               .compatible = "st,m41t00",
+               .data = (void *)m41t00
+       },
+       {
+               .compatible = "st,m41t11",
+               .data = (void *)m41t11
+       },
+       {
+               .compatible = "microchip,mcp7940x",
+               .data = (void *)mcp794xx
+       },
+       {
+               .compatible = "microchip,mcp7941x",
+               .data = (void *)mcp794xx
+       },
+       {
+               .compatible = "pericom,pt7c4338",
+               .data = (void *)ds_1307
+       },
+       {
+               .compatible = "epson,rx8025",
+               .data = (void *)rx_8025
+       },
+       {
+               .compatible = "isil,isl12057",
+               .data = (void *)ds_1337
+       },
+       {
+               .compatible = "epson,rx8130",
+               .data = (void *)rx_8130
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ds1307_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ds1307_acpi_ids[] = {
+       { .id = "DS1307", .driver_data = ds_1307 },
+       { .id = "DS1308", .driver_data = ds_1308 },
+       { .id = "DS1337", .driver_data = ds_1337 },
+       { .id = "DS1338", .driver_data = ds_1338 },
+       { .id = "DS1339", .driver_data = ds_1339 },
+       { .id = "DS1388", .driver_data = ds_1388 },
+       { .id = "DS1340", .driver_data = ds_1340 },
+       { .id = "DS1341", .driver_data = ds_1341 },
+       { .id = "DS3231", .driver_data = ds_3231 },
+       { .id = "M41T0", .driver_data = m41t0 },
+       { .id = "M41T00", .driver_data = m41t00 },
+       { .id = "M41T11", .driver_data = m41t11 },
+       { .id = "MCP7940X", .driver_data = mcp794xx },
+       { .id = "MCP7941X", .driver_data = mcp794xx },
+       { .id = "PT7C4338", .driver_data = ds_1307 },
+       { .id = "RX8025", .driver_data = rx_8025 },
+       { .id = "ISL12057", .driver_data = ds_1337 },
+       { .id = "RX8130", .driver_data = rx_8130 },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
+#endif
 
 /*
- * Alarm support for mcp794xx devices.
+ * The ds1337 and ds1339 both have two alarms, but we only use the first
+ * one (with a "seconds" field).  For ds1337 we expect nINTA is our alarm
+ * signal; ds1339 chips have only one alarm signal.
  */
-
-#define MCP794XX_REG_CONTROL           0x07
-#      define MCP794XX_BIT_ALM0_EN     0x10
-#      define MCP794XX_BIT_ALM1_EN     0x20
-#define MCP794XX_REG_ALARM0_BASE       0x0a
-#define MCP794XX_REG_ALARM0_CTRL       0x0d
-#define MCP794XX_REG_ALARM1_BASE       0x11
-#define MCP794XX_REG_ALARM1_CTRL       0x14
-#      define MCP794XX_BIT_ALMX_IF     BIT(3)
-#      define MCP794XX_BIT_ALMX_C0     BIT(4)
-#      define MCP794XX_BIT_ALMX_C1     BIT(5)
-#      define MCP794XX_BIT_ALMX_C2     BIT(6)
-#      define MCP794XX_BIT_ALMX_POL    BIT(7)
-#      define MCP794XX_MSK_ALMX_MATCH  (MCP794XX_BIT_ALMX_C0 | \
-                                        MCP794XX_BIT_ALMX_C1 | \
-                                        MCP794XX_BIT_ALMX_C2)
-
-static irqreturn_t mcp794xx_irq(int irq, void *dev_id)
+static irqreturn_t ds1307_irq(int irq, void *dev_id)
 {
-       struct ds1307           *ds1307 = dev_id;
-       struct mutex            *lock = &ds1307->rtc->ops_lock;
-       int reg, ret;
+       struct ds1307           *ds1307 = dev_id;
+       struct mutex            *lock = &ds1307->rtc->ops_lock;
+       int                     stat, ret;
 
        mutex_lock(lock);
-
-       /* Check and clear alarm 0 interrupt flag. */
-       ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, &reg);
-       if (ret)
-               goto out;
-       if (!(reg & MCP794XX_BIT_ALMX_IF))
-               goto out;
-       reg &= ~MCP794XX_BIT_ALMX_IF;
-       ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg);
+       ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat);
        if (ret)
                goto out;
 
-       /* Disable alarm 0. */
-       ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
-                                MCP794XX_BIT_ALM0_EN, 0);
-       if (ret)
-               goto out;
+       if (stat & DS1337_BIT_A1I) {
+               stat &= ~DS1337_BIT_A1I;
+               regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat);
 
-       rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+               ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
+                                        DS1337_BIT_A1IE, 0);
+               if (ret)
+                       goto out;
+
+               rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+       }
 
 out:
        mutex_unlock(lock);
@@ -888,167 +1061,15 @@ out:
        return IRQ_HANDLED;
 }
 
-static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
-{
-       struct ds1307 *ds1307 = dev_get_drvdata(dev);
-       u8 regs[10];
-       int ret;
-
-       if (!test_bit(HAS_ALARM, &ds1307->flags))
-               return -EINVAL;
-
-       /* Read control and alarm 0 registers. */
-       ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
-                              sizeof(regs));
-       if (ret)
-               return ret;
-
-       t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN);
-
-       /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
-       t->time.tm_sec = bcd2bin(regs[3] & 0x7f);
-       t->time.tm_min = bcd2bin(regs[4] & 0x7f);
-       t->time.tm_hour = bcd2bin(regs[5] & 0x3f);
-       t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1;
-       t->time.tm_mday = bcd2bin(regs[7] & 0x3f);
-       t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1;
-       t->time.tm_year = -1;
-       t->time.tm_yday = -1;
-       t->time.tm_isdst = -1;
-
-       dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
-               "enabled=%d polarity=%d irq=%d match=%lu\n", __func__,
-               t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
-               t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled,
-               !!(regs[6] & MCP794XX_BIT_ALMX_POL),
-               !!(regs[6] & MCP794XX_BIT_ALMX_IF),
-               (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4);
-
-       return 0;
-}
-
-/*
- * We may have a random RTC weekday, therefore calculate alarm weekday based
- * on current weekday we read from the RTC timekeeping regs
- */
-static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm)
-{
-       struct rtc_time tm_now;
-       int days_now, days_alarm, ret;
-
-       ret = ds1307_get_time(dev, &tm_now);
-       if (ret)
-               return ret;
-
-       days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60);
-       days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60);
-
-       return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1;
-}
-
-static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
-{
-       struct ds1307 *ds1307 = dev_get_drvdata(dev);
-       unsigned char regs[10];
-       int wday, ret;
-
-       if (!test_bit(HAS_ALARM, &ds1307->flags))
-               return -EINVAL;
-
-       wday = mcp794xx_alm_weekday(dev, &t->time);
-       if (wday < 0)
-               return wday;
-
-       dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
-               "enabled=%d pending=%d\n", __func__,
-               t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
-               t->time.tm_wday, t->time.tm_mday, t->time.tm_mon,
-               t->enabled, t->pending);
-
-       /* Read control and alarm 0 registers. */
-       ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
-                              sizeof(regs));
-       if (ret)
-               return ret;
-
-       /* Set alarm 0, using 24-hour and day-of-month modes. */
-       regs[3] = bin2bcd(t->time.tm_sec);
-       regs[4] = bin2bcd(t->time.tm_min);
-       regs[5] = bin2bcd(t->time.tm_hour);
-       regs[6] = wday;
-       regs[7] = bin2bcd(t->time.tm_mday);
-       regs[8] = bin2bcd(t->time.tm_mon + 1);
-
-       /* Clear the alarm 0 interrupt flag. */
-       regs[6] &= ~MCP794XX_BIT_ALMX_IF;
-       /* Set alarm match: second, minute, hour, day, date, month. */
-       regs[6] |= MCP794XX_MSK_ALMX_MATCH;
-       /* Disable interrupt. We will not enable until completely programmed */
-       regs[0] &= ~MCP794XX_BIT_ALM0_EN;
-
-       ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
-                               sizeof(regs));
-       if (ret)
-               return ret;
-
-       if (!t->enabled)
-               return 0;
-       regs[0] |= MCP794XX_BIT_ALM0_EN;
-       return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]);
-}
-
-static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
-{
-       struct ds1307 *ds1307 = dev_get_drvdata(dev);
-
-       if (!test_bit(HAS_ALARM, &ds1307->flags))
-               return -EINVAL;
-
-       return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
-                                 MCP794XX_BIT_ALM0_EN,
-                                 enabled ? MCP794XX_BIT_ALM0_EN : 0);
-}
-
-static int m41txx_rtc_read_offset(struct device *dev, long *offset)
-{
-       struct ds1307 *ds1307 = dev_get_drvdata(dev);
-       unsigned int ctrl_reg;
-       u8 val;
-
-       regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg);
-
-       val = ctrl_reg & M41TXX_M_CALIBRATION;
-
-       /* check if positive */
-       if (ctrl_reg & M41TXX_BIT_CALIB_SIGN)
-               *offset = (val * M41TXX_POS_OFFSET_STEP_PPB);
-       else
-               *offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB);
-
-       return 0;
-}
-
-static int m41txx_rtc_set_offset(struct device *dev, long offset)
-{
-       struct ds1307 *ds1307 = dev_get_drvdata(dev);
-       unsigned int ctrl_reg;
-
-       if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET))
-               return -ERANGE;
-
-       if (offset >= 0) {
-               ctrl_reg = DIV_ROUND_CLOSEST(offset,
-                                            M41TXX_POS_OFFSET_STEP_PPB);
-               ctrl_reg |= M41TXX_BIT_CALIB_SIGN;
-       } else {
-               ctrl_reg = DIV_ROUND_CLOSEST(abs(offset),
-                                            M41TXX_NEG_OFFSET_STEP_PPB);
-       }
+/*----------------------------------------------------------------------*/
 
-       return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL,
-                                 M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN,
-                                 ctrl_reg);
-}
+static const struct rtc_class_ops ds13xx_rtc_ops = {
+       .read_time      = ds1307_get_time,
+       .set_time       = ds1307_set_time,
+       .read_alarm     = ds1337_read_alarm,
+       .set_alarm      = ds1337_set_alarm,
+       .alarm_irq_enable = ds1307_alarm_irq_enable,
+};
 
 static ssize_t frequency_test_store(struct device *dev,
                                    struct device_attribute *attr,
@@ -1137,30 +1158,6 @@ static int ds1307_nvram_write(void *priv, unsigned int offset, void *val,
 
 /*----------------------------------------------------------------------*/
 
-static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307,
-                                 u32 ohms, bool diode)
-{
-       u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
-               DS1307_TRICKLE_CHARGER_NO_DIODE;
-
-       switch (ohms) {
-       case 250:
-               setup |= DS1307_TRICKLE_CHARGER_250_OHM;
-               break;
-       case 2000:
-               setup |= DS1307_TRICKLE_CHARGER_2K_OHM;
-               break;
-       case 4000:
-               setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
-               break;
-       default:
-               dev_warn(ds1307->dev,
-                        "Unsupported ohm value %u in dt\n", ohms);
-               return 0;
-       }
-       return setup;
-}
-
 static u8 ds1307_trickle_init(struct ds1307 *ds1307,
                              const struct chip_desc *chip)
 {
index 9caaccc..b1ebca0 100644 (file)
@@ -58,7 +58,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
                "%s: raw read data - counters=%02x,%02x,%02x,%02x\n",
                __func__, buf[0], buf[1], buf[2], buf[3]);
 
-       time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+              (buf[1] << 8) | buf[0];
 
        rtc_time_to_tm(time, tm);
 
index e5ad527..d03f5d2 100644 (file)
@@ -109,6 +109,8 @@ static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
        }
 
        ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf);
+       if (ret < 0)
+               return ret;
 
        tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK);
        tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK);
index 7ff0854..19642bf 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright 2018 NXP.
  */
 
+#include <linux/arm-smccc.h>
 #include <linux/firmware/imx/sci.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -12,6 +13,9 @@
 #define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970      9
 #define IMX_SC_TIMER_FUNC_SET_RTC_TIME         6
 
+#define IMX_SIP_SRTC                   0xC2000002
+#define IMX_SIP_SRTC_SET_TIME          0x0
+
 static struct imx_sc_ipc *rtc_ipc_handle;
 static struct rtc_device *imx_sc_rtc;
 
@@ -37,13 +41,28 @@ static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm)
                return ret;
        }
 
-       rtc_time_to_tm(msg.time, tm);
+       rtc_time64_to_tm(msg.time, tm);
 
        return 0;
 }
 
+static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct arm_smccc_res res;
+
+       /* pack 2 time parameters into 1 register, 16 bits for each */
+       arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME,
+                     ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1),
+                     (tm->tm_mday << 16) | tm->tm_hour,
+                     (tm->tm_min << 16) | tm->tm_sec,
+                     0, 0, 0, &res);
+
+       return res.a0;
+}
+
 static const struct rtc_class_ops imx_sc_rtc_ops = {
        .read_time = imx_sc_rtc_read_time,
+       .set_time = imx_sc_rtc_set_time,
 };
 
 static int imx_sc_rtc_probe(struct platform_device *pdev)
index 37ab3e1..471e395 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/bcd.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/rtc.h>
 
 static struct i2c_driver isl1208_driver;
 
 /* ISL1208 various variants */
-enum {
+enum isl1208_id {
        TYPE_ISL1208 = 0,
+       TYPE_ISL1209,
        TYPE_ISL1218,
        TYPE_ISL1219,
+       ISL_LAST_ID
+};
+
+/* Chip capabilities table */
+static const struct isl1208_config {
+       const char      name[8];
+       unsigned int    nvmem_length;
+       unsigned        has_tamper:1;
+       unsigned        has_timestamp:1;
+} isl1208_configs[] = {
+       [TYPE_ISL1208] = { "isl1208", 2, false, false },
+       [TYPE_ISL1209] = { "isl1209", 2, true,  false },
+       [TYPE_ISL1218] = { "isl1218", 8, false, false },
+       [TYPE_ISL1219] = { "isl1219", 2, true,  true },
+};
+
+static const struct i2c_device_id isl1208_id[] = {
+       { "isl1208", TYPE_ISL1208 },
+       { "isl1209", TYPE_ISL1209 },
+       { "isl1218", TYPE_ISL1218 },
+       { "isl1219", TYPE_ISL1219 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, isl1208_id);
+
+static const struct of_device_id isl1208_of_match[] = {
+       { .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] },
+       { .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] },
+       { .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] },
+       { .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] },
+       { }
+};
+MODULE_DEVICE_TABLE(of, isl1208_of_match);
+
+/* Device state */
+struct isl1208_state {
+       struct nvmem_config nvmem_config;
+       struct rtc_device *rtc;
+       const struct isl1208_config *config;
 };
 
 /* block read */
@@ -161,6 +202,7 @@ isl1208_i2c_get_atr(struct i2c_client *client)
        return atr;
 }
 
+/* returns adjustment value + 100 */
 static int
 isl1208_i2c_get_dtr(struct i2c_client *client)
 {
@@ -171,7 +213,7 @@ isl1208_i2c_get_dtr(struct i2c_client *client)
        /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
        dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1);
 
-       return dtr;
+       return dtr + 100;
 }
 
 static int
@@ -248,8 +290,8 @@ isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
                   (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay");
 
        dtr = isl1208_i2c_get_dtr(client);
-       if (dtr >= 0 - 1)
-               seq_printf(seq, "digital_trim\t: %d ppm\n", dtr);
+       if (dtr >= 0)
+               seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100);
 
        atr = isl1208_i2c_get_atr(client);
        if (atr >= 0)
@@ -556,7 +598,7 @@ isl1208_rtc_interrupt(int irq, void *data)
 {
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
        struct i2c_client *client = data;
-       struct rtc_device *rtc = i2c_get_clientdata(client);
+       struct isl1208_state *isl1208 = i2c_get_clientdata(client);
        int handled = 0, sr, err;
 
        /*
@@ -579,7 +621,7 @@ isl1208_rtc_interrupt(int irq, void *data)
        if (sr & ISL1208_REG_SR_ALM) {
                dev_dbg(&client->dev, "alarm!\n");
 
-               rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+               rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF);
 
                /* Clear the alarm */
                sr &= ~ISL1208_REG_SR_ALM;
@@ -596,11 +638,12 @@ isl1208_rtc_interrupt(int irq, void *data)
                        return err;
        }
 
-       if (sr & ISL1208_REG_SR_EVT) {
-               sysfs_notify(&rtc->dev.kobj, NULL,
-                            dev_attr_timestamp0.attr.name);
+       if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) {
                dev_warn(&client->dev, "event detected");
                handled = 1;
+               if (isl1208->config->has_timestamp)
+                       sysfs_notify(&isl1208->rtc->dev.kobj, NULL,
+                                    dev_attr_timestamp0.attr.name);
        }
 
        return handled ? IRQ_HANDLED : IRQ_NONE;
@@ -637,7 +680,7 @@ isl1208_sysfs_show_dtrim(struct device *dev,
        if (dtr < 0)
                return dtr;
 
-       return sprintf(buf, "%d ppm\n", dtr);
+       return sprintf(buf, "%d ppm\n", dtr - 100);
 }
 
 static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);
@@ -700,6 +743,46 @@ static const struct attribute_group isl1219_rtc_sysfs_files = {
        .attrs  = isl1219_rtc_attrs,
 };
 
+static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf,
+                             size_t count)
+{
+       struct isl1208_state *isl1208 = priv;
+       struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
+       int ret;
+
+       /* nvmem sanitizes offset/count for us, but count==0 is possible */
+       if (!count)
+               return count;
+       ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf,
+                                   count);
+       return ret == 0 ? count : ret;
+}
+
+static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf,
+                              size_t count)
+{
+       struct isl1208_state *isl1208 = priv;
+       struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
+       int ret;
+
+       /* nvmem sanitizes off/count for us, but count==0 is possible */
+       if (!count)
+               return count;
+       ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf,
+                                  count);
+
+       return ret == 0 ? count : ret;
+}
+
+static const struct nvmem_config isl1208_nvmem_config = {
+       .name = "isl1208_nvram",
+       .word_size = 1,
+       .stride = 1,
+       /* .size from chip specific config */
+       .reg_read = isl1208_nvmem_read,
+       .reg_write = isl1208_nvmem_write,
+};
+
 static int isl1208_setup_irq(struct i2c_client *client, int irq)
 {
        int rc = devm_request_threaded_irq(&client->dev, irq, NULL,
@@ -722,7 +805,7 @@ static int
 isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        int rc = 0;
-       struct rtc_device *rtc;
+       struct isl1208_state *isl1208;
        int evdet_irq = -1;
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
@@ -731,13 +814,33 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
        if (isl1208_i2c_validate_client(client) < 0)
                return -ENODEV;
 
-       rtc = devm_rtc_allocate_device(&client->dev);
-       if (IS_ERR(rtc))
-               return PTR_ERR(rtc);
+       /* Allocate driver state, point i2c client data to it */
+       isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL);
+       if (!isl1208)
+               return -ENOMEM;
+       i2c_set_clientdata(client, isl1208);
+
+       /* Determine which chip we have */
+       if (client->dev.of_node) {
+               isl1208->config = of_device_get_match_data(&client->dev);
+               if (!isl1208->config)
+                       return -ENODEV;
+       } else {
+               if (id->driver_data >= ISL_LAST_ID)
+                       return -ENODEV;
+               isl1208->config = &isl1208_configs[id->driver_data];
+       }
+
+       isl1208->rtc = devm_rtc_allocate_device(&client->dev);
+       if (IS_ERR(isl1208->rtc))
+               return PTR_ERR(isl1208->rtc);
 
-       rtc->ops = &isl1208_rtc_ops;
+       isl1208->rtc->ops = &isl1208_rtc_ops;
 
-       i2c_set_clientdata(client, rtc);
+       /* Setup nvmem configuration in driver state struct */
+       isl1208->nvmem_config = isl1208_nvmem_config;
+       isl1208->nvmem_config.size = isl1208->config->nvmem_length;
+       isl1208->nvmem_config.priv = isl1208;
 
        rc = isl1208_i2c_get_sr(client);
        if (rc < 0) {
@@ -749,7 +852,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
                dev_warn(&client->dev, "rtc power failure detected, "
                         "please set clock.\n");
 
-       if (id->driver_data == TYPE_ISL1219) {
+       if (isl1208->config->has_tamper) {
                struct device_node *np = client->dev.of_node;
                u32 evienb;
 
@@ -770,13 +873,15 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
                        dev_err(&client->dev, "could not enable tamper detection\n");
                        return rc;
                }
-               rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files);
+               evdet_irq = of_irq_get_byname(np, "evdet");
+       }
+       if (isl1208->config->has_timestamp) {
+               rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files);
                if (rc)
                        return rc;
-               evdet_irq = of_irq_get_byname(np, "evdet");
        }
 
-       rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files);
+       rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files);
        if (rc)
                return rc;
 
@@ -790,24 +895,12 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
        if (rc)
                return rc;
 
-       return rtc_register_device(rtc);
-}
-
-static const struct i2c_device_id isl1208_id[] = {
-       { "isl1208", TYPE_ISL1208 },
-       { "isl1218", TYPE_ISL1218 },
-       { "isl1219", TYPE_ISL1219 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, isl1208_id);
+       rc = rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config);
+       if (rc)
+               return rc;
 
-static const struct of_device_id isl1208_of_match[] = {
-       { .compatible = "isil,isl1208" },
-       { .compatible = "isil,isl1218" },
-       { .compatible = "isil,isl1219" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, isl1208_of_match);
+       return rtc_register_device(isl1208->rtc);
+}
 
 static struct i2c_driver isl1208_driver = {
        .driver = {
index 2f1772a..18a6f15 100644 (file)
@@ -82,7 +82,7 @@ unsigned int mc146818_get_time(struct rtc_time *time)
        time->tm_year += real_year - 72;
 #endif
 
-       if (century)
+       if (century > 20)
                time->tm_year += (century - 19) * 100;
 
        /*
diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c
new file mode 100644 (file)
index 0000000..e08b981
--- /dev/null
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RTC driver for the interal RTC block in the Amlogic Meson6, Meson8,
+ * Meson8b and Meson8m2 SoCs.
+ *
+ * The RTC is split in to two parts, the AHB front end and a simple serial
+ * connection to the actual registers. This driver manages both parts.
+ *
+ * Copyright (c) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (c) 2015 Ben Dooks <ben.dooks@codethink.co.uk> for Codethink Ltd
+ * Based on origin by Carlo Caione <carlo@endlessm.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/rtc.h>
+
+/* registers accessed from cpu bus */
+#define RTC_ADDR0                              0x00
+       #define RTC_ADDR0_LINE_SCLK             BIT(0)
+       #define RTC_ADDR0_LINE_SEN              BIT(1)
+       #define RTC_ADDR0_LINE_SDI              BIT(2)
+       #define RTC_ADDR0_START_SER             BIT(17)
+       #define RTC_ADDR0_WAIT_SER              BIT(22)
+       #define RTC_ADDR0_DATA                  GENMASK(31, 24)
+
+#define RTC_ADDR1                              0x04
+       #define RTC_ADDR1_SDO                   BIT(0)
+       #define RTC_ADDR1_S_READY               BIT(1)
+
+#define RTC_ADDR2                              0x08
+#define RTC_ADDR3                              0x0c
+
+#define RTC_REG4                               0x10
+       #define RTC_REG4_STATIC_VALUE           GENMASK(7, 0)
+
+/* rtc registers accessed via rtc-serial interface */
+#define RTC_COUNTER            (0)
+#define RTC_SEC_ADJ            (2)
+#define RTC_REGMEM_0           (4)
+#define RTC_REGMEM_1           (5)
+#define RTC_REGMEM_2           (6)
+#define RTC_REGMEM_3           (7)
+
+#define RTC_ADDR_BITS          (3)     /* number of address bits to send */
+#define RTC_DATA_BITS          (32)    /* number of data bits to tx/rx */
+
+#define MESON_STATIC_BIAS_CUR  (0x5 << 1)
+#define MESON_STATIC_VOLTAGE   (0x3 << 11)
+#define MESON_STATIC_DEFAULT    (MESON_STATIC_BIAS_CUR | MESON_STATIC_VOLTAGE)
+
+struct meson_rtc {
+       struct rtc_device       *rtc;           /* rtc device we created */
+       struct device           *dev;           /* device we bound from */
+       struct reset_control    *reset;         /* reset source */
+       struct regulator        *vdd;           /* voltage input */
+       struct regmap           *peripheral;    /* peripheral registers */
+       struct regmap           *serial;        /* serial registers */
+};
+
+static const struct regmap_config meson_rtc_peripheral_regmap_config = {
+       .name           = "peripheral-registers",
+       .reg_bits       = 8,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+       .max_register   = RTC_REG4,
+       .fast_io        = true,
+};
+
+/* RTC front-end serialiser controls */
+
+static void meson_rtc_sclk_pulse(struct meson_rtc *rtc)
+{
+       udelay(5);
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, 0);
+       udelay(5);
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK,
+                          RTC_ADDR0_LINE_SCLK);
+}
+
+static void meson_rtc_send_bit(struct meson_rtc *rtc, unsigned int bit)
+{
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI,
+                          bit ? RTC_ADDR0_LINE_SDI : 0);
+       meson_rtc_sclk_pulse(rtc);
+}
+
+static void meson_rtc_send_bits(struct meson_rtc *rtc, u32 data,
+                               unsigned int nr)
+{
+       u32 bit = 1 << (nr - 1);
+
+       while (bit) {
+               meson_rtc_send_bit(rtc, data & bit);
+               bit >>= 1;
+       }
+}
+
+static void meson_rtc_set_dir(struct meson_rtc *rtc, u32 mode)
+{
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, 0);
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0);
+       meson_rtc_send_bit(rtc, mode);
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0);
+}
+
+static u32 meson_rtc_get_data(struct meson_rtc *rtc)
+{
+       u32 tmp, val = 0;
+       int bit;
+
+       for (bit = 0; bit < RTC_DATA_BITS; bit++) {
+               meson_rtc_sclk_pulse(rtc);
+               val <<= 1;
+
+               regmap_read(rtc->peripheral, RTC_ADDR1, &tmp);
+               val |= tmp & RTC_ADDR1_SDO;
+       }
+
+       return val;
+}
+
+static int meson_rtc_get_bus(struct meson_rtc *rtc)
+{
+       int ret, retries = 3;
+       u32 val;
+
+       /* prepare bus for transfers, set all lines low */
+       val = RTC_ADDR0_LINE_SDI | RTC_ADDR0_LINE_SEN | RTC_ADDR0_LINE_SCLK;
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, val, 0);
+
+       for (retries = 0; retries < 3; retries++) {
+               /* wait for the bus to be ready */
+               if (!regmap_read_poll_timeout(rtc->peripheral, RTC_ADDR1, val,
+                                             val & RTC_ADDR1_S_READY, 10,
+                                             10000))
+                       return 0;
+
+               dev_warn(rtc->dev, "failed to get bus, resetting RTC\n");
+
+               ret = reset_control_reset(rtc->reset);
+               if (ret)
+                       return ret;
+       }
+
+       dev_err(rtc->dev, "bus is not ready\n");
+       return -ETIMEDOUT;
+}
+
+static int meson_rtc_serial_bus_reg_read(void *context, unsigned int reg,
+                                        unsigned int *data)
+{
+       struct meson_rtc *rtc = context;
+       int ret;
+
+       ret = meson_rtc_get_bus(rtc);
+       if (ret)
+               return ret;
+
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN,
+                          RTC_ADDR0_LINE_SEN);
+       meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS);
+       meson_rtc_set_dir(rtc, 0);
+       *data = meson_rtc_get_data(rtc);
+
+       return 0;
+}
+
+static int meson_rtc_serial_bus_reg_write(void *context, unsigned int reg,
+                                         unsigned int data)
+{
+       struct meson_rtc *rtc = context;
+       int ret;
+
+       ret = meson_rtc_get_bus(rtc);
+       if (ret)
+               return ret;
+
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN,
+                          RTC_ADDR0_LINE_SEN);
+       meson_rtc_send_bits(rtc, data, RTC_DATA_BITS);
+       meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS);
+       meson_rtc_set_dir(rtc, 1);
+
+       return 0;
+}
+
+static const struct regmap_bus meson_rtc_serial_bus = {
+       .reg_read       = meson_rtc_serial_bus_reg_read,
+       .reg_write      = meson_rtc_serial_bus_reg_write,
+};
+
+static const struct regmap_config meson_rtc_serial_regmap_config = {
+       .name           = "serial-registers",
+       .reg_bits       = 4,
+       .reg_stride     = 1,
+       .val_bits       = 32,
+       .max_register   = RTC_REGMEM_3,
+       .fast_io        = false,
+};
+
+static int meson_rtc_write_static(struct meson_rtc *rtc, u32 data)
+{
+       u32 tmp;
+
+       regmap_write(rtc->peripheral, RTC_REG4,
+                    FIELD_PREP(RTC_REG4_STATIC_VALUE, (data >> 8)));
+
+       /* write the static value and start the auto serializer */
+       tmp = FIELD_PREP(RTC_ADDR0_DATA, (data & 0xff)) | RTC_ADDR0_START_SER;
+       regmap_update_bits(rtc->peripheral, RTC_ADDR0,
+                          RTC_ADDR0_DATA | RTC_ADDR0_START_SER, tmp);
+
+       /* wait for the auto serializer to complete */
+       return regmap_read_poll_timeout(rtc->peripheral, RTC_REG4, tmp,
+                                       !(tmp & RTC_ADDR0_WAIT_SER), 10,
+                                       10000);
+}
+
+/* RTC interface layer functions */
+
+static int meson_rtc_gettime(struct device *dev, struct rtc_time *tm)
+{
+       struct meson_rtc *rtc = dev_get_drvdata(dev);
+       u32 time;
+       int ret;
+
+       ret = regmap_read(rtc->serial, RTC_COUNTER, &time);
+       if (!ret)
+               rtc_time64_to_tm(time, tm);
+
+       return ret;
+}
+
+static int meson_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+       struct meson_rtc *rtc = dev_get_drvdata(dev);
+
+       return regmap_write(rtc->serial, RTC_COUNTER, rtc_tm_to_time64(tm));
+}
+
+static const struct rtc_class_ops meson_rtc_ops = {
+       .read_time      = meson_rtc_gettime,
+       .set_time       = meson_rtc_settime,
+};
+
+/* NVMEM interface layer functions */
+
+static int meson_rtc_regmem_read(void *context, unsigned int offset,
+                                void *buf, size_t bytes)
+{
+       struct meson_rtc *rtc = context;
+       unsigned int read_offset, read_size;
+
+       read_offset = RTC_REGMEM_0 + (offset / 4);
+       read_size = bytes / 4;
+
+       return regmap_bulk_read(rtc->serial, read_offset, buf, read_size);
+}
+
+static int meson_rtc_regmem_write(void *context, unsigned int offset,
+                                 void *buf, size_t bytes)
+{
+       struct meson_rtc *rtc = context;
+       unsigned int write_offset, write_size;
+
+       write_offset = RTC_REGMEM_0 + (offset / 4);
+       write_size = bytes / 4;
+
+       return regmap_bulk_write(rtc->serial, write_offset, buf, write_size);
+}
+
+static int meson_rtc_probe(struct platform_device *pdev)
+{
+       struct nvmem_config meson_rtc_nvmem_config = {
+               .name = "meson-rtc-regmem",
+               .type = NVMEM_TYPE_BATTERY_BACKED,
+               .word_size = 4,
+               .stride = 4,
+               .size = 4 * 4,
+               .reg_read = meson_rtc_regmem_read,
+               .reg_write = meson_rtc_regmem_write,
+       };
+       struct device *dev = &pdev->dev;
+       struct meson_rtc *rtc;
+       struct resource *res;
+       void __iomem *base;
+       int ret;
+       u32 tm;
+
+       rtc = devm_kzalloc(dev, sizeof(struct meson_rtc), GFP_KERNEL);
+       if (!rtc)
+               return -ENOMEM;
+
+       rtc->rtc = devm_rtc_allocate_device(dev);
+       if (IS_ERR(rtc->rtc))
+               return PTR_ERR(rtc->rtc);
+
+       platform_set_drvdata(pdev, rtc);
+
+       rtc->dev = dev;
+
+       rtc->rtc->ops = &meson_rtc_ops;
+       rtc->rtc->range_max = U32_MAX;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       rtc->peripheral = devm_regmap_init_mmio(dev, base,
+                                       &meson_rtc_peripheral_regmap_config);
+       if (IS_ERR(rtc->peripheral)) {
+               dev_err(dev, "failed to create peripheral regmap\n");
+               return PTR_ERR(rtc->peripheral);
+       }
+
+       rtc->reset = devm_reset_control_get(dev, NULL);
+       if (IS_ERR(rtc->reset)) {
+               dev_err(dev, "missing reset line\n");
+               return PTR_ERR(rtc->reset);
+       }
+
+       rtc->vdd = devm_regulator_get(dev, "vdd");
+       if (IS_ERR(rtc->vdd)) {
+               dev_err(dev, "failed to get the vdd-supply\n");
+               return PTR_ERR(rtc->vdd);
+       }
+
+       ret = regulator_enable(rtc->vdd);
+       if (ret) {
+               dev_err(dev, "failed to enable vdd-supply\n");
+               return ret;
+       }
+
+       ret = meson_rtc_write_static(rtc, MESON_STATIC_DEFAULT);
+       if (ret) {
+               dev_err(dev, "failed to set static values\n");
+               goto out_disable_vdd;
+       }
+
+       rtc->serial = devm_regmap_init(dev, &meson_rtc_serial_bus, rtc,
+                                      &meson_rtc_serial_regmap_config);
+       if (IS_ERR(rtc->serial)) {
+               dev_err(dev, "failed to create serial regmap\n");
+               ret = PTR_ERR(rtc->serial);
+               goto out_disable_vdd;
+       }
+
+       /*
+        * check if we can read RTC counter, if not then the RTC is probably
+        * not functional. If it isn't probably best to not bind.
+        */
+       ret = regmap_read(rtc->serial, RTC_COUNTER, &tm);
+       if (ret) {
+               dev_err(dev, "cannot read RTC counter, RTC not functional\n");
+               goto out_disable_vdd;
+       }
+
+       meson_rtc_nvmem_config.priv = rtc;
+       ret = rtc_nvmem_register(rtc->rtc, &meson_rtc_nvmem_config);
+       if (ret)
+               goto out_disable_vdd;
+
+       ret = rtc_register_device(rtc->rtc);
+       if (ret)
+               goto out_disable_vdd;
+
+       return 0;
+
+out_disable_vdd:
+       regulator_disable(rtc->vdd);
+       return ret;
+}
+
+static const struct of_device_id meson_rtc_dt_match[] = {
+       { .compatible = "amlogic,meson6-rtc", },
+       { .compatible = "amlogic,meson8-rtc", },
+       { .compatible = "amlogic,meson8b-rtc", },
+       { .compatible = "amlogic,meson8m2-rtc", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, meson_rtc_dt_match);
+
+static struct platform_driver meson_rtc_driver = {
+       .probe          = meson_rtc_probe,
+       .driver         = {
+               .name           = "meson-rtc",
+               .of_match_table = of_match_ptr(meson_rtc_dt_match),
+       },
+};
+module_platform_driver(meson_rtc_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson RTC Driver");
+MODULE_AUTHOR("Ben Dooks <ben.doosk@codethink.co.uk>");
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:meson-rtc");
index 283c233..f6ce63c 100644 (file)
 */
 
 #define PCF85063_REG_CTRL1             0x00 /* status */
+#define PCF85063_REG_CTRL1_CAP_SEL     BIT(0)
 #define PCF85063_REG_CTRL1_STOP                BIT(5)
-#define PCF85063_REG_CTRL2             0x01
 
 #define PCF85063_REG_SC                        0x04 /* datetime */
 #define PCF85063_REG_SC_OS             0x80
-#define PCF85063_REG_MN                        0x05
-#define PCF85063_REG_HR                        0x06
-#define PCF85063_REG_DM                        0x07
-#define PCF85063_REG_DW                        0x08
-#define PCF85063_REG_MO                        0x09
-#define PCF85063_REG_YR                        0x0A
 
 static struct i2c_driver pcf85063_driver;
 
@@ -180,6 +174,39 @@ static const struct rtc_class_ops pcf85063_rtc_ops = {
        .set_time       = pcf85063_rtc_set_time
 };
 
+static int pcf85063_load_capacitance(struct i2c_client *client)
+{
+       u32 load;
+       int rc;
+       u8 reg;
+
+       rc = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
+       if (rc < 0)
+               return rc;
+
+       reg = rc;
+       load = 7000;
+       of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads",
+                            &load);
+
+       switch (load) {
+       default:
+               dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000",
+                        load);
+               /* fall through */
+       case 7000:
+               reg &= ~PCF85063_REG_CTRL1_CAP_SEL;
+               break;
+       case 12500:
+               reg |= PCF85063_REG_CTRL1_CAP_SEL;
+               break;
+       }
+
+       rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, reg);
+
+       return rc;
+}
+
 static int pcf85063_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
 {
@@ -197,6 +224,11 @@ static int pcf85063_probe(struct i2c_client *client,
                return err;
        }
 
+       err = pcf85063_load_capacitance(client);
+       if (err < 0)
+               dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
+                        err);
+
        rtc = devm_rtc_device_register(&client->dev,
                                       pcf85063_driver.driver.name,
                                       &pcf85063_rtc_ops, THIS_MODULE);
index 3fcd2cb..b5c61a7 100644 (file)
@@ -97,8 +97,9 @@ static int pcf8523_voltage_low(struct i2c_client *client)
        return !!(value & REG_CONTROL3_BLF);
 }
 
-static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
+static int pcf8523_load_capacitance(struct i2c_client *client)
 {
+       u32 load;
        u8 value;
        int err;
 
@@ -106,14 +107,24 @@ static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
        if (err < 0)
                return err;
 
-       if (!high)
-               value &= ~REG_CONTROL1_CAP_SEL;
-       else
+       load = 12500;
+       of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads",
+                            &load);
+
+       switch (load) {
+       default:
+               dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500",
+                        load);
+               /* fall through */
+       case 12500:
                value |= REG_CONTROL1_CAP_SEL;
+               break;
+       case 7000:
+               value &= ~REG_CONTROL1_CAP_SEL;
+               break;
+       }
 
        err = pcf8523_write(client, REG_CONTROL1, value);
-       if (err < 0)
-               return err;
 
        return err;
 }
@@ -347,9 +358,10 @@ static int pcf8523_probe(struct i2c_client *client,
        if (!pcf)
                return -ENOMEM;
 
-       err = pcf8523_select_capacitance(client, true);
+       err = pcf8523_load_capacitance(client);
        if (err < 0)
-               return err;
+               dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
+                        err);
 
        err = pcf8523_set_pm(client, 0);
        if (err < 0)
@@ -374,6 +386,7 @@ MODULE_DEVICE_TABLE(i2c, pcf8523_id);
 #ifdef CONFIG_OF
 static const struct of_device_id pcf8523_of_match[] = {
        { .compatible = "nxp,pcf8523" },
+       { .compatible = "microcrystal,rv8523" },
        { }
 };
 MODULE_DEVICE_TABLE(of, pcf8523_of_match);
index d7ef0a6..1c4de6e 100644 (file)
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * PIC32 RTC driver
  *
  * Joshua Henderson <joshua.henderson@microchip.com>
  * Copyright (C) 2016 Microchip Technology Inc.  All rights reserved.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that 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/init.h>
 #include <linux/module.h>
@@ -180,22 +172,16 @@ static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm)
 {
        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
        void __iomem *base = pdata->reg_base;
-       int year = tm->tm_year - 100;
 
        dev_dbg(dev, "set time %ptR\n", tm);
 
-       if (year < 0 || year >= 100) {
-               dev_err(dev, "rtc only supports 100 years\n");
-               return -EINVAL;
-       }
-
        clk_enable(pdata->clk);
        writeb(bin2bcd(tm->tm_sec),  base + PIC32_RTCSEC);
        writeb(bin2bcd(tm->tm_min),  base + PIC32_RTCMIN);
        writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR);
        writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY);
        writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON);
-       writeb(bin2bcd(year), base + PIC32_RTCYEAR);
+       writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR);
        clk_disable(pdata->clk);
 
        return 0;
@@ -348,13 +334,17 @@ static int pic32_rtc_probe(struct platform_device *pdev)
 
        device_init_wakeup(&pdev->dev, 1);
 
-       pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
-                                                &pic32_rtcops,
-                                                THIS_MODULE);
-       if (IS_ERR(pdata->rtc)) {
-               ret = PTR_ERR(pdata->rtc);
+       pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
+       if (IS_ERR(pdata->rtc))
+               return PTR_ERR(pdata->rtc);
+
+       pdata->rtc->ops = &pic32_rtcops;
+       pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+       pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+       ret = rtc_register_device(pdata->rtc);
+       if (ret)
                goto err_nortc;
-       }
 
        pdata->rtc->max_user_freq = 128;
 
index 1074e3d..cda0207 100644 (file)
@@ -213,7 +213,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
                }
        }
 
-       secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+       secs = value[0] | (value[1] << 8) | (value[2] << 16) |
+              ((unsigned long)value[3] << 24);
 
        rtc_time_to_tm(secs, tm);
 
@@ -284,7 +285,8 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
                return rc;
        }
 
-       secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+       secs = value[0] | (value[1] << 8) | (value[2] << 16) |
+              ((unsigned long)value[3] << 24);
 
        rtc_time_to_tm(secs, &alarm->time);
 
index c503832..66a473a 100644 (file)
 #      define RS5C_CTRL1_CT4           (4 << 0)        /* 1 Hz level irq */
 #define RS5C_REG_CTRL2         15
 #      define RS5C372_CTRL2_24         (1 << 5)
-#      define R2025_CTRL2_XST          (1 << 5)
-#      define RS5C_CTRL2_XSTP          (1 << 4)        /* only if !R2025S/D */
+#      define RS5C_CTRL2_XSTP          (1 << 4)        /* only if !R2x2x */
+#      define R2x2x_CTRL2_VDET         (1 << 6)        /* only if  R2x2x */
+#      define R2x2x_CTRL2_XSTP         (1 << 5)        /* only if  R2x2x */
+#      define R2x2x_CTRL2_PON          (1 << 4)        /* only if  R2x2x */
 #      define RS5C_CTRL2_CTFG          (1 << 2)
 #      define RS5C_CTRL2_AAFG          (1 << 1)        /* or WAFG */
 #      define RS5C_CTRL2_BAFG          (1 << 0)        /* or DAFG */
@@ -212,10 +214,27 @@ static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
        struct i2c_client *client = to_i2c_client(dev);
        struct rs5c372  *rs5c = i2c_get_clientdata(client);
        int             status = rs5c_get_regs(rs5c);
+       unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2];
 
        if (status < 0)
                return status;
 
+       switch (rs5c->type) {
+       case rtc_r2025sd:
+       case rtc_r2221tl:
+               if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
+                   (rs5c->type == rtc_r2221tl &&  (ctrl2 & R2x2x_CTRL2_XSTP))) {
+                       dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               if (ctrl2 & RS5C_CTRL2_XSTP) {
+                       dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
+                       return -EINVAL;
+               }
+       }
+
        tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f);
        tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f);
        tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);
@@ -243,6 +262,7 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
        struct i2c_client *client = to_i2c_client(dev);
        struct rs5c372  *rs5c = i2c_get_clientdata(client);
        unsigned char   buf[7];
+       unsigned char   ctrl2;
        int             addr;
 
        dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
@@ -261,7 +281,32 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
        buf[6] = bin2bcd(tm->tm_year - 100);
 
        if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
-               dev_err(&client->dev, "%s: write error\n", __func__);
+               dev_dbg(&client->dev, "%s: write error in line %i\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+
+       addr = RS5C_ADDR(RS5C_REG_CTRL2);
+       ctrl2 = i2c_smbus_read_byte_data(client, addr);
+
+       /* clear rtc warning bits */
+       switch (rs5c->type) {
+       case rtc_r2025sd:
+       case rtc_r2221tl:
+               ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON);
+               if (rs5c->type == rtc_r2025sd)
+                       ctrl2 |= R2x2x_CTRL2_XSTP;
+               else
+                       ctrl2 &= ~R2x2x_CTRL2_XSTP;
+               break;
+       default:
+               ctrl2 &= ~RS5C_CTRL2_XSTP;
+               break;
+       }
+
+       if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) {
+               dev_dbg(&client->dev, "%s: write error in line %i\n",
+                       __func__, __LINE__);
                return -EIO;
        }
 
@@ -519,20 +564,25 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
        unsigned char buf[2];
        int addr, i, ret = 0;
 
-       if (rs5c372->type == rtc_r2025sd) {
-               if (rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST)
-                       return ret;
-               rs5c372->regs[RS5C_REG_CTRL2] |= R2025_CTRL2_XST;
-       } else {
-               if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP))
-                       return ret;
-               rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
-       }
-
        addr   = RS5C_ADDR(RS5C_REG_CTRL1);
        buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
        buf[1] = rs5c372->regs[RS5C_REG_CTRL2];
 
+       switch (rs5c372->type) {
+       case rtc_r2025sd:
+               if (buf[1] & R2x2x_CTRL2_XSTP)
+                       return ret;
+               break;
+       case rtc_r2221tl:
+               if (!(buf[1] & R2x2x_CTRL2_XSTP))
+                       return ret;
+               break;
+       default:
+               if (!(buf[1] & RS5C_CTRL2_XSTP))
+                       return ret;
+               break;
+       }
+
        /* use 24hr mode */
        switch (rs5c372->type) {
        case rtc_rs5c372a:
diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c
new file mode 100644 (file)
index 0000000..06884eb
--- /dev/null
@@ -0,0 +1,732 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RTC driver for the Micro Crystal RV3028
+ *
+ * Copyright (C) 2019 Micro Crystal SA
+ *
+ * Alexandre Belloni <alexandre.belloni@bootlin.com>
+ *
+ */
+
+#include <linux/bcd.h>
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+
+#define RV3028_SEC                     0x00
+#define RV3028_MIN                     0x01
+#define RV3028_HOUR                    0x02
+#define RV3028_WDAY                    0x03
+#define RV3028_DAY                     0x04
+#define RV3028_MONTH                   0x05
+#define RV3028_YEAR                    0x06
+#define RV3028_ALARM_MIN               0x07
+#define RV3028_ALARM_HOUR              0x08
+#define RV3028_ALARM_DAY               0x09
+#define RV3028_STATUS                  0x0E
+#define RV3028_CTRL1                   0x0F
+#define RV3028_CTRL2                   0x10
+#define RV3028_EVT_CTRL                        0x13
+#define RV3028_TS_COUNT                        0x14
+#define RV3028_TS_SEC                  0x15
+#define RV3028_RAM1                    0x1F
+#define RV3028_EEPROM_ADDR             0x25
+#define RV3028_EEPROM_DATA             0x26
+#define RV3028_EEPROM_CMD              0x27
+#define RV3028_CLKOUT                  0x35
+#define RV3028_OFFSET                  0x36
+#define RV3028_BACKUP                  0x37
+
+#define RV3028_STATUS_PORF             BIT(0)
+#define RV3028_STATUS_EVF              BIT(1)
+#define RV3028_STATUS_AF               BIT(2)
+#define RV3028_STATUS_TF               BIT(3)
+#define RV3028_STATUS_UF               BIT(4)
+#define RV3028_STATUS_BSF              BIT(5)
+#define RV3028_STATUS_CLKF             BIT(6)
+#define RV3028_STATUS_EEBUSY           BIT(7)
+
+#define RV3028_CTRL1_EERD              BIT(3)
+#define RV3028_CTRL1_WADA              BIT(5)
+
+#define RV3028_CTRL2_RESET             BIT(0)
+#define RV3028_CTRL2_12_24             BIT(1)
+#define RV3028_CTRL2_EIE               BIT(2)
+#define RV3028_CTRL2_AIE               BIT(3)
+#define RV3028_CTRL2_TIE               BIT(4)
+#define RV3028_CTRL2_UIE               BIT(5)
+#define RV3028_CTRL2_TSE               BIT(7)
+
+#define RV3028_EVT_CTRL_TSR            BIT(2)
+
+#define RV3028_EEPROM_CMD_WRITE                0x21
+#define RV3028_EEPROM_CMD_READ         0x22
+
+#define RV3028_EEBUSY_POLL             10000
+#define RV3028_EEBUSY_TIMEOUT          100000
+
+#define RV3028_BACKUP_TCE              BIT(5)
+#define RV3028_BACKUP_TCR_MASK         GENMASK(1,0)
+
+#define OFFSET_STEP_PPT                        953674
+
+enum rv3028_type {
+       rv_3028,
+};
+
+struct rv3028_data {
+       struct regmap *regmap;
+       struct rtc_device *rtc;
+       enum rv3028_type type;
+};
+
+static u16 rv3028_trickle_resistors[] = {1000, 3000, 6000, 11000};
+
+static ssize_t timestamp0_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
+
+       regmap_update_bits(rv3028->regmap, RV3028_EVT_CTRL, RV3028_EVT_CTRL_TSR,
+                          RV3028_EVT_CTRL_TSR);
+
+       return count;
+};
+
+static ssize_t timestamp0_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
+       struct rtc_time tm;
+       int ret, count;
+       u8 date[6];
+
+       ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
+       if (ret)
+               return ret;
+
+       if (!count)
+               return 0;
+
+       ret = regmap_bulk_read(rv3028->regmap, RV3028_TS_SEC, date,
+                              sizeof(date));
+       if (ret)
+               return ret;
+
+       tm.tm_sec = bcd2bin(date[0]);
+       tm.tm_min = bcd2bin(date[1]);
+       tm.tm_hour = bcd2bin(date[2]);
+       tm.tm_mday = bcd2bin(date[3]);
+       tm.tm_mon = bcd2bin(date[4]) - 1;
+       tm.tm_year = bcd2bin(date[5]) + 100;
+
+       ret = rtc_valid_tm(&tm);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%llu\n",
+                      (unsigned long long)rtc_tm_to_time64(&tm));
+};
+
+static DEVICE_ATTR_RW(timestamp0);
+
+static ssize_t timestamp0_count_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
+       int ret, count;
+
+       ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%u\n", count);
+};
+
+static DEVICE_ATTR_RO(timestamp0_count);
+
+static struct attribute *rv3028_attrs[] = {
+       &dev_attr_timestamp0.attr,
+       &dev_attr_timestamp0_count.attr,
+       NULL
+};
+
+static const struct attribute_group rv3028_attr_group = {
+       .attrs  = rv3028_attrs,
+};
+
+static irqreturn_t rv3028_handle_irq(int irq, void *dev_id)
+{
+       struct rv3028_data *rv3028 = dev_id;
+       unsigned long events = 0;
+       u32 status = 0, ctrl = 0;
+
+       if (regmap_read(rv3028->regmap, RV3028_STATUS, &status) < 0 ||
+          status == 0) {
+               return IRQ_NONE;
+       }
+
+       if (status & RV3028_STATUS_PORF)
+               dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n");
+
+       if (status & RV3028_STATUS_TF) {
+               status |= RV3028_STATUS_TF;
+               ctrl |= RV3028_CTRL2_TIE;
+               events |= RTC_PF;
+       }
+
+       if (status & RV3028_STATUS_AF) {
+               status |= RV3028_STATUS_AF;
+               ctrl |= RV3028_CTRL2_AIE;
+               events |= RTC_AF;
+       }
+
+       if (status & RV3028_STATUS_UF) {
+               status |= RV3028_STATUS_UF;
+               ctrl |= RV3028_CTRL2_UIE;
+               events |= RTC_UF;
+       }
+
+       if (events) {
+               rtc_update_irq(rv3028->rtc, 1, events);
+               regmap_update_bits(rv3028->regmap, RV3028_STATUS, status, 0);
+               regmap_update_bits(rv3028->regmap, RV3028_CTRL2, ctrl, 0);
+       }
+
+       if (status & RV3028_STATUS_EVF) {
+               sysfs_notify(&rv3028->rtc->dev.kobj, NULL,
+                            dev_attr_timestamp0.attr.name);
+               dev_warn(&rv3028->rtc->dev, "event detected");
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int rv3028_get_time(struct device *dev, struct rtc_time *tm)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       u8 date[7];
+       int ret, status;
+
+       ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       if (status & RV3028_STATUS_PORF) {
+               dev_warn(dev, "Voltage low, data is invalid.\n");
+               return -EINVAL;
+       }
+
+       ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date));
+       if (ret)
+               return ret;
+
+       tm->tm_sec  = bcd2bin(date[RV3028_SEC] & 0x7f);
+       tm->tm_min  = bcd2bin(date[RV3028_MIN] & 0x7f);
+       tm->tm_hour = bcd2bin(date[RV3028_HOUR] & 0x3f);
+       tm->tm_wday = ilog2(date[RV3028_WDAY] & 0x7f);
+       tm->tm_mday = bcd2bin(date[RV3028_DAY] & 0x3f);
+       tm->tm_mon  = bcd2bin(date[RV3028_MONTH] & 0x1f) - 1;
+       tm->tm_year = bcd2bin(date[RV3028_YEAR]) + 100;
+
+       return 0;
+}
+
+static int rv3028_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       u8 date[7];
+       int ret;
+
+       date[RV3028_SEC]   = bin2bcd(tm->tm_sec);
+       date[RV3028_MIN]   = bin2bcd(tm->tm_min);
+       date[RV3028_HOUR]  = bin2bcd(tm->tm_hour);
+       date[RV3028_WDAY]  = 1 << (tm->tm_wday);
+       date[RV3028_DAY]   = bin2bcd(tm->tm_mday);
+       date[RV3028_MONTH] = bin2bcd(tm->tm_mon + 1);
+       date[RV3028_YEAR]  = bin2bcd(tm->tm_year - 100);
+
+       /*
+        * Writing to the Seconds register has the same effect as setting RESET
+        * bit to 1
+        */
+       ret = regmap_bulk_write(rv3028->regmap, RV3028_SEC, date,
+                               sizeof(date));
+       if (ret)
+               return ret;
+
+       ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+                                RV3028_STATUS_PORF, 0);
+
+       return ret;
+}
+
+static int rv3028_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       u8 alarmvals[3];
+       int status, ctrl, ret;
+
+       ret = regmap_bulk_read(rv3028->regmap, RV3028_ALARM_MIN, alarmvals,
+                              sizeof(alarmvals));
+       if (ret)
+               return ret;
+
+       ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read(rv3028->regmap, RV3028_CTRL2, &ctrl);
+       if (ret < 0)
+               return ret;
+
+       alrm->time.tm_sec  = 0;
+       alrm->time.tm_min  = bcd2bin(alarmvals[0] & 0x7f);
+       alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);
+       alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f);
+
+       alrm->enabled = !!(ctrl & RV3028_CTRL2_AIE);
+       alrm->pending = (status & RV3028_STATUS_AF) && alrm->enabled;
+
+       return 0;
+}
+
+static int rv3028_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       u8 alarmvals[3];
+       u8 ctrl = 0;
+       int ret;
+
+       /* The alarm has no seconds, round up to nearest minute */
+       if (alrm->time.tm_sec) {
+               time64_t alarm_time = rtc_tm_to_time64(&alrm->time);
+
+               alarm_time += 60 - alrm->time.tm_sec;
+               rtc_time64_to_tm(alarm_time, &alrm->time);
+       }
+
+       ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+                                RV3028_CTRL2_AIE | RV3028_CTRL2_UIE, 0);
+       if (ret)
+               return ret;
+
+       alarmvals[0] = bin2bcd(alrm->time.tm_min);
+       alarmvals[1] = bin2bcd(alrm->time.tm_hour);
+       alarmvals[2] = bin2bcd(alrm->time.tm_mday);
+
+       ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+                                RV3028_STATUS_AF, 0);
+       if (ret)
+               return ret;
+
+       ret = regmap_bulk_write(rv3028->regmap, RV3028_ALARM_MIN, alarmvals,
+                               sizeof(alarmvals));
+       if (ret)
+               return ret;
+
+       if (alrm->enabled) {
+               if (rv3028->rtc->uie_rtctimer.enabled)
+                       ctrl |= RV3028_CTRL2_UIE;
+               if (rv3028->rtc->aie_timer.enabled)
+                       ctrl |= RV3028_CTRL2_AIE;
+       }
+
+       ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+                                RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl);
+
+       return ret;
+}
+
+static int rv3028_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       int ctrl = 0, ret;
+
+       if (enabled) {
+               if (rv3028->rtc->uie_rtctimer.enabled)
+                       ctrl |= RV3028_CTRL2_UIE;
+               if (rv3028->rtc->aie_timer.enabled)
+                       ctrl |= RV3028_CTRL2_AIE;
+       }
+
+       ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+                                RV3028_STATUS_AF | RV3028_STATUS_UF, 0);
+       if (ret)
+               return ret;
+
+       ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+                                RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rv3028_read_offset(struct device *dev, long *offset)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       int ret, value, steps;
+
+       ret = regmap_read(rv3028->regmap, RV3028_OFFSET, &value);
+       if (ret < 0)
+               return ret;
+
+       steps = sign_extend32(value << 1, 8);
+
+       ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value);
+       if (ret < 0)
+               return ret;
+
+       steps += value >> 7;
+
+       *offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000);
+
+       return 0;
+}
+
+static int rv3028_set_offset(struct device *dev, long offset)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       int ret;
+
+       offset = clamp(offset, -244141L, 243187L) * 1000;
+       offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT);
+
+       ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1);
+       if (ret < 0)
+               return ret;
+
+       return regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7),
+                                 offset << 7);
+}
+
+static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       int status, ret = 0;
+
+       switch (cmd) {
+       case RTC_VL_READ:
+               ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+               if (ret < 0)
+                       return ret;
+
+               if (status & RV3028_STATUS_PORF)
+                       dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n");
+
+               status &= RV3028_STATUS_PORF;
+
+               if (copy_to_user((void __user *)arg, &status, sizeof(int)))
+                       return -EFAULT;
+
+               return 0;
+
+       case RTC_VL_CLR:
+               ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+                                        RV3028_STATUS_PORF, 0);
+
+               return ret;
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+static int rv3028_nvram_write(void *priv, unsigned int offset, void *val,
+                             size_t bytes)
+{
+       return regmap_bulk_write(priv, RV3028_RAM1 + offset, val, bytes);
+}
+
+static int rv3028_nvram_read(void *priv, unsigned int offset, void *val,
+                            size_t bytes)
+{
+       return regmap_bulk_read(priv, RV3028_RAM1 + offset, val, bytes);
+}
+
+static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val,
+                              size_t bytes)
+{
+       u32 status, ctrl1;
+       int i, ret, err;
+       u8 *buf = val;
+
+       ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
+       if (ret)
+               return ret;
+
+       if (!(ctrl1 & RV3028_CTRL1_EERD)) {
+               ret = regmap_update_bits(priv, RV3028_CTRL1,
+                                        RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
+               if (ret)
+                       return ret;
+
+               ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+                                              !(status & RV3028_STATUS_EEBUSY),
+                                              RV3028_EEBUSY_POLL,
+                                              RV3028_EEBUSY_TIMEOUT);
+               if (ret)
+                       goto restore_eerd;
+       }
+
+       for (i = 0; i < bytes; i++) {
+               ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
+               if (ret)
+                       goto restore_eerd;
+
+               ret = regmap_write(priv, RV3028_EEPROM_DATA, buf[i]);
+               if (ret)
+                       goto restore_eerd;
+
+               ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
+               if (ret)
+                       goto restore_eerd;
+
+               ret = regmap_write(priv, RV3028_EEPROM_CMD,
+                                  RV3028_EEPROM_CMD_WRITE);
+               if (ret)
+                       goto restore_eerd;
+
+               usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT);
+
+               ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+                                              !(status & RV3028_STATUS_EEBUSY),
+                                              RV3028_EEBUSY_POLL,
+                                              RV3028_EEBUSY_TIMEOUT);
+               if (ret)
+                       goto restore_eerd;
+       }
+
+restore_eerd:
+       if (!(ctrl1 & RV3028_CTRL1_EERD))
+       {
+               err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
+                                        0);
+               if (err && !ret)
+                       ret = err;
+       }
+
+       return ret;
+}
+
+static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val,
+                             size_t bytes)
+{
+       u32 status, ctrl1, data;
+       int i, ret, err;
+       u8 *buf = val;
+
+       ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
+       if (ret)
+               return ret;
+
+       if (!(ctrl1 & RV3028_CTRL1_EERD)) {
+               ret = regmap_update_bits(priv, RV3028_CTRL1,
+                                        RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
+               if (ret)
+                       return ret;
+
+               ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+                                              !(status & RV3028_STATUS_EEBUSY),
+                                              RV3028_EEBUSY_POLL,
+                                              RV3028_EEBUSY_TIMEOUT);
+               if (ret)
+                       goto restore_eerd;
+       }
+
+       for (i = 0; i < bytes; i++) {
+               ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
+               if (ret)
+                       goto restore_eerd;
+
+               ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
+               if (ret)
+                       goto restore_eerd;
+
+               ret = regmap_write(priv, RV3028_EEPROM_CMD,
+                                  RV3028_EEPROM_CMD_READ);
+               if (ret)
+                       goto restore_eerd;
+
+               ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+                                              !(status & RV3028_STATUS_EEBUSY),
+                                              RV3028_EEBUSY_POLL,
+                                              RV3028_EEBUSY_TIMEOUT);
+               if (ret)
+                       goto restore_eerd;
+
+               ret = regmap_read(priv, RV3028_EEPROM_DATA, &data);
+               if (ret)
+                       goto restore_eerd;
+               buf[i] = data;
+       }
+
+restore_eerd:
+       if (!(ctrl1 & RV3028_CTRL1_EERD))
+       {
+               err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
+                                        0);
+               if (err && !ret)
+                       ret = err;
+       }
+
+       return ret;
+}
+
+static struct rtc_class_ops rv3028_rtc_ops = {
+       .read_time = rv3028_get_time,
+       .set_time = rv3028_set_time,
+       .read_offset = rv3028_read_offset,
+       .set_offset = rv3028_set_offset,
+       .ioctl = rv3028_ioctl,
+};
+
+static const struct regmap_config regmap_config = {
+        .reg_bits = 8,
+        .val_bits = 8,
+        .max_register = 0x37,
+};
+
+static int rv3028_probe(struct i2c_client *client)
+{
+       struct rv3028_data *rv3028;
+       int ret, status;
+       u32 ohms;
+       struct nvmem_config nvmem_cfg = {
+               .name = "rv3028_nvram",
+               .word_size = 1,
+               .stride = 1,
+               .size = 2,
+               .type = NVMEM_TYPE_BATTERY_BACKED,
+               .reg_read = rv3028_nvram_read,
+               .reg_write = rv3028_nvram_write,
+       };
+       struct nvmem_config eeprom_cfg = {
+               .name = "rv3028_eeprom",
+               .word_size = 1,
+               .stride = 1,
+               .size = 43,
+               .type = NVMEM_TYPE_EEPROM,
+               .reg_read = rv3028_eeprom_read,
+               .reg_write = rv3028_eeprom_write,
+       };
+
+       rv3028 = devm_kzalloc(&client->dev, sizeof(struct rv3028_data),
+                             GFP_KERNEL);
+       if (!rv3028)
+               return -ENOMEM;
+
+       rv3028->regmap = devm_regmap_init_i2c(client, &regmap_config);
+
+       i2c_set_clientdata(client, rv3028);
+
+       ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       if (status & RV3028_STATUS_PORF)
+               dev_warn(&client->dev, "Voltage low, data loss detected.\n");
+
+       if (status & RV3028_STATUS_AF)
+               dev_warn(&client->dev, "An alarm may have been missed.\n");
+
+       rv3028->rtc = devm_rtc_allocate_device(&client->dev);
+       if (IS_ERR(rv3028->rtc)) {
+               return PTR_ERR(rv3028->rtc);
+       }
+
+       if (client->irq > 0) {
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                               NULL, rv3028_handle_irq,
+                                               IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                               "rv3028", rv3028);
+               if (ret) {
+                       dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
+                       client->irq = 0;
+               } else {
+                       rv3028_rtc_ops.read_alarm = rv3028_get_alarm;
+                       rv3028_rtc_ops.set_alarm = rv3028_set_alarm;
+                       rv3028_rtc_ops.alarm_irq_enable = rv3028_alarm_irq_enable;
+               }
+       }
+
+       ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1,
+                                RV3028_CTRL1_WADA, RV3028_CTRL1_WADA);
+       if (ret)
+               return ret;
+
+       /* setup timestamping */
+       ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+                                RV3028_CTRL2_EIE | RV3028_CTRL2_TSE,
+                                RV3028_CTRL2_EIE | RV3028_CTRL2_TSE);
+       if (ret)
+               return ret;
+
+       /* setup trickle charger */
+       if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms",
+                                     &ohms)) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++)
+                       if (ohms == rv3028_trickle_resistors[i])
+                               break;
+
+               if (i < ARRAY_SIZE(rv3028_trickle_resistors)) {
+                       ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
+                                                RV3028_BACKUP_TCE |
+                                                RV3028_BACKUP_TCR_MASK,
+                                                RV3028_BACKUP_TCE | i);
+                       if (ret)
+                               return ret;
+               } else {
+                       dev_warn(&client->dev, "invalid trickle resistor value\n");
+               }
+       }
+
+       ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group);
+       if (ret)
+               return ret;
+
+       rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+       rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099;
+       rv3028->rtc->ops = &rv3028_rtc_ops;
+       ret = rtc_register_device(rv3028->rtc);
+       if (ret)
+               return ret;
+
+       nvmem_cfg.priv = rv3028->regmap;
+       rtc_nvmem_register(rv3028->rtc, &nvmem_cfg);
+       eeprom_cfg.priv = rv3028->regmap;
+       rtc_nvmem_register(rv3028->rtc, &eeprom_cfg);
+
+       rv3028->rtc->max_user_freq = 1;
+
+       return 0;
+}
+
+static const struct of_device_id rv3028_of_match[] = {
+       { .compatible = "microcrystal,rv3028", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, rv3028_of_match);
+
+static struct i2c_driver rv3028_driver = {
+       .driver = {
+               .name = "rtc-rv3028",
+               .of_match_table = of_match_ptr(rv3028_of_match),
+       },
+       .probe_new      = rv3028_probe,
+};
+module_i2c_driver(rv3028_driver);
+
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
+MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver");
+MODULE_LICENSE("GPL v2");
index 450a0b8..0b102c3 100644 (file)
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * RTC driver for the Micro Crystal RV8803
  *
  * Copyright (C) 2015 Micro Crystal SA
- *
- * Alexandre Belloni <alexandre.belloni@free-electrons.com>
- *
- * 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.
+ * Alexandre Belloni <alexandre.belloni@bootlin.com>
  *
  */
 
@@ -236,9 +232,6 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm)
        u8 date[7];
        int ctrl, flags, ret;
 
-       if ((tm->tm_year < 100) || (tm->tm_year > 199))
-               return -EINVAL;
-
        ctrl = rv8803_read_reg(rv8803->client, RV8803_CTRL);
        if (ctrl < 0)
                return ctrl;
@@ -602,6 +595,8 @@ static int rv8803_probe(struct i2c_client *client,
 
        rv8803->rtc->ops = &rv8803_rtc_ops;
        rv8803->rtc->nvram_old_abi = true;
+       rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+       rv8803->rtc->range_max = RTC_TIMESTAMP_END_2099;
        err = rtc_register_device(rv8803->rtc);
        if (err)
                return err;
@@ -648,6 +643,6 @@ static struct i2c_driver rv8803_driver = {
 };
 module_i2c_driver(rv8803_driver);
 
-MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
 MODULE_DESCRIPTION("Micro Crystal RV8803 RTC driver");
 MODULE_LICENSE("GPL v2");
index eac8821..776e3a2 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/regmap.h>
 #include <linux/rtc.h>
 #include <linux/log2.h>
 #define RX8581_CTRL_STOP       0x02 /* STOP bit */
 #define RX8581_CTRL_RESET      0x01 /* RESET bit */
 
+#define RX8571_USER_RAM                0x10
+#define RX8571_NVRAM_SIZE      0x10
+
 struct rx8581 {
        struct regmap           *regmap;
        struct rtc_device       *rtc;
 };
 
+struct rx85x1_config {
+       struct regmap_config regmap;
+       unsigned int num_nvram;
+};
+
 /*
  * In the routines that deal directly with the rx8581 hardware, we use
  * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
@@ -181,25 +191,103 @@ static const struct rtc_class_ops rx8581_rtc_ops = {
        .set_time       = rx8581_rtc_set_time,
 };
 
-static int rx8581_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
+static int rx8571_nvram_read(void *priv, unsigned int offset, void *val,
+                            size_t bytes)
 {
-       struct rx8581     *rx8581;
-       static const struct regmap_config config = {
+       struct rx8581 *rx8581 = priv;
+
+       return regmap_bulk_read(rx8581->regmap, RX8571_USER_RAM + offset,
+                               val, bytes);
+}
+
+static int rx8571_nvram_write(void *priv, unsigned int offset, void *val,
+                             size_t bytes)
+{
+       struct rx8581 *rx8581 = priv;
+
+       return regmap_bulk_write(rx8581->regmap, RX8571_USER_RAM + offset,
+                                val, bytes);
+}
+
+static int rx85x1_nvram_read(void *priv, unsigned int offset, void *val,
+                            size_t bytes)
+{
+       struct rx8581 *rx8581 = priv;
+       unsigned int tmp_val;
+       int ret;
+
+       ret = regmap_read(rx8581->regmap, RX8581_REG_RAM, &tmp_val);
+       (*(unsigned char *)val) = (unsigned char) tmp_val;
+
+       return ret;
+}
+
+static int rx85x1_nvram_write(void *priv, unsigned int offset, void *val,
+                             size_t bytes)
+{
+       struct rx8581 *rx8581 = priv;
+       unsigned char tmp_val;
+
+       tmp_val = *((unsigned char *)val);
+       return regmap_write(rx8581->regmap, RX8581_REG_RAM,
+                               (unsigned int)tmp_val);
+}
+
+static const struct rx85x1_config rx8581_config = {
+       .regmap = {
                .reg_bits = 8,
                .val_bits = 8,
                .max_register = 0xf,
+       },
+       .num_nvram = 1
+};
+
+static const struct rx85x1_config rx8571_config = {
+       .regmap = {
+               .reg_bits = 8,
+               .val_bits = 8,
+               .max_register = 0x1f,
+       },
+       .num_nvram = 2
+};
+
+static int rx8581_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct rx8581 *rx8581;
+       const struct rx85x1_config *config = &rx8581_config;
+       const void *data = of_device_get_match_data(&client->dev);
+       static struct nvmem_config nvmem_cfg[] = {
+               {
+                       .name = "rx85x1-",
+                       .word_size = 1,
+                       .stride = 1,
+                       .size = 1,
+                       .reg_read = rx85x1_nvram_read,
+                       .reg_write = rx85x1_nvram_write,
+               }, {
+                       .name = "rx8571-",
+                       .word_size = 1,
+                       .stride = 1,
+                       .size = RX8571_NVRAM_SIZE,
+                       .reg_read = rx8571_nvram_read,
+                       .reg_write = rx8571_nvram_write,
+               },
        };
+       int ret, i;
 
        dev_dbg(&client->dev, "%s\n", __func__);
 
+       if (data)
+               config = data;
+
        rx8581 = devm_kzalloc(&client->dev, sizeof(struct rx8581), GFP_KERNEL);
        if (!rx8581)
                return -ENOMEM;
 
        i2c_set_clientdata(client, rx8581);
 
-       rx8581->regmap = devm_regmap_init_i2c(client, &config);
+       rx8581->regmap = devm_regmap_init_i2c(client, &config->regmap);
        if (IS_ERR(rx8581->regmap))
                return PTR_ERR(rx8581->regmap);
 
@@ -213,7 +301,14 @@ static int rx8581_probe(struct i2c_client *client,
        rx8581->rtc->start_secs = 0;
        rx8581->rtc->set_start_time = true;
 
-       return rtc_register_device(rx8581->rtc);
+       ret = rtc_register_device(rx8581->rtc);
+
+       for (i = 0; i < config->num_nvram; i++) {
+               nvmem_cfg[i].priv = rx8581;
+               rtc_nvmem_register(rx8581->rtc, &nvmem_cfg[i]);
+       }
+
+       return ret;
 }
 
 static const struct i2c_device_id rx8581_id[] = {
@@ -223,8 +318,9 @@ static const struct i2c_device_id rx8581_id[] = {
 MODULE_DEVICE_TABLE(i2c, rx8581_id);
 
 static const struct of_device_id rx8581_of_match[] = {
-       { .compatible = "epson,rx8581" },
-       { }
+       { .compatible = "epson,rx8571", .data = &rx8571_config },
+       { .compatible = "epson,rx8581", .data = &rx8581_config },
+       { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, rx8581_of_match);
 
@@ -240,5 +336,5 @@ static struct i2c_driver rx8581_driver = {
 module_i2c_driver(rx8581_driver);
 
 MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
-MODULE_DESCRIPTION("Epson RX-8581 RTC driver");
+MODULE_DESCRIPTION("Epson RX-8571/RX-8581 RTC driver");
 MODULE_LICENSE("GPL");
index 04c6817..e81a2b2 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/log2.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
 
@@ -39,7 +40,7 @@ struct s3c_rtc {
        void __iomem *base;
        struct clk *rtc_clk;
        struct clk *rtc_src_clk;
-       bool clk_disabled;
+       bool alarm_enabled;
 
        const struct s3c_rtc_data *data;
 
@@ -47,7 +48,7 @@ struct s3c_rtc {
        int irq_tick;
 
        spinlock_t pie_lock;
-       spinlock_t alarm_clk_lock;
+       spinlock_t alarm_lock;
 
        int ticnt_save;
        int ticnt_en_save;
@@ -70,44 +71,27 @@ struct s3c_rtc_data {
 
 static int s3c_rtc_enable_clk(struct s3c_rtc *info)
 {
-       unsigned long irq_flags;
-       int ret = 0;
+       int ret;
 
-       spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
+       ret = clk_enable(info->rtc_clk);
+       if (ret)
+               return ret;
 
-       if (info->clk_disabled) {
-               ret = clk_enable(info->rtc_clk);
-               if (ret)
-                       goto out;
-
-               if (info->data->needs_src_clk) {
-                       ret = clk_enable(info->rtc_src_clk);
-                       if (ret) {
-                               clk_disable(info->rtc_clk);
-                               goto out;
-                       }
+       if (info->data->needs_src_clk) {
+               ret = clk_enable(info->rtc_src_clk);
+               if (ret) {
+                       clk_disable(info->rtc_clk);
+                       return ret;
                }
-               info->clk_disabled = false;
        }
-
-out:
-       spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
-
-       return ret;
+       return 0;
 }
 
 static void s3c_rtc_disable_clk(struct s3c_rtc *info)
 {
-       unsigned long irq_flags;
-
-       spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
-       if (!info->clk_disabled) {
-               if (info->data->needs_src_clk)
-                       clk_disable(info->rtc_src_clk);
-               clk_disable(info->rtc_clk);
-               info->clk_disabled = true;
-       }
-       spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
+       if (info->data->needs_src_clk)
+               clk_disable(info->rtc_src_clk);
+       clk_disable(info->rtc_clk);
 }
 
 /* IRQ Handlers */
@@ -135,6 +119,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
 static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
 {
        struct s3c_rtc *info = dev_get_drvdata(dev);
+       unsigned long flags;
        unsigned int tmp;
        int ret;
 
@@ -151,17 +136,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
 
        writeb(tmp, info->base + S3C2410_RTCALM);
 
-       s3c_rtc_disable_clk(info);
+       spin_lock_irqsave(&info->alarm_lock, flags);
 
-       if (enabled) {
-               ret = s3c_rtc_enable_clk(info);
-               if (ret)
-                       return ret;
-       } else {
+       if (info->alarm_enabled && !enabled)
                s3c_rtc_disable_clk(info);
-       }
+       else if (!info->alarm_enabled && enabled)
+               ret = s3c_rtc_enable_clk(info);
 
-       return 0;
+       info->alarm_enabled = enabled;
+       spin_unlock_irqrestore(&info->alarm_lock, flags);
+
+       s3c_rtc_disable_clk(info);
+
+       return ret;
 }
 
 /* Set RTC frequency */
@@ -357,10 +344,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        writeb(alrm_en, info->base + S3C2410_RTCALM);
 
-       s3c_rtc_disable_clk(info);
-
        s3c_rtc_setaie(dev, alrm->enabled);
 
+       s3c_rtc_disable_clk(info);
+
        return 0;
 }
 
@@ -456,16 +443,6 @@ static int s3c_rtc_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id s3c_rtc_dt_match[];
-
-static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
-{
-       const struct of_device_id *match;
-
-       match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
-       return match->data;
-}
-
 static int s3c_rtc_probe(struct platform_device *pdev)
 {
        struct s3c_rtc *info = NULL;
@@ -485,13 +462,13 @@ static int s3c_rtc_probe(struct platform_device *pdev)
        }
 
        info->dev = &pdev->dev;
-       info->data = s3c_rtc_get_data(pdev);
+       info->data = of_device_get_match_data(&pdev->dev);
        if (!info->data) {
                dev_err(&pdev->dev, "failed getting s3c_rtc_data\n");
                return -EINVAL;
        }
        spin_lock_init(&info->pie_lock);
-       spin_lock_init(&info->alarm_clk_lock);
+       spin_lock_init(&info->alarm_lock);
 
        platform_set_drvdata(pdev, info);
 
@@ -591,6 +568,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
 
        s3c_rtc_setfreq(info, 1);
 
+       s3c_rtc_disable_clk(info);
+
        return 0;
 
 err_nortc:
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
new file mode 100644 (file)
index 0000000..42cb90d
--- /dev/null
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock (RTC) Driver for sd3078
+ * Copyright (C) 2018 Zoro Li
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define SD3078_REG_SC                  0x00
+#define SD3078_REG_MN                  0x01
+#define SD3078_REG_HR                  0x02
+#define SD3078_REG_DW                  0x03
+#define SD3078_REG_DM                  0x04
+#define SD3078_REG_MO                  0x05
+#define SD3078_REG_YR                  0x06
+
+#define SD3078_REG_CTRL1               0x0f
+#define SD3078_REG_CTRL2               0x10
+#define SD3078_REG_CTRL3               0x11
+
+#define KEY_WRITE1             0x80
+#define KEY_WRITE2             0x04
+#define KEY_WRITE3             0x80
+
+#define NUM_TIME_REGS   (SD3078_REG_YR - SD3078_REG_SC + 1)
+
+/*
+ * The sd3078 has write protection
+ * and we can choose whether or not to use it.
+ * Write protection is turned off by default.
+ */
+#define WRITE_PROTECT_EN       0
+
+struct sd3078 {
+       struct rtc_device       *rtc;
+       struct regmap           *regmap;
+};
+
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * when modification of the register,
+ * the "write" bit needs to be written in a certain order.
+ * 1. set WRITE1 bit
+ * 2. set WRITE2 bit
+ * 3. set WRITE3 bit
+ */
+static void sd3078_enable_reg_write(struct sd3078 *sd3078)
+{
+       regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+                          KEY_WRITE1, KEY_WRITE1);
+       regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+                          KEY_WRITE2, KEY_WRITE2);
+       regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+                          KEY_WRITE3, KEY_WRITE3);
+}
+
+#if WRITE_PROTECT_EN
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * we should disable the write function.
+ * when disable write,
+ * the "write" bit needs to be clear in a certain order.
+ * 1. clear WRITE2 bit
+ * 2. clear WRITE3 bit
+ * 3. clear WRITE1 bit
+ */
+static void sd3078_disable_reg_write(struct sd3078 *sd3078)
+{
+       regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+                          KEY_WRITE2, 0);
+       regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+                          KEY_WRITE3, 0);
+       regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+                          KEY_WRITE1, 0);
+}
+#endif
+
+static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       unsigned char hour;
+       unsigned char rtc_data[NUM_TIME_REGS] = {0};
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sd3078 *sd3078 = i2c_get_clientdata(client);
+       int ret;
+
+       ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data,
+                              NUM_TIME_REGS);
+       if (ret < 0) {
+               dev_err(dev, "reading from RTC failed with err:%d\n", ret);
+               return ret;
+       }
+
+       tm->tm_sec      = bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F);
+       tm->tm_min      = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F);
+
+       /*
+        * The sd3078 supports 12/24 hour mode.
+        * When getting time,
+        * we need to convert the 12 hour mode to the 24 hour mode.
+        */
+       hour = rtc_data[SD3078_REG_HR];
+       if (hour & 0x80) /* 24H MODE */
+               tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F);
+       else if (hour & 0x20) /* 12H MODE PM */
+               tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12;
+       else /* 12H MODE AM */
+               tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F);
+
+       tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F);
+       tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07;
+       tm->tm_mon      = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1;
+       tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100;
+
+       return 0;
+}
+
+static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       unsigned char rtc_data[NUM_TIME_REGS];
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sd3078 *sd3078 = i2c_get_clientdata(client);
+       int ret;
+
+       rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
+       rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min);
+       rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
+       rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
+       rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07;
+       rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
+       rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100);
+
+#if WRITE_PROTECT_EN
+       sd3078_enable_reg_write(sd3078);
+#endif
+
+       ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data,
+                               NUM_TIME_REGS);
+       if (ret < 0) {
+               dev_err(dev, "writing to RTC failed with err:%d\n", ret);
+               return ret;
+       }
+
+#if WRITE_PROTECT_EN
+       sd3078_disable_reg_write(sd3078);
+#endif
+
+       return 0;
+}
+
+static const struct rtc_class_ops sd3078_rtc_ops = {
+       .read_time      = sd3078_rtc_read_time,
+       .set_time       = sd3078_rtc_set_time,
+};
+
+static const struct regmap_config regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 0x11,
+};
+
+static int sd3078_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int ret;
+       struct sd3078 *sd3078;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               return -ENODEV;
+
+       sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
+       if (!sd3078)
+               return -ENOMEM;
+
+       sd3078->regmap = devm_regmap_init_i2c(client, &regmap_config);
+       if (IS_ERR(sd3078->regmap)) {
+               dev_err(&client->dev, "regmap allocation failed\n");
+               return PTR_ERR(sd3078->regmap);
+       }
+
+       i2c_set_clientdata(client, sd3078);
+
+       sd3078->rtc = devm_rtc_allocate_device(&client->dev);
+       if (IS_ERR(sd3078->rtc))
+               return PTR_ERR(sd3078->rtc);
+
+       sd3078->rtc->ops = &sd3078_rtc_ops;
+       sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+       sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+       ret = rtc_register_device(sd3078->rtc);
+       if (ret) {
+               dev_err(&client->dev, "failed to register rtc device\n");
+               return ret;
+       }
+
+       sd3078_enable_reg_write(sd3078);
+
+       return 0;
+}
+
+static const struct i2c_device_id sd3078_id[] = {
+       {"sd3078", 0},
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, sd3078_id);
+
+static const struct of_device_id rtc_dt_match[] = {
+       { .compatible = "whwave,sd3078" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtc_dt_match);
+
+static struct i2c_driver sd3078_driver = {
+       .driver     = {
+               .name   = "sd3078",
+               .of_match_table = of_match_ptr(rtc_dt_match),
+       },
+       .probe      = sd3078_probe,
+       .id_table   = sd3078_id,
+};
+
+module_i2c_driver(sd3078_driver);
+
+MODULE_AUTHOR("Dianlong Li <long17.cool@163.com>");
+MODULE_DESCRIPTION("SD3078 RTC driver");
+MODULE_LICENSE("GPL v2");
index b2483a7..0b9eff1 100644 (file)
@@ -239,6 +239,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
        u32 lpsr;
        u32 events = 0;
 
+       if (data->clk)
+               clk_enable(data->clk);
+
        regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
 
        if (lpsr & SNVS_LPSR_LPTA) {
@@ -253,6 +256,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
        /* clear interrupt status */
        regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
 
+       if (data->clk)
+               clk_disable(data->clk);
+
        return events ? IRQ_HANDLED : IRQ_NONE;
 }
 
index 61c110b..2d24bab 100644 (file)
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * TX4939 internal RTC driver
  * Based on RBTX49xx patch from CELF patch archive.
  *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
  * (C) Copyright TOSHIBA CORPORATION 2005-2007
  */
 #include <linux/rtc.h>
@@ -65,10 +62,11 @@ static int tx4939_rtc_cmd(struct tx4939_rtc_reg __iomem *rtcreg, int cmd)
        return 0;
 }
 
-static int tx4939_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int tx4939_rtc_set_time(struct device *dev, struct rtc_time *tm)
 {
        struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
        struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+       unsigned long secs = rtc_tm_to_time64(tm);
        int i, ret;
        unsigned char buf[6];
 
@@ -111,7 +109,7 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
        spin_unlock_irq(&pdata->lock);
        sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
                (buf[3] << 8) | buf[2];
-       rtc_time_to_tm(sec, tm);
+       rtc_time64_to_tm(sec, tm);
        return 0;
 }
 
@@ -123,14 +121,7 @@ static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        unsigned long sec;
        unsigned char buf[6];
 
-       if (alrm->time.tm_sec < 0 ||
-           alrm->time.tm_min < 0 ||
-           alrm->time.tm_hour < 0 ||
-           alrm->time.tm_mday < 0 ||
-           alrm->time.tm_mon < 0 ||
-           alrm->time.tm_year < 0)
-               return -EINVAL;
-       rtc_tm_to_time(&alrm->time, &sec);
+       sec = rtc_tm_to_time64(&alrm->time);
        buf[0] = 0;
        buf[1] = 0;
        buf[2] = sec;
@@ -173,7 +164,7 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        spin_unlock_irq(&pdata->lock);
        sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
                (buf[3] << 8) | buf[2];
-       rtc_time_to_tm(sec, &alrm->time);
+       rtc_time64_to_tm(sec, &alrm->time);
        return rtc_valid_tm(&alrm->time);
 }
 
@@ -210,7 +201,7 @@ static const struct rtc_class_ops tx4939_rtc_ops = {
        .read_time              = tx4939_rtc_read_time,
        .read_alarm             = tx4939_rtc_read_alarm,
        .set_alarm              = tx4939_rtc_set_alarm,
-       .set_mmss               = tx4939_rtc_set_mmss,
+       .set_time               = tx4939_rtc_set_time,
        .alarm_irq_enable       = tx4939_rtc_alarm_irq_enable,
 };
 
@@ -283,6 +274,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
 
        rtc->ops = &tx4939_rtc_ops;
        rtc->nvram_old_abi = true;
+       rtc->range_max = U32_MAX;
 
        pdata->rtc = rtc;
 
@@ -315,5 +307,5 @@ module_platform_driver_probe(tx4939_rtc_driver, tx4939_rtc_probe);
 
 MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
 MODULE_DESCRIPTION("TX4939 internal RTC driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:tx4939rtc");
index c532bd1..bb95094 100644 (file)
@@ -49,7 +49,6 @@
 
 #define RTC_CALIB_DEF          0x198233
 #define RTC_CALIB_MASK         0x1FFFFF
-#define RTC_SEC_MAX_VAL                0xFFFFFFFF
 
 struct xlnx_rtc_dev {
        struct rtc_device       *rtc;
@@ -71,9 +70,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm)
         */
        new_time = rtc_tm_to_time64(tm) + 1;
 
-       if (new_time > RTC_SEC_MAX_VAL)
-               return -EINVAL;
-
        /*
         * Writing into calibration register will clear the Tick Counter and
         * force the next second to be signaled exactly in 1 second period
@@ -154,9 +150,6 @@ static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        alarm_time = rtc_tm_to_time64(&alrm->time);
 
-       if (alarm_time > RTC_SEC_MAX_VAL)
-               return -EINVAL;
-
        writel((u32)alarm_time, (xrtcdev->reg_base + RTC_ALRM));
 
        xlnx_rtc_alarm_irq_enable(dev, alrm->enabled);
@@ -222,6 +215,13 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, xrtcdev);
 
+       xrtcdev->rtc = devm_rtc_allocate_device(&pdev->dev);
+       if (IS_ERR(xrtcdev->rtc))
+               return PTR_ERR(xrtcdev->rtc);
+
+       xrtcdev->rtc->ops = &xlnx_rtc_ops;
+       xrtcdev->rtc->range_max = U32_MAX;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        xrtcdev->reg_base = devm_ioremap_resource(&pdev->dev, res);
@@ -263,9 +263,7 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
 
        device_init_wakeup(&pdev->dev, 1);
 
-       xrtcdev->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
-                                        &xlnx_rtc_ops, THIS_MODULE);
-       return PTR_ERR_OR_ZERO(xrtcdev->rtc);
+       return rtc_register_device(xrtcdev->rtc);
 }
 
 static int xlnx_rtc_remove(struct platform_device *pdev)
index c1089fe..f89bfbb 100644 (file)
@@ -67,7 +67,7 @@ extern struct class *rtc_class;
  *
  * The (current) exceptions are mostly filesystem hooks:
  *   - the proc() hook for procfs
- *   - non-ioctl() chardev hooks:  open(), release(), read_callback()
+ *   - non-ioctl() chardev hooks:  open(), release()
  *
  * REVISIT those periodic irq calls *do* have ops_lock when they're
  * issued through ioctl() ...
@@ -81,7 +81,6 @@ struct rtc_class_ops {
        int (*proc)(struct device *, struct seq_file *);
        int (*set_mmss64)(struct device *, time64_t secs);
        int (*set_mmss)(struct device *, unsigned long secs);
-       int (*read_callback)(struct device *, int data);
        int (*alarm_irq_enable)(struct device *, unsigned int enabled);
        int (*read_offset)(struct device *, long *offset);
        int (*set_offset)(struct device *, long offset);