Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 7d7be60..1adf9f8 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# RTC class/drivers configuration
#
@@ -185,8 +186,19 @@
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
help
If you say yes here you get support for Abracon AB080X and AB180X
families of ultra-low-power battery- and capacitor-backed real-time
@@ -361,12 +373,23 @@
This driver can also be built as a module. If so, the module
will be called rtc-max77686.
+config RTC_DRV_MESON_VRTC
+ tristate "Amlogic Meson Virtual RTC"
+ depends on ARCH_MESON || COMPILE_TEST
+ default m if ARCH_MESON
+ help
+ If you say yes here you will get support for the
+ Virtual RTC of Amlogic SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-meson-vrtc.
+
config RTC_DRV_RK808
- tristate "Rockchip RK805/RK808/RK818 RTC"
+ tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC"
depends on MFD_RK808
help
If you say yes here you will get support for the
- RTC of RK805, RK808 and RK818 PMIC.
+ RTC of RK805, RK809 and RK817, RK808 and RK818 PMIC.
This driver can also be built as a module. If so, the module
will be called rk808-rtc.
@@ -428,6 +451,7 @@
config RTC_DRV_PCF85063
tristate "NXP PCF85063"
+ select REGMAP_I2C
help
If you say yes here you get support for the PCF85063 RTC chip
@@ -436,7 +460,6 @@
config RTC_DRV_PCF85363
tristate "NXP PCF85363"
- depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for the PCF85363 RTC chip.
@@ -486,6 +509,15 @@
help
If you say Y here you will get support for the
watchdog timer in the ST M41T60 and M41T80 RTC chips series.
+config RTC_DRV_BD70528
+ tristate "ROHM BD70528 PMIC RTC"
+ depends on MFD_ROHM_BD70528 && (BD70528_WATCHDOG || !BD70528_WATCHDOG)
+ help
+ If you say Y here you will get support for the RTC
+ on ROHM BD70528 Power Management IC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-bd70528.
config RTC_DRV_BQ32K
tristate "TI BQ32000"
@@ -542,7 +574,7 @@
config RTC_DRV_TPS65910
tristate "TI TPS65910 RTC driver"
- depends on RTC_CLASS && MFD_TPS65910
+ depends on MFD_TPS65910
help
If you say yes here you get support for the RTC on the
TPS65910 chips.
@@ -591,7 +623,6 @@
config RTC_DRV_RX8010
tristate "Epson RX8010SJ"
- depends on I2C
help
If you say yes here you get support for the Epson RX8010SJ RTC
chip.
@@ -600,9 +631,10 @@
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.
@@ -625,6 +657,15 @@
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
@@ -645,6 +686,15 @@
This driver can also be built as a module. If so, the module
will be called rtc-s5m.
+config RTC_DRV_SD3078
+ tristate "ZXW Shenzhen whwave SD3078"
+ help
+ If you say yes here you get support for the ZXW Shenzhen whwave
+ 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"
@@ -782,6 +832,7 @@
config RTC_DRV_PCF2123
tristate "NXP PCF2123"
+ select REGMAP_SPI
help
If you say yes here you get support for the NXP PCF2123
RTC chip.
@@ -835,9 +886,15 @@
config RTC_DRV_PCF2127
tristate "NXP PCF2127"
depends on RTC_I2C_AND_SPI
+ select WATCHDOG_CORE if WATCHDOG
help
If you say yes here you get support for the NXP PCF2127/29 RTC
- chips.
+ chips with integrated quartz crystal for industrial applications.
+ Both chips also have watchdog timer and tamper switch detection
+ features.
+
+ PCF2127 has an additional feature of 512 bytes battery backed
+ memory that's accessible using nvmem interface.
This driver can also be built as a module. If so, the module
will be called rtc-pcf2127.
@@ -1007,17 +1064,6 @@
endchoice
-config RTC_DS1685_PROC_REGS
- bool "Display register values in /proc"
- depends on RTC_DRV_DS1685_FAMILY && PROC_FS
- help
- Enable this to display a readout of all of the RTC registers in
- /proc/drivers/rtc. Keep in mind that this can potentially lead
- to lost interrupts, as reading Control Register C will clear
- all pending IRQ flags.
-
- Unless you are debugging this driver, choose N.
-
config RTC_DRV_DS1742
tristate "Maxim/Dallas DS1742/1743"
depends on HAS_IOMEM
@@ -1219,13 +1265,6 @@
Select this to enable the ST-Ericsson AB8500 power management IC RTC
support. This chip contains a battery- and capacitor-backed RTC.
-config RTC_DRV_NUC900
- tristate "NUC910/NUC920 RTC driver"
- depends on ARCH_W90X900 || COMPILE_TEST
- help
- If you say yes here you get support for the RTC subsystem of the
- NUC910/NUC920 used in embedded systems.
-
config RTC_DRV_OPAL
tristate "IBM OPAL RTC driver"
depends on PPC_POWERNV
@@ -1246,7 +1285,7 @@
config RTC_DRV_CROS_EC
tristate "Chrome OS EC RTC driver"
- depends on MFD_CROS_EC
+ depends on CROS_EC
help
If you say yes here you will get support for the
Chrome OS Embedded Controller's RTC.
@@ -1295,6 +1334,32 @@
This driver can also be built as a module, if so, the module
will be called "rtc-imxdi".
+config RTC_DRV_FSL_FTM_ALARM
+ tristate "Freescale FlexTimer alarm timer"
+ depends on ARCH_LAYERSCAPE || SOC_LS1021A
+ select FSL_RCPM
+ default y
+ help
+ For the FlexTimer in LS1012A, LS1021A, LS1028A, LS1043A, LS1046A,
+ LS1088A, LS208xA, we can use FTM as the wakeup source.
+
+ Say y here to enable FTM alarm support. The FTM alarm provides
+ alarm functions for wakeup system from deep sleep.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-fsl-ftm-alarm".
+
+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
@@ -1402,7 +1467,7 @@
config RTC_DRV_AT91SAM9
tristate "AT91SAM9 RTT as RTC"
depends on ARCH_AT91 || COMPILE_TEST
- depends on HAS_IOMEM
+ depends on OF && HAS_IOMEM
select MFD_SYSCON
help
Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which
@@ -1518,6 +1583,16 @@
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
@@ -1587,7 +1662,7 @@
config RTC_DRV_JZ4740
tristate "Ingenic JZ4740 SoC"
- depends on MACH_INGENIC || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
help
If you say yes here you get support for the Ingenic JZ47xx SoCs RTC
controllers.
@@ -1687,6 +1762,14 @@
This driver can also be built as a module, if so, the module
will be called "rtc-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
+ Controller RTC module.
+
config RTC_DRV_SIRFSOC
tristate "SiRFSOC RTC"
depends on ARCH_SIRF
@@ -1793,13 +1876,23 @@
If you say yes here, you get support for the RTD1295 SoC
Real Time Clock.
+config RTC_DRV_ASPEED
+ tristate "ASPEED RTC"
+ depends on OF
+ depends on ARCH_ASPEED || COMPILE_TEST
+ help
+ If you say yes here you get support for the ASPEED BMC SoC real time
+ clocks.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-aspeed".
+
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.
@@ -1810,11 +1903,23 @@
config RTC_DRV_GOLDFISH
tristate "Goldfish Real Time Clock"
- depends on MIPS && (GOLDFISH || COMPILE_TEST)
+ depends on OF && HAS_IOMEM
+ depends on GOLDFISH || COMPILE_TEST
help
Say yes to enable RTC driver for the Goldfish based virtual platform.
Goldfish is a code name for the virtual platform developed by Google
for Android emulation.
+config RTC_DRV_WILCO_EC
+ tristate "Wilco EC RTC"
+ depends on WILCO_EC
+ default m
+ help
+ If you say yes here, you get read/write support for the Real Time
+ Clock on the Wilco Embedded Controller (Wilco is a kind of Chromebook)
+
+ This can also be built as a module. If so, the module will
+ be named "rtc_wilco_ec".
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 5ff2fc0..4ac8f19 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -5,7 +5,7 @@
ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG
-obj-$(CONFIG_RTC_LIB) += rtc-lib.o
+obj-$(CONFIG_RTC_LIB) += lib.o
obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
obj-$(CONFIG_RTC_SYSTOHC) += systohc.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
@@ -17,9 +17,9 @@
endif
rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o
-rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
-rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
-rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
+rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o
+rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o
+rtc-core-$(CONFIG_RTC_INTF_SYSFS) += sysfs.o
# Keep the list ordered.
@@ -28,17 +28,21 @@
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
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o
+obj-$(CONFIG_RTC_DRV_ASPEED) += rtc-aspeed.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
-obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
+obj-$(CONFIG_RTC_DRV_BD70528) += rtc-bd70528.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
@@ -69,19 +73,22 @@
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
+obj-$(CONFIG_RTC_DRV_FSL_FTM_ALARM) += rtc-fsl-ftm-alarm.o
obj-$(CONFIG_RTC_DRV_FTRTC010) += rtc-ftrtc010.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
+obj-$(CONFIG_RTC_DRV_IMX_SC) += rtc-imx-sc.o
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
obj-$(CONFIG_RTC_DRV_ISL12026) += rtc-isl12026.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
+obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o
obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o
obj-$(CONFIG_RTC_DRV_LPC24XX) += rtc-lpc24xx.o
obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o
-obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
@@ -96,18 +103,18 @@
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
+obj-$(CONFIG_RTC_DRV_MESON_VRTC)+= rtc-meson-vrtc.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_VRTC) += rtc-mrst.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o
obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
-obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
@@ -116,8 +123,8 @@
obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
-obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
+obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.o
@@ -136,6 +143,7 @@
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
@@ -148,15 +156,16 @@
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
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
+obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
-obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o
obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
@@ -169,10 +178,11 @@
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
+obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o
-obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 0fca4d7..9458e6d 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* RTC subsystem, base class
*
@@ -5,11 +6,7 @@
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* class skeleton from drivers/hwmon/hwmon.c
- *
- * 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.
-*/
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -23,13 +20,13 @@
#include "rtc-core.h"
-
static DEFINE_IDA(rtc_ida);
struct class *rtc_class;
static void rtc_device_release(struct device *dev)
{
struct rtc_device *rtc = to_rtc_device(dev);
+
ida_simple_remove(&rtc_ida, rtc->id);
kfree(rtc);
}
@@ -47,7 +44,6 @@
static struct timespec64 old_rtc, old_system, old_delta;
-
static int rtc_suspend(struct device *dev)
{
struct rtc_device *rtc = to_rtc_device(dev);
@@ -71,7 +67,6 @@
ktime_get_real_ts64(&old_system);
old_rtc.tv_sec = rtc_tm_to_time64(&tm);
-
/*
* To avoid drift caused by repeated suspend/resumes,
* which each can add ~1 second drift error,
@@ -83,7 +78,7 @@
if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) {
/*
* if delta_delta is too large, assume time correction
- * has occured and set old_delta to the current delta.
+ * has occurred and set old_delta to the current delta.
*/
old_delta = delta;
} else {
@@ -136,7 +131,7 @@
* to keep things accurate.
*/
sleep_time = timespec64_sub(sleep_time,
- timespec64_sub(new_system, old_system));
+ timespec64_sub(new_system, old_system));
if (sleep_time.tv_sec >= 0)
timekeeping_inject_sleeptime64(&sleep_time);
@@ -178,9 +173,9 @@
timerqueue_init_head(&rtc->timerqueue);
INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
/* Init aie timer */
- rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
+ rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, rtc);
/* Init uie timer */
- rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
+ rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, rtc);
/* Init pie timer */
hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
rtc->pie_timer.function = rtc_pie_update_irq;
@@ -278,88 +273,11 @@
}
/**
- * rtc_device_register - register w/ RTC class
- * @dev: the device to register
- *
- * rtc_device_unregister() must be called when the class device is no
- * longer needed.
- *
- * Returns the pointer to the new struct class device.
- */
-struct rtc_device *rtc_device_register(const char *name, struct device *dev,
- const struct rtc_class_ops *ops,
- struct module *owner)
-{
- struct rtc_device *rtc;
- struct rtc_wkalrm alrm;
- int id, err;
-
- id = rtc_device_get_id(dev);
- if (id < 0) {
- err = id;
- goto exit;
- }
-
- rtc = rtc_allocate_device();
- if (!rtc) {
- err = -ENOMEM;
- goto exit_ida;
- }
-
- rtc->id = id;
- rtc->ops = ops;
- rtc->owner = owner;
- rtc->dev.parent = dev;
-
- dev_set_name(&rtc->dev, "rtc%d", id);
-
- rtc_device_get_offset(rtc);
-
- /* Check to see if there is an ALARM already set in hw */
- err = __rtc_read_alarm(rtc, &alrm);
-
- if (!err && !rtc_valid_tm(&alrm.time))
- rtc_initialize_alarm(rtc, &alrm);
-
- rtc_dev_prepare(rtc);
-
- err = cdev_device_add(&rtc->char_dev, &rtc->dev);
- if (err) {
- dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
- name, MAJOR(rtc->dev.devt), rtc->id);
-
- /* This will free both memory and the ID */
- put_device(&rtc->dev);
- goto exit;
- } else {
- dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", name,
- MAJOR(rtc->dev.devt), rtc->id);
- }
-
- rtc_proc_add_device(rtc);
-
- dev_info(dev, "rtc core: registered %s as %s\n",
- name, dev_name(&rtc->dev));
-
- return rtc;
-
-exit_ida:
- ida_simple_remove(&rtc_ida, id);
-
-exit:
- dev_err(dev, "rtc core: unable to register %s, err = %d\n",
- name, err);
- return ERR_PTR(err);
-}
-EXPORT_SYMBOL_GPL(rtc_device_register);
-
-
-/**
* rtc_device_unregister - removes the previously registered RTC class device
*
* @rtc: the RTC class device to destroy
*/
-void rtc_device_unregister(struct rtc_device *rtc)
+static void rtc_device_unregister(struct rtc_device *rtc)
{
mutex_lock(&rtc->ops_lock);
/*
@@ -372,78 +290,6 @@
mutex_unlock(&rtc->ops_lock);
put_device(&rtc->dev);
}
-EXPORT_SYMBOL_GPL(rtc_device_unregister);
-
-static void devm_rtc_device_release(struct device *dev, void *res)
-{
- struct rtc_device *rtc = *(struct rtc_device **)res;
-
- rtc_nvmem_unregister(rtc);
- rtc_device_unregister(rtc);
-}
-
-static int devm_rtc_device_match(struct device *dev, void *res, void *data)
-{
- struct rtc **r = res;
-
- return *r == data;
-}
-
-/**
- * devm_rtc_device_register - resource managed rtc_device_register()
- * @dev: the device to register
- * @name: the name of the device
- * @ops: the rtc operations structure
- * @owner: the module owner
- *
- * @return a struct rtc on success, or an ERR_PTR on error
- *
- * Managed rtc_device_register(). The rtc_device returned from this function
- * are automatically freed on driver detach. See rtc_device_register()
- * for more information.
- */
-
-struct rtc_device *devm_rtc_device_register(struct device *dev,
- const char *name,
- const struct rtc_class_ops *ops,
- struct module *owner)
-{
- struct rtc_device **ptr, *rtc;
-
- ptr = devres_alloc(devm_rtc_device_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- rtc = rtc_device_register(name, dev, ops, owner);
- if (!IS_ERR(rtc)) {
- *ptr = rtc;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
-
- return rtc;
-}
-EXPORT_SYMBOL_GPL(devm_rtc_device_register);
-
-/**
- * devm_rtc_device_unregister - resource managed devm_rtc_device_unregister()
- * @dev: the device to unregister
- * @rtc: the RTC class device to unregister
- *
- * Deallocated a rtc allocated with devm_rtc_device_register(). Normally this
- * function will not need to be called and the resource management code will
- * ensure that the resource is freed.
- */
-void devm_rtc_device_unregister(struct device *dev, struct rtc_device *rtc)
-{
- int rc;
-
- rc = devres_release(dev, devm_rtc_device_release,
- devm_rtc_device_match, rtc);
- WARN_ON(rc);
-}
-EXPORT_SYMBOL_GPL(devm_rtc_device_unregister);
static void devm_rtc_release_device(struct device *dev, void *res)
{
@@ -500,8 +346,10 @@
struct rtc_wkalrm alrm;
int err;
- if (!rtc->ops)
+ if (!rtc->ops) {
+ dev_dbg(&rtc->dev, "no ops set\n");
return -EINVAL;
+ }
rtc->owner = owner;
rtc_device_get_offset(rtc);
@@ -531,6 +379,42 @@
}
EXPORT_SYMBOL_GPL(__rtc_register_device);
+/**
+ * devm_rtc_device_register - resource managed rtc_device_register()
+ * @dev: the device to register
+ * @name: the name of the device (unused)
+ * @ops: the rtc operations structure
+ * @owner: the module owner
+ *
+ * @return a struct rtc on success, or an ERR_PTR on error
+ *
+ * Managed rtc_device_register(). The rtc_device returned from this function
+ * are automatically freed on driver detach.
+ * This function is deprecated, use devm_rtc_allocate_device and
+ * rtc_register_device instead
+ */
+struct rtc_device *devm_rtc_device_register(struct device *dev,
+ const char *name,
+ const struct rtc_class_ops *ops,
+ struct module *owner)
+{
+ struct rtc_device *rtc;
+ int err;
+
+ rtc = devm_rtc_allocate_device(dev);
+ if (IS_ERR(rtc))
+ return rtc;
+
+ rtc->ops = ops;
+
+ err = __rtc_register_device(owner, rtc);
+ if (err)
+ return ERR_PTR(err);
+
+ return rtc;
+}
+EXPORT_SYMBOL_GPL(devm_rtc_device_register);
+
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/dev.c
similarity index 95%
rename from drivers/rtc/rtc-dev.c
rename to drivers/rtc/dev.c
index 43d962a..84feb25 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/dev.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* RTC subsystem, dev interface
*
@@ -5,11 +6,7 @@
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* based on arch/arm/common/rtctime.c
- *
- * 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.
-*/
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -60,7 +57,7 @@
} else if (rtc->oldsecs != tm.tm_sec) {
num = (tm.tm_sec + 60 - rtc->oldsecs) % 60;
rtc->oldsecs = tm.tm_sec;
- rtc->uie_timer.expires = jiffies + HZ - (HZ/10);
+ rtc->uie_timer.expires = jiffies + HZ - (HZ / 10);
rtc->uie_timer_active = 1;
rtc->uie_task_active = 0;
add_timer(&rtc->uie_timer);
@@ -71,6 +68,7 @@
if (num)
rtc_handle_legacy_irq(rtc, num, RTC_UF);
}
+
static void rtc_uie_timer(struct timer_list *t)
{
struct rtc_device *rtc = from_timer(rtc, t, uie_timer);
@@ -178,11 +176,6 @@
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) ?:
@@ -207,14 +200,14 @@
}
static long rtc_dev_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
int err = 0;
struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops;
struct rtc_time tm;
struct rtc_wkalrm alarm;
- void __user *uarg = (void __user *) arg;
+ void __user *uarg = (void __user *)arg;
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
@@ -238,7 +231,7 @@
case RTC_PIE_ON:
if (rtc->irq_freq > rtc->max_user_freq &&
- !capable(CAP_SYS_RESOURCE))
+ !capable(CAP_SYS_RESOURCE))
err = -EACCES;
break;
}
@@ -395,8 +388,9 @@
err = ops->ioctl(rtc->dev.parent, cmd, arg);
if (err == -ENOIOCTLCMD)
err = -ENOTTY;
- } else
+ } else {
err = -ENOTTY;
+ }
break;
}
@@ -408,6 +402,7 @@
static int rtc_dev_fasync(int fd, struct file *file, int on)
{
struct rtc_device *rtc = file->private_data;
+
return fasync_helper(fd, file, on, &rtc->async_queue);
}
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index b9ec4a1..a74d0d8 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* RTC subsystem, initialize system time on startup
*
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * 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.
-*/
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -33,7 +30,7 @@
};
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (rtc == NULL) {
+ if (!rtc) {
pr_info("unable to open rtc device (%s)\n",
CONFIG_RTC_HCTOSYS_DEVICE);
goto err_open;
@@ -44,7 +41,6 @@
dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
goto err_read;
-
}
tv64.tv_sec = rtc_tm_to_time64(&tm);
@@ -58,12 +54,8 @@
err = do_settimeofday64(&tv64);
- dev_info(rtc->dev.parent,
- "setting system clock to "
- "%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- (long long) tv64.tv_sec);
+ dev_info(rtc->dev.parent, "setting system clock to %ptR UTC (%lld)\n",
+ &tm, (long long)tv64.tv_sec);
err_read:
rtc_class_close(rtc);
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 3d577e2..c93ef33 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* RTC subsystem, interface functions
*
@@ -5,11 +6,7 @@
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* based on arch/arm/common/rtctime.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
+ */
#include <linux/rtc.h>
#include <linux/sched.h>
@@ -87,11 +84,12 @@
static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
- if (!rtc->ops)
+
+ if (!rtc->ops) {
err = -ENODEV;
- else if (!rtc->ops->read_time)
+ } else if (!rtc->ops->read_time) {
err = -EINVAL;
- else {
+ } else {
memset(tm, 0, sizeof(struct rtc_time));
err = rtc->ops->read_time(rtc->dev.parent, tm);
if (err < 0) {
@@ -147,14 +145,7 @@
err = -ENODEV;
else if (rtc->ops->set_time)
err = rtc->ops->set_time(rtc->dev.parent, tm);
- else if (rtc->ops->set_mmss64) {
- time64_t secs64 = rtc_tm_to_time64(tm);
-
- err = rtc->ops->set_mmss64(rtc->dev.parent, secs64);
- } else if (rtc->ops->set_mmss) {
- time64_t secs64 = rtc_tm_to_time64(tm);
- err = rtc->ops->set_mmss(rtc->dev.parent, secs64);
- } else
+ else
err = -EINVAL;
pm_stay_awake(rtc->dev.parent);
@@ -167,7 +158,8 @@
}
EXPORT_SYMBOL_GPL(rtc_set_time);
-static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+static int rtc_read_alarm_internal(struct rtc_device *rtc,
+ struct rtc_wkalrm *alarm)
{
int err;
@@ -175,11 +167,11 @@
if (err)
return err;
- if (rtc->ops == NULL)
+ if (!rtc->ops) {
err = -ENODEV;
- else if (!rtc->ops->read_alarm)
+ } else if (!rtc->ops->read_alarm) {
err = -EINVAL;
- else {
+ } else {
alarm->enabled = 0;
alarm->pending = 0;
alarm->time.tm_sec = -1;
@@ -207,7 +199,7 @@
int first_time = 1;
time64_t t_now, t_alm;
enum { none, day, month, year } missing = none;
- unsigned days;
+ unsigned int days;
/* The lower level RTC driver may return -1 in some fields,
* creating invalid alarm->time values, for reasons like:
@@ -276,10 +268,10 @@
return err;
/* note that tm_sec is a "don't care" value here: */
- } while ( before.tm_min != now.tm_min
- || before.tm_hour != now.tm_hour
- || before.tm_mon != now.tm_mon
- || before.tm_year != now.tm_year);
+ } while (before.tm_min != now.tm_min ||
+ before.tm_hour != now.tm_hour ||
+ before.tm_mon != now.tm_mon ||
+ before.tm_year != now.tm_year);
/* Fill in the missing alarm fields using the timestamp; we
* know there's at least one since alarm->time is invalid.
@@ -296,7 +288,7 @@
alarm->time.tm_mday = now.tm_mday;
missing = day;
}
- if ((unsigned)alarm->time.tm_mon >= 12) {
+ if ((unsigned int)alarm->time.tm_mon >= 12) {
alarm->time.tm_mon = now.tm_mon;
if (missing == none)
missing = month;
@@ -321,7 +313,6 @@
goto done;
switch (missing) {
-
/* 24 hour rollover ... if it's now 10am Monday, an alarm that
* that will trigger at 5am will do so at 5am Tuesday, which
* could also be in the next month or year. This is a common
@@ -341,14 +332,14 @@
case month:
dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month");
do {
- if (alarm->time.tm_mon < 11)
+ if (alarm->time.tm_mon < 11) {
alarm->time.tm_mon++;
- else {
+ } else {
alarm->time.tm_mon = 0;
alarm->time.tm_year++;
}
days = rtc_month_days(alarm->time.tm_mon,
- alarm->time.tm_year);
+ alarm->time.tm_year);
} while (days < alarm->time.tm_mday);
break;
@@ -357,8 +348,8 @@
dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year");
do {
alarm->time.tm_year++;
- } while (!is_leap_year(alarm->time.tm_year + 1900)
- && rtc_valid_tm(&alarm->time) != 0);
+ } while (!is_leap_year(alarm->time.tm_year + 1900) &&
+ rtc_valid_tm(&alarm->time) != 0);
break;
default:
@@ -368,12 +359,9 @@
err = rtc_valid_tm(&alarm->time);
done:
- if (err) {
- dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n",
- alarm->time.tm_year + 1900, alarm->time.tm_mon + 1,
- alarm->time.tm_mday, alarm->time.tm_hour, alarm->time.tm_min,
- alarm->time.tm_sec);
- }
+ if (err)
+ dev_warn(&rtc->dev, "invalid alarm value: %ptR\n",
+ &alarm->time);
return err;
}
@@ -385,11 +373,11 @@
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;
- if (rtc->ops == NULL)
+ if (!rtc->ops) {
err = -ENODEV;
- else if (!rtc->ops->read_alarm)
+ } else if (!rtc->ops->read_alarm) {
err = -EINVAL;
- else {
+ } else {
memset(alarm, 0, sizeof(struct rtc_wkalrm));
alarm->enabled = rtc->aie_timer.enabled;
alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires);
@@ -498,7 +486,6 @@
/* Alarm has to be enabled & in the future for us to enqueue it */
if (alarm->enabled && (rtc_tm_to_ktime(now) <
rtc->aie_timer.node.expires)) {
-
rtc->aie_timer.enabled = 1;
timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
trace_rtc_timer_enqueue(&rtc->aie_timer);
@@ -510,7 +497,9 @@
int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
{
- int err = mutex_lock_interruptible(&rtc->ops_lock);
+ int err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;
@@ -539,7 +528,9 @@
int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled)
{
- int err = mutex_lock_interruptible(&rtc->ops_lock);
+ int err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;
@@ -568,27 +559,25 @@
rtc->uie_rtctimer.node.expires = ktime_add(now, onesec);
rtc->uie_rtctimer.period = ktime_set(1, 0);
err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer);
- } else
+ } else {
rtc_timer_remove(rtc, &rtc->uie_rtctimer);
+ }
out:
mutex_unlock(&rtc->ops_lock);
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
/*
- * Enable emulation if the driver did not provide
- * the update_irq_enable function pointer or if returned
- * -EINVAL to signal that it has been configured without
- * interrupts or that are not available at the moment.
+ * Enable emulation if the driver returned -EINVAL to signal that it has
+ * been configured without interrupts or they are not available at the
+ * moment.
*/
if (err == -EINVAL)
err = rtc_dev_update_irq_enable_emul(rtc, enabled);
#endif
return err;
-
}
EXPORT_SYMBOL_GPL(rtc_update_irq_enable);
-
/**
* rtc_handle_legacy_irq - AIE, UIE and PIE event hook
* @rtc: pointer to the rtc device
@@ -596,7 +585,6 @@
* This function is called when an AIE, UIE or PIE mode interrupt
* has occurred (or been emulated).
*
- * Triggers the registered irq_task function callback.
*/
void rtc_handle_legacy_irq(struct rtc_device *rtc, int num, int mode)
{
@@ -604,40 +592,35 @@
/* mark one irq of the appropriate mode */
spin_lock_irqsave(&rtc->irq_lock, flags);
- rtc->irq_data = (rtc->irq_data + (num << 8)) | (RTC_IRQF|mode);
+ rtc->irq_data = (rtc->irq_data + (num << 8)) | (RTC_IRQF | mode);
spin_unlock_irqrestore(&rtc->irq_lock, flags);
wake_up_interruptible(&rtc->irq_queue);
kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
}
-
/**
* rtc_aie_update_irq - AIE mode rtctimer hook
- * @private: pointer to the rtc_device
+ * @rtc: pointer to the rtc_device
*
* This functions is called when the aie_timer expires.
*/
-void rtc_aie_update_irq(void *private)
+void rtc_aie_update_irq(struct rtc_device *rtc)
{
- struct rtc_device *rtc = (struct rtc_device *)private;
rtc_handle_legacy_irq(rtc, 1, RTC_AF);
}
-
/**
* rtc_uie_update_irq - UIE mode rtctimer hook
- * @private: pointer to the rtc_device
+ * @rtc: pointer to the rtc_device
*
* This functions is called when the uie_timer expires.
*/
-void rtc_uie_update_irq(void *private)
+void rtc_uie_update_irq(struct rtc_device *rtc)
{
- struct rtc_device *rtc = (struct rtc_device *)private;
rtc_handle_legacy_irq(rtc, 1, RTC_UF);
}
-
/**
* rtc_pie_update_irq - PIE mode hrtimer hook
* @timer: pointer to the pie mode hrtimer
@@ -650,7 +633,8 @@
{
struct rtc_device *rtc;
ktime_t period;
- int count;
+ u64 count;
+
rtc = container_of(timer, struct rtc_device, pie_timer);
period = NSEC_PER_SEC / rtc->irq_freq;
@@ -669,7 +653,7 @@
* Context: any
*/
void rtc_update_irq(struct rtc_device *rtc,
- unsigned long num, unsigned long events)
+ unsigned long num, unsigned long events)
{
if (IS_ERR_OR_NULL(rtc))
return;
@@ -679,21 +663,12 @@
}
EXPORT_SYMBOL_GPL(rtc_update_irq);
-static int __rtc_match(struct device *dev, const void *data)
-{
- const char *name = data;
-
- if (strcmp(dev_name(dev), name) == 0)
- return 1;
- return 0;
-}
-
struct rtc_device *rtc_class_open(const char *name)
{
struct device *dev;
struct rtc_device *rtc = NULL;
- dev = class_find_device(rtc_class, NULL, name, __rtc_match);
+ dev = class_find_device_by_name(rtc_class, name);
if (dev)
rtc = to_rtc_device(dev);
@@ -741,7 +716,6 @@
/**
* rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
* @rtc: the rtc device
- * @task: currently registered with rtc_irq_register()
* @enabled: true to enable periodic IRQs
* Context: any
*
@@ -764,7 +738,6 @@
/**
* rtc_irq_set_freq - set 2^N Hz periodic IRQ frequency for IRQ
* @rtc: the rtc device
- * @task: currently registered with rtc_irq_register()
* @freq: positive frequency
* Context: any
*
@@ -820,6 +793,7 @@
if (!next || ktime_before(timer->node.expires, next->expires)) {
struct rtc_wkalrm alarm;
int err;
+
alarm.time = rtc_ktime_to_tm(timer->node.expires);
alarm.enabled = 1;
err = __rtc_set_alarm(rtc, &alarm);
@@ -860,12 +834,14 @@
static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer)
{
struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
+
timerqueue_del(&rtc->timerqueue, &timer->node);
trace_rtc_timer_dequeue(timer);
timer->enabled = 0;
if (next == &timer->node) {
struct rtc_wkalrm alarm;
int err;
+
next = timerqueue_getnext(&rtc->timerqueue);
if (!next) {
rtc_alarm_disable(rtc);
@@ -915,7 +891,7 @@
trace_rtc_timer_dequeue(timer);
timer->enabled = 0;
if (timer->func)
- timer->func(timer->private_data);
+ timer->func(timer->rtc);
trace_rtc_timer_fired(timer);
/* Re-add/fwd periodic timers */
@@ -938,9 +914,9 @@
alarm.enabled = 1;
reprogram:
err = __rtc_set_alarm(rtc, &alarm);
- if (err == -ETIME)
+ if (err == -ETIME) {
goto again;
- else if (err) {
+ } else if (err) {
if (retry-- > 0)
goto reprogram;
@@ -951,27 +927,28 @@
dev_err(&rtc->dev, "__rtc_set_alarm: err=%d\n", err);
goto again;
}
- } else
+ } else {
rtc_alarm_disable(rtc);
+ }
pm_relax(rtc->dev.parent);
mutex_unlock(&rtc->ops_lock);
}
-
/* rtc_timer_init - Initializes an rtc_timer
* @timer: timer to be intiialized
* @f: function pointer to be called when timer fires
- * @data: private data passed to function pointer
+ * @rtc: pointer to the rtc_device
*
* Kernel interface to initializing an rtc_timer.
*/
-void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data)
+void rtc_timer_init(struct rtc_timer *timer, void (*f)(struct rtc_device *r),
+ struct rtc_device *rtc)
{
timerqueue_init(&timer->node);
timer->enabled = 0;
timer->func = f;
- timer->private_data = data;
+ timer->rtc = rtc;
}
/* rtc_timer_start - Sets an rtc_timer to fire in the future
@@ -983,9 +960,10 @@
* Kernel interface to set an rtc_timer
*/
int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer,
- ktime_t expires, ktime_t period)
+ ktime_t expires, ktime_t period)
{
int ret = 0;
+
mutex_lock(&rtc->ops_lock);
if (timer->enabled)
rtc_timer_remove(rtc, timer);
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/lib.c
similarity index 79%
rename from drivers/rtc/rtc-lib.c
rename to drivers/rtc/lib.c
index 4a3c0f3..2328458 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* rtc and date/time utility functions
*
@@ -5,11 +6,7 @@
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* based on arch/arm/common/rtctime.c and other bits
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
+ */
#include <linux/export.h>
#include <linux/rtc.h>
@@ -25,7 +22,7 @@
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
-#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
/*
* The number of days in the month.
@@ -41,13 +38,12 @@
*/
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
{
- return rtc_ydays[is_leap_year(year)][month] + day-1;
+ return rtc_ydays[is_leap_year(year)][month] + day - 1;
}
EXPORT_SYMBOL(rtc_year_days);
-
/*
- * rtc_time_to_tm64 - Converts time64_t to rtc_time.
+ * rtc_time64_to_tm - Converts time64_t to rtc_time.
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
*/
void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
@@ -97,13 +93,15 @@
*/
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)
- || ((unsigned)tm->tm_hour) >= 24
- || ((unsigned)tm->tm_min) >= 60
- || ((unsigned)tm->tm_sec) >= 60)
+ if (tm->tm_year < 70 ||
+ tm->tm_year > (INT_MAX - 1900) ||
+ ((unsigned int)tm->tm_mon) >= 12 ||
+ tm->tm_mday < 1 ||
+ tm->tm_mday > rtc_month_days(tm->tm_mon,
+ ((unsigned int)tm->tm_year + 1900)) ||
+ ((unsigned int)tm->tm_hour) >= 24 ||
+ ((unsigned int)tm->tm_min) >= 60 ||
+ ((unsigned int)tm->tm_sec) >= 60)
return -EINVAL;
return 0;
@@ -116,8 +114,8 @@
*/
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 int)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);
diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c
index 36ab183..4312096 100644
--- a/drivers/rtc/nvmem.c
+++ b/drivers/rtc/nvmem.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* RTC subsystem, nvmem interface
*
* Copyright (C) 2017 Alexandre Belloni
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/types.h>
#include <linux/nvmem-consumer.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
#include <linux/sysfs.h>
/*
@@ -25,11 +23,9 @@
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
- struct rtc_device *rtc = attr->private;
-
dev_warn_once(kobj_to_dev(kobj), nvram_warning);
- return nvmem_device_read(rtc->nvmem, off, count, buf);
+ return nvmem_device_read(attr->private, off, count, buf);
}
static ssize_t
@@ -37,26 +33,23 @@
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
- struct rtc_device *rtc = attr->private;
-
dev_warn_once(kobj_to_dev(kobj), nvram_warning);
- return nvmem_device_write(rtc->nvmem, off, count, buf);
+ return nvmem_device_write(attr->private, off, count, buf);
}
-static int rtc_nvram_register(struct rtc_device *rtc, size_t size)
+static int rtc_nvram_register(struct rtc_device *rtc,
+ struct nvmem_device *nvmem, size_t size)
{
int err;
- rtc->nvram = devm_kzalloc(rtc->dev.parent,
- sizeof(struct bin_attribute),
- GFP_KERNEL);
+ rtc->nvram = kzalloc(sizeof(*rtc->nvram), GFP_KERNEL);
if (!rtc->nvram)
return -ENOMEM;
rtc->nvram->attr.name = "nvram";
rtc->nvram->attr.mode = 0644;
- rtc->nvram->private = rtc;
+ rtc->nvram->private = nvmem;
sysfs_bin_attr_init(rtc->nvram);
@@ -67,7 +60,7 @@
err = sysfs_create_bin_file(&rtc->dev.parent->kobj,
rtc->nvram);
if (err) {
- devm_kfree(rtc->dev.parent, rtc->nvram);
+ kfree(rtc->nvram);
rtc->nvram = NULL;
}
@@ -77,6 +70,8 @@
static void rtc_nvram_unregister(struct rtc_device *rtc)
{
sysfs_remove_bin_file(&rtc->dev.parent->kobj, rtc->nvram);
+ kfree(rtc->nvram);
+ rtc->nvram = NULL;
}
/*
@@ -85,21 +80,20 @@
int rtc_nvmem_register(struct rtc_device *rtc,
struct nvmem_config *nvmem_config)
{
- if (!IS_ERR_OR_NULL(rtc->nvmem))
- return -EBUSY;
+ struct nvmem_device *nvmem;
if (!nvmem_config)
return -ENODEV;
nvmem_config->dev = rtc->dev.parent;
nvmem_config->owner = rtc->owner;
- rtc->nvmem = nvmem_register(nvmem_config);
- if (IS_ERR(rtc->nvmem))
- return PTR_ERR(rtc->nvmem);
+ nvmem = devm_nvmem_register(rtc->dev.parent, nvmem_config);
+ if (IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
/* Register the old ABI */
if (rtc->nvram_old_abi)
- rtc_nvram_register(rtc, nvmem_config->size);
+ rtc_nvram_register(rtc, nvmem, nvmem_config->size);
return 0;
}
@@ -107,12 +101,7 @@
void rtc_nvmem_unregister(struct rtc_device *rtc)
{
- if (IS_ERR_OR_NULL(rtc->nvmem))
- return;
-
/* unregister the old ABI */
if (rtc->nvram)
rtc_nvram_unregister(rtc);
-
- nvmem_unregister(rtc->nvmem);
}
diff --git a/drivers/rtc/proc.c b/drivers/rtc/proc.c
new file mode 100644
index 0000000..7334459
--- /dev/null
+++ b/drivers/rtc/proc.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RTC subsystem, proc interface
+ *
+ * Copyright (C) 2005-06 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include "rtc-core.h"
+
+#define NAME_SIZE 10
+
+#if defined(CONFIG_RTC_HCTOSYS_DEVICE)
+static bool is_rtc_hctosys(struct rtc_device *rtc)
+{
+ int size;
+ char name[NAME_SIZE];
+
+ size = scnprintf(name, NAME_SIZE, "rtc%d", rtc->id);
+ if (size > NAME_SIZE)
+ return false;
+
+ return !strncmp(name, CONFIG_RTC_HCTOSYS_DEVICE, NAME_SIZE);
+}
+#else
+static bool is_rtc_hctosys(struct rtc_device *rtc)
+{
+ return (rtc->id == 0);
+}
+#endif
+
+static int rtc_proc_show(struct seq_file *seq, void *offset)
+{
+ int err;
+ struct rtc_device *rtc = seq->private;
+ const struct rtc_class_ops *ops = rtc->ops;
+ struct rtc_wkalrm alrm;
+ struct rtc_time tm;
+
+ err = rtc_read_time(rtc, &tm);
+ if (err == 0) {
+ seq_printf(seq,
+ "rtc_time\t: %ptRt\n"
+ "rtc_date\t: %ptRd\n",
+ &tm, &tm);
+ }
+
+ err = rtc_read_alarm(rtc, &alrm);
+ if (err == 0) {
+ seq_printf(seq, "alrm_time\t: %ptRt\n", &alrm.time);
+ seq_printf(seq, "alrm_date\t: %ptRd\n", &alrm.time);
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ alrm.enabled ? "yes" : "no");
+ seq_printf(seq, "alrm_pending\t: %s\n",
+ alrm.pending ? "yes" : "no");
+ seq_printf(seq, "update IRQ enabled\t: %s\n",
+ (rtc->uie_rtctimer.enabled) ? "yes" : "no");
+ seq_printf(seq, "periodic IRQ enabled\t: %s\n",
+ (rtc->pie_enabled) ? "yes" : "no");
+ seq_printf(seq, "periodic IRQ frequency\t: %d\n",
+ rtc->irq_freq);
+ seq_printf(seq, "max user IRQ frequency\t: %d\n",
+ rtc->max_user_freq);
+ }
+
+ seq_printf(seq, "24hr\t\t: yes\n");
+
+ if (ops->proc)
+ ops->proc(rtc->dev.parent, seq);
+
+ return 0;
+}
+
+void rtc_proc_add_device(struct rtc_device *rtc)
+{
+ if (is_rtc_hctosys(rtc))
+ proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show,
+ rtc);
+}
+
+void rtc_proc_del_device(struct rtc_device *rtc)
+{
+ if (is_rtc_hctosys(rtc))
+ remove_proc_entry("driver/rtc", NULL);
+}
diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c
index cab293c..75779e8 100644
--- a/drivers/rtc/rtc-88pm80x.c
+++ b/drivers/rtc/rtc-88pm80x.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Real Time Clock driver for Marvell 88PM80x PMIC
*
* Copyright (c) 2012 Marvell International Ltd.
* Wenzeng Chen<wzch@marvell.com>
* Qiao Zhou <zhouqiao@marvell.com>
- *
- * 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.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
@@ -114,12 +102,14 @@
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 +127,8 @@
/* 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 +149,13 @@
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 +178,14 @@
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);
@@ -269,7 +264,6 @@
return -ENOMEM;
info->irq = platform_get_irq(pdev, 0);
if (info->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource!\n");
ret = -EINVAL;
goto out;
}
@@ -301,10 +295,9 @@
info->rtc_dev->range_max = U32_MAX;
ret = rtc_register_device(info->rtc_dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ if (ret)
goto out_rtc;
- }
+
/*
* enable internal XO instead of internal 3.25MHz clock since it can
* free running in PMIC power-down state.
diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c
index 01ffc0e..4743b16 100644
--- a/drivers/rtc/rtc-88pm860x.c
+++ b/drivers/rtc/rtc-88pm860x.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Real Time Clock driver for Marvell 88PM860x PMIC
*
* Copyright (c) 2010 Marvell International Ltd.
* Author: Haojian Zhuang <haojian.zhuang@marvell.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.
*/
#include <linux/kernel.h>
@@ -115,11 +112,13 @@
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 +144,8 @@
/* 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 +170,12 @@
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 +200,13 @@
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);
@@ -324,10 +328,8 @@
if (!info)
return -ENOMEM;
info->irq = platform_get_irq(pdev, 0);
- if (info->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource!\n");
+ if (info->irq < 0)
return info->irq;
- }
info->chip = chip;
info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
@@ -414,7 +416,7 @@
struct pm860x_rtc_info *info = platform_get_drvdata(pdev);
#ifdef VRTC_CALIBRATION
- flush_scheduled_work();
+ cancel_delayed_work_sync(&info->calib_work);
/* disable measurement */
pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0);
#endif /* VRTC_CALIBRATION */
diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c
index 2233601..cdad6f0 100644
--- a/drivers/rtc/rtc-ab-b5ze-s3.c
+++ b/drivers/rtc/rtc-ab-b5ze-s3.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* rtc-ab-b5ze-s3 - Driver for Abracon AB-RTCMC-32.768Khz-B5ZE-S3
* I2C RTC / Alarm chip
@@ -10,19 +11,9 @@
*
* This work is based on ISL12057 driver (drivers/rtc/rtc-isl12057.c).
*
- * 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/module.h>
-#include <linux/mutex.h>
#include <linux/rtc.h>
#include <linux/i2c.h>
#include <linux/bcd.h>
@@ -128,7 +119,6 @@
struct abb5zes3_rtc_data {
struct rtc_device *rtc;
struct regmap *regmap;
- struct mutex lock;
int irq;
@@ -138,8 +128,7 @@
/*
* Try and match register bits w/ fixed null values to see whether we
- * are dealing with an ABB5ZES3. Note: this function is called early
- * during init and hence does need mutex protection.
+ * are dealing with an ABB5ZES3.
*/
static int abb5zes3_i2c_validate_chip(struct regmap *regmap)
{
@@ -230,14 +219,12 @@
if (ret) {
dev_err(dev, "%s: reading RTC time failed (%d)\n",
__func__, ret);
- goto err;
+ return ret;
}
/* If clock integrity is not guaranteed, do not return a time value */
- if (regs[ABB5ZES3_REG_RTC_SC] & ABB5ZES3_REG_RTC_SC_OSC) {
- ret = -ENODATA;
- goto err;
- }
+ if (regs[ABB5ZES3_REG_RTC_SC] & ABB5ZES3_REG_RTC_SC_OSC)
+ return -ENODATA;
tm->tm_sec = bcd2bin(regs[ABB5ZES3_REG_RTC_SC] & 0x7F);
tm->tm_min = bcd2bin(regs[ABB5ZES3_REG_RTC_MN]);
@@ -255,7 +242,6 @@
tm->tm_mon = bcd2bin(regs[ABB5ZES3_REG_RTC_MO]) - 1; /* starts at 1 */
tm->tm_year = bcd2bin(regs[ABB5ZES3_REG_RTC_YR]) + 100;
-err:
return ret;
}
@@ -273,12 +259,9 @@
regs[ABB5ZES3_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1);
regs[ABB5ZES3_REG_RTC_YR] = bin2bcd(tm->tm_year - 100);
- mutex_lock(&data->lock);
ret = regmap_bulk_write(data->regmap, ABB5ZES3_REG_RTC_SC,
regs + ABB5ZES3_REG_RTC_SC,
ABB5ZES3_RTC_SEC_LEN);
- mutex_unlock(&data->lock);
-
return ret;
}
@@ -332,38 +315,35 @@
if (ret) {
dev_err(dev, "%s: reading Timer A section failed (%d)\n",
__func__, ret);
- goto err;
+ return ret;
}
/* get current time ... */
ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
if (ret)
- goto err;
+ return ret;
/* ... convert to seconds ... */
- ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
- if (ret)
- goto err;
+ rtc_secs = rtc_tm_to_time64(&rtc_tm);
/* ... add remaining timer A time ... */
ret = sec_from_timer_a(&timer_secs, regs[1], regs[2]);
if (ret)
- goto err;
+ return ret;
/* ... and convert back. */
- rtc_time_to_tm(rtc_secs + timer_secs, alarm_tm);
+ rtc_time64_to_tm(rtc_secs + timer_secs, alarm_tm);
ret = regmap_read(data->regmap, ABB5ZES3_REG_CTRL2, ®);
if (ret) {
dev_err(dev, "%s: reading ctrl reg failed (%d)\n",
__func__, ret);
- goto err;
+ return ret;
}
alarm->enabled = !!(reg & ABB5ZES3_REG_CTRL2_WTAIE);
-err:
- return ret;
+ return 0;
}
/* Read alarm currently configured via a RTC alarm registers. */
@@ -382,7 +362,7 @@
if (ret) {
dev_err(dev, "%s: reading alarm section failed (%d)\n",
__func__, ret);
- goto err;
+ return ret;
}
alarm_tm->tm_sec = 0;
@@ -398,18 +378,13 @@
*/
ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
if (ret)
- goto err;
+ return ret;
alarm_tm->tm_year = rtc_tm.tm_year;
alarm_tm->tm_mon = rtc_tm.tm_mon;
- ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
- if (ret)
- goto err;
-
- ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
- if (ret)
- goto err;
+ rtc_secs = rtc_tm_to_time64(&rtc_tm);
+ alarm_secs = rtc_tm_to_time64(alarm_tm);
if (alarm_secs < rtc_secs) {
if (alarm_tm->tm_mon == 11) {
@@ -424,13 +399,12 @@
if (ret) {
dev_err(dev, "%s: reading ctrl reg failed (%d)\n",
__func__, ret);
- goto err;
+ return ret;
}
alarm->enabled = !!(reg & ABB5ZES3_REG_CTRL1_AIE);
-err:
- return ret;
+ return 0;
}
/*
@@ -447,12 +421,10 @@
struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
int ret;
- mutex_lock(&data->lock);
if (data->timer_alarm)
ret = _abb5zes3_rtc_read_timer(dev, alarm);
else
ret = _abb5zes3_rtc_read_alarm(dev, alarm);
- mutex_unlock(&data->lock);
return ret;
}
@@ -466,33 +438,25 @@
{
struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
struct rtc_time *alarm_tm = &alarm->time;
- unsigned long rtc_secs, alarm_secs;
u8 regs[ABB5ZES3_ALRM_SEC_LEN];
struct rtc_time rtc_tm;
int ret, enable = 1;
- ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
- if (ret)
- goto err;
-
- ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
- if (ret)
- goto err;
-
- ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
- if (ret)
- goto err;
-
- /* If alarm time is before current time, disable the alarm */
- if (!alarm->enabled || alarm_secs <= rtc_secs) {
+ if (!alarm->enabled) {
enable = 0;
} else {
+ unsigned long rtc_secs, alarm_secs;
+
/*
* Chip only support alarms up to one month in the future. Let's
* return an error if we get something after that limit.
* Comparison is done by incrementing rtc_tm month field by one
* and checking alarm value is still below.
*/
+ ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
+ if (ret)
+ return ret;
+
if (rtc_tm.tm_mon == 11) { /* handle year wrapping */
rtc_tm.tm_mon = 0;
rtc_tm.tm_year += 1;
@@ -500,15 +464,13 @@
rtc_tm.tm_mon += 1;
}
- ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
- if (ret)
- goto err;
+ rtc_secs = rtc_tm_to_time64(&rtc_tm);
+ alarm_secs = rtc_tm_to_time64(alarm_tm);
if (alarm_secs > rtc_secs) {
- dev_err(dev, "%s: alarm maximum is one month in the "
- "future (%d)\n", __func__, ret);
- ret = -EINVAL;
- goto err;
+ dev_err(dev, "%s: alarm maximum is one month in the future (%d)\n",
+ __func__, ret);
+ return -EINVAL;
}
}
@@ -526,17 +488,14 @@
if (ret < 0) {
dev_err(dev, "%s: writing ALARM section failed (%d)\n",
__func__, ret);
- goto err;
+ return ret;
}
/* Record currently configured alarm is not a timer */
data->timer_alarm = 0;
/* Enable or disable alarm interrupt generation */
- ret = _abb5zes3_rtc_update_alarm(dev, enable);
-
-err:
- return ret;
+ return _abb5zes3_rtc_update_alarm(dev, enable);
}
/*
@@ -557,7 +516,7 @@
ABB5ZES3_TIMA_SEC_LEN);
if (ret < 0) {
dev_err(dev, "%s: writing timer section failed\n", __func__);
- goto err;
+ return ret;
}
/* Configure Timer A as a watchdog timer */
@@ -570,10 +529,7 @@
data->timer_alarm = 1;
/* Enable or disable timer interrupt generation */
- ret = _abb5zes3_rtc_update_timer(dev, alarm->enabled);
-
-err:
- return ret;
+ return _abb5zes3_rtc_update_timer(dev, alarm->enabled);
}
/*
@@ -590,31 +546,25 @@
struct rtc_time rtc_tm;
int ret;
- mutex_lock(&data->lock);
ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
if (ret)
- goto err;
+ return ret;
- ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
- if (ret)
- goto err;
-
- ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
- if (ret)
- goto err;
+ rtc_secs = rtc_tm_to_time64(&rtc_tm);
+ alarm_secs = rtc_tm_to_time64(alarm_tm);
/* Let's first disable both the alarm and the timer interrupts */
ret = _abb5zes3_rtc_update_alarm(dev, false);
if (ret < 0) {
dev_err(dev, "%s: unable to disable alarm (%d)\n", __func__,
ret);
- goto err;
+ return ret;
}
ret = _abb5zes3_rtc_update_timer(dev, false);
if (ret < 0) {
dev_err(dev, "%s: unable to disable timer (%d)\n", __func__,
ret);
- goto err;
+ return ret;
}
data->timer_alarm = 0;
@@ -629,9 +579,6 @@
else
ret = _abb5zes3_rtc_set_alarm(dev, alarm);
- err:
- mutex_unlock(&data->lock);
-
if (ret)
dev_err(dev, "%s: unable to configure alarm (%d)\n", __func__,
ret);
@@ -650,8 +597,7 @@
/*
* Check current RTC status and enable/disable what needs to be. Return 0 if
- * everything went ok and a negative value upon error. Note: this function
- * is called early during init and hence does need mutex protection.
+ * everything went ok and a negative value upon error.
*/
static int abb5zes3_rtc_check_setup(struct device *dev)
{
@@ -675,8 +621,9 @@
ABB5ZES3_REG_TIM_CLK_COF1 | ABB5ZES3_REG_TIM_CLK_COF2 |
ABB5ZES3_REG_TIM_CLK_TBM | ABB5ZES3_REG_TIM_CLK_TAM);
ret = regmap_update_bits(regmap, ABB5ZES3_REG_TIM_CLK, mask,
- ABB5ZES3_REG_TIM_CLK_COF0 | ABB5ZES3_REG_TIM_CLK_COF1 |
- ABB5ZES3_REG_TIM_CLK_COF2);
+ ABB5ZES3_REG_TIM_CLK_COF0 |
+ ABB5ZES3_REG_TIM_CLK_COF1 |
+ ABB5ZES3_REG_TIM_CLK_COF2);
if (ret < 0) {
dev_err(dev, "%s: unable to initialize clkout register (%d)\n",
__func__, ret);
@@ -729,9 +676,9 @@
* switchover flag but not battery low flag. The latter is checked
* later below.
*/
- mask = (ABB5ZES3_REG_CTRL3_PM0 | ABB5ZES3_REG_CTRL3_PM1 |
- ABB5ZES3_REG_CTRL3_PM2 | ABB5ZES3_REG_CTRL3_BLIE |
- ABB5ZES3_REG_CTRL3_BSIE| ABB5ZES3_REG_CTRL3_BSF);
+ mask = (ABB5ZES3_REG_CTRL3_PM0 | ABB5ZES3_REG_CTRL3_PM1 |
+ ABB5ZES3_REG_CTRL3_PM2 | ABB5ZES3_REG_CTRL3_BLIE |
+ ABB5ZES3_REG_CTRL3_BSIE | ABB5ZES3_REG_CTRL3_BSF);
ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL3, mask, 0);
if (ret < 0) {
dev_err(dev, "%s: unable to initialize CTRL3 register (%d)\n",
@@ -748,10 +695,8 @@
}
if (reg & ABB5ZES3_REG_RTC_SC_OSC) {
- dev_err(dev, "clock integrity not guaranteed. Osc. has stopped "
- "or has been interrupted.\n");
- dev_err(dev, "change battery (if not already done) and "
- "then set time to reset osc. failure flag.\n");
+ dev_err(dev, "clock integrity not guaranteed. Osc. has stopped or has been interrupted.\n");
+ dev_err(dev, "change battery (if not already done) and then set time to reset osc. failure flag.\n");
}
/*
@@ -769,13 +714,12 @@
data->battery_low = reg & ABB5ZES3_REG_CTRL3_BLF;
if (data->battery_low) {
- dev_err(dev, "RTC battery is low; please, consider "
- "changing it!\n");
+ dev_err(dev, "RTC battery is low; please, consider changing it!\n");
ret = _abb5zes3_rtc_battery_low_irq_enable(regmap, false);
if (ret)
- dev_err(dev, "%s: disabling battery low interrupt "
- "generation failed (%d)\n", __func__, ret);
+ dev_err(dev, "%s: disabling battery low interrupt generation failed (%d)\n",
+ __func__, ret);
}
return ret;
@@ -788,12 +732,10 @@
int ret = 0;
if (rtc_data->irq) {
- mutex_lock(&rtc_data->lock);
if (rtc_data->timer_alarm)
ret = _abb5zes3_rtc_update_timer(dev, enable);
else
ret = _abb5zes3_rtc_update_alarm(dev, enable);
- mutex_unlock(&rtc_data->lock);
}
return ret;
@@ -885,49 +827,44 @@
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_I2C_BLOCK)) {
- ret = -ENODEV;
- goto err;
- }
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -ENODEV;
regmap = devm_regmap_init_i2c(client, &abb5zes3_rtc_regmap_config);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
dev_err(dev, "%s: regmap allocation failed: %d\n",
__func__, ret);
- goto err;
+ return ret;
}
ret = abb5zes3_i2c_validate_chip(regmap);
if (ret)
- goto err;
+ return ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!data)
+ return -ENOMEM;
- mutex_init(&data->lock);
data->regmap = regmap;
dev_set_drvdata(dev, data);
ret = abb5zes3_rtc_check_setup(dev);
if (ret)
- goto err;
+ return ret;
data->rtc = devm_rtc_allocate_device(dev);
ret = PTR_ERR_OR_ZERO(data->rtc);
if (ret) {
dev_err(dev, "%s: unable to allocate RTC device (%d)\n",
__func__, ret);
- goto err;
+ return ret;
}
if (client->irq > 0) {
ret = devm_request_threaded_irq(dev, client->irq, NULL,
_abb5zes3_rtc_interrupt,
- IRQF_SHARED|IRQF_ONESHOT,
+ IRQF_SHARED | IRQF_ONESHOT,
DRV_NAME, client);
if (!ret) {
device_init_wakeup(dev, true);
@@ -949,8 +886,8 @@
if (!data->battery_low && data->irq) {
ret = _abb5zes3_rtc_battery_low_irq_enable(regmap, true);
if (ret) {
- dev_err(dev, "%s: enabling battery low interrupt "
- "generation failed (%d)\n", __func__, ret);
+ dev_err(dev, "%s: enabling battery low interrupt generation failed (%d)\n",
+ __func__, ret);
goto err;
}
}
@@ -958,7 +895,7 @@
ret = rtc_register_device(data->rtc);
err:
- if (ret && data && data->irq)
+ if (ret && data->irq)
device_init_wakeup(dev, false);
return ret;
}
diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c
new file mode 100644
index 0000000..d690985
--- /dev/null
+++ b/drivers/rtc/rtc-ab-eoz9.c
@@ -0,0 +1,457 @@
+// 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))
+ return -ENODEV;
+
+ 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);
+ return ret;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = regmap;
+ dev_set_drvdata(dev, data);
+
+ ret = abeoz9_rtc_setup(dev, client->dev.of_node);
+ if (ret)
+ return ret;
+
+ data->rtc = devm_rtc_allocate_device(dev);
+ ret = PTR_ERR_OR_ZERO(data->rtc);
+ if (ret)
+ return ret;
+
+ 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)
+ return ret;
+
+ abeoz9_hwmon_register(dev, data);
+ return 0;
+}
+
+#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");
diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c
index 821ff52..2ed6def 100644
--- a/drivers/rtc/rtc-ab3100.c
+++ b/drivers/rtc/rtc-ab3100.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007-2009 ST-Ericsson AB
- * License terms: GNU General Public License (GPL) version 2
* RTC clock driver for the AB3100 Analog Baseband Chip
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
@@ -43,12 +43,12 @@
/*
* RTC clock functions and device struct declaration
*/
-static int ab3100_rtc_set_mmss(struct device *dev, time64_t secs)
+static int ab3100_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2,
AB3100_TI3, AB3100_TI4, AB3100_TI5};
unsigned char buf[6];
- u64 hw_counter = secs * AB3100_RTC_CLOCK_RATE * 2;
+ u64 hw_counter = rtc_tm_to_time64(tm) * AB3100_RTC_CLOCK_RATE * 2;
int err = 0;
int i;
@@ -192,7 +192,7 @@
static const struct rtc_class_ops ab3100_rtc_ops = {
.read_time = ab3100_rtc_read_time,
- .set_mmss64 = ab3100_rtc_set_mmss,
+ .set_time = ab3100_rtc_set_time,
.read_alarm = ab3100_rtc_read_alarm,
.set_alarm = ab3100_rtc_set_alarm,
.alarm_irq_enable = ab3100_rtc_irq_enable,
@@ -228,15 +228,17 @@
/* Ignore any error on this write */
}
- rtc = devm_rtc_device_register(&pdev->dev, "ab3100-rtc",
- &ab3100_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- err = PTR_ERR(rtc);
- return err;
- }
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->ops = &ab3100_rtc_ops;
+ /* 48bit counter at (AB3100_RTC_CLOCK_RATE * 2) */
+ rtc->range_max = U32_MAX;
+
platform_set_drvdata(pdev, rtc);
- return 0;
+ return rtc_register_device(rtc);
}
static struct platform_driver ab3100_rtc_driver = {
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index e28f440..8492ffe 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) ST-Ericsson SA 2010
*
- * License terms: GNU General Public License (GPL) version 2
* Author: Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>
*
* RTC clock driver for the RTC part of the AB8500 Power management chip.
@@ -46,7 +46,6 @@
#define RTC_STATUS_DATA 0x01
#define COUNTS_PER_SEC (0xF000 / 60)
-#define AB8500_RTC_EPOCH 2000
static const u8 ab8500_rtc_time_regs[] = {
AB8500_RTC_WATCH_TMIN_HI_REG, AB8500_RTC_WATCH_TMIN_MID_REG,
@@ -59,23 +58,6 @@
AB8500_RTC_ALRM_MIN_LOW_REG
};
-/* Calculate the seconds from 1970 to 01-01-2000 00:00:00 */
-static unsigned long get_elapsed_seconds(int year)
-{
- unsigned long secs;
- struct rtc_time tm = {
- .tm_year = year - 1900,
- .tm_mday = 1,
- };
-
- /*
- * This function calculates secs from 1970 and not from
- * 1900, even if we supply the offset from year 1900.
- */
- rtc_tm_to_time(&tm, &secs);
- return secs;
-}
-
static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned long timeout = jiffies + HZ;
@@ -118,9 +100,6 @@
secs = secs / COUNTS_PER_SEC;
secs = secs + (mins * 60);
- /* Add back the initially subtracted number of seconds */
- secs += get_elapsed_seconds(AB8500_RTC_EPOCH);
-
rtc_time_to_tm(secs, tm);
return 0;
}
@@ -131,21 +110,8 @@
unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
unsigned long no_secs, no_mins, secs = 0;
- if (tm->tm_year < (AB8500_RTC_EPOCH - 1900)) {
- dev_dbg(dev, "year should be equal to or greater than %d\n",
- AB8500_RTC_EPOCH);
- return -EINVAL;
- }
-
- /* Get the number of seconds since 1970 */
rtc_tm_to_time(tm, &secs);
- /*
- * Convert it to the number of seconds since 01-01-2000 00:00:00, since
- * we only have a small counter in the RTC.
- */
- secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
-
no_mins = secs / 60;
no_secs = secs % 60;
@@ -202,12 +168,9 @@
mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);
secs = mins * 60;
- /* Add back the initially subtracted number of seconds */
- secs += get_elapsed_seconds(AB8500_RTC_EPOCH);
-
rtc_time_to_tm(secs, &alarm->time);
- return rtc_valid_tm(&alarm->time);
+ return 0;
}
static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled)
@@ -224,12 +187,6 @@
unsigned long mins, secs = 0, cursec = 0;
struct rtc_time curtm;
- if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
- dev_dbg(dev, "year should be equal to or greater than %d\n",
- AB8500_RTC_EPOCH);
- return -EINVAL;
- }
-
/* Get the number of seconds since 1970 */
rtc_tm_to_time(&alarm->time, &secs);
@@ -245,12 +202,6 @@
return -EINVAL;
}
- /*
- * Convert it to the number of seconds since 01-01-2000 00:00:00, since
- * we only have a small counter in the RTC.
- */
- secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
-
mins = secs / 60;
buf[2] = mins & 0xFF;
@@ -360,15 +311,14 @@
ab8500_sysfs_show_rtc_calibration,
ab8500_sysfs_store_rtc_calibration);
-static int ab8500_sysfs_rtc_register(struct device *dev)
-{
- return device_create_file(dev, &dev_attr_rtc_calibration);
-}
+static struct attribute *ab8500_rtc_attrs[] = {
+ &dev_attr_rtc_calibration.attr,
+ NULL
+};
-static void ab8500_sysfs_rtc_unregister(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_rtc_calibration);
-}
+static const struct attribute_group ab8500_rtc_sysfs_files = {
+ .attrs = ab8500_rtc_attrs,
+};
static irqreturn_t rtc_alarm_handler(int irq, void *data)
{
@@ -429,14 +379,11 @@
device_init_wakeup(&pdev->dev, true);
- rtc = devm_rtc_device_register(&pdev->dev, "ab8500-rtc",
- (struct rtc_class_ops *)platid->driver_data,
- THIS_MODULE);
- if (IS_ERR(rtc)) {
- dev_err(&pdev->dev, "Registration failed\n");
- err = PTR_ERR(rtc);
- return err;
- }
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->ops = (struct rtc_class_ops *)platid->driver_data;
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
rtc_alarm_handler, IRQF_ONESHOT,
@@ -447,22 +394,23 @@
dev_pm_set_wake_irq(&pdev->dev, irq);
platform_set_drvdata(pdev, rtc);
- err = ab8500_sysfs_rtc_register(&pdev->dev);
- if (err) {
- dev_err(&pdev->dev, "sysfs RTC failed to register\n");
- return err;
- }
-
rtc->uie_unsupported = 1;
- return 0;
+ rtc->range_max = (1ULL << 24) * 60 - 1; // 24-bit minutes + 59 secs
+ rtc->start_secs = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->set_start_time = true;
+
+ err = rtc_add_group(rtc, &ab8500_rtc_sysfs_files);
+ if (err)
+ return err;
+
+ return rtc_register_device(rtc);
}
static int ab8500_rtc_remove(struct platform_device *pdev)
{
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
- ab8500_sysfs_rtc_unregister(&pdev->dev);
return 0;
}
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 2cefa67..7383067 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* A driver for the I2C members of the Abracon AB x8xx RTC family,
* and compatible: AB 1805 and AB 0805
@@ -5,11 +6,7 @@
* Copyright 2014-2015 Macq S.A.
*
* Author: Philippe De Muyter <phdm@macqel.be>
- * Author: 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.
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
*
*/
@@ -17,6 +14,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/rtc.h>
+#include <linux/watchdog.h>
#define ABX8XX_REG_HTH 0x00
#define ABX8XX_REG_SC 0x01
@@ -37,12 +35,17 @@
#define ABX8XX_REG_STATUS 0x0f
#define ABX8XX_STATUS_AF BIT(2)
+#define ABX8XX_STATUS_BLF BIT(4)
+#define ABX8XX_STATUS_WDT BIT(6)
#define ABX8XX_REG_CTRL1 0x10
#define ABX8XX_CTRL_WRITE BIT(0)
#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)
@@ -61,12 +64,23 @@
#define ABX8XX_OSS_OF BIT(1)
#define ABX8XX_OSS_OMODE BIT(4)
+#define ABX8XX_REG_WDT 0x1b
+#define ABX8XX_WDT_WDS BIT(7)
+#define ABX8XX_WDT_BMB_MASK 0x7c
+#define ABX8XX_WDT_BMB_SHIFT 2
+#define ABX8XX_WDT_MAX_TIME (ABX8XX_WDT_BMB_MASK >> ABX8XX_WDT_BMB_SHIFT)
+#define ABX8XX_WDT_WRB_MASK 0x03
+#define ABX8XX_WDT_WRB_1HZ 0x02
+
#define ABX8XX_REG_CFG_KEY 0x1f
#define ABX8XX_CFG_KEY_OSC 0xa1
#define ABX8XX_CFG_KEY_MISC 0x9d
#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
@@ -75,25 +89,33 @@
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;
bool has_tc;
+ bool has_wdog;
};
static struct abx80x_cap abx80x_caps[] = {
[AB0801] = {.pn = 0x0801},
[AB0803] = {.pn = 0x0803},
- [AB0804] = {.pn = 0x0804, .has_tc = true},
- [AB0805] = {.pn = 0x0805, .has_tc = true},
+ [AB0804] = {.pn = 0x0804, .has_tc = true, .has_wdog = true},
+ [AB0805] = {.pn = 0x0805, .has_tc = true, .has_wdog = true},
[AB1801] = {.pn = 0x1801},
[AB1803] = {.pn = 0x1803},
- [AB1804] = {.pn = 0x1804, .has_tc = true},
- [AB1805] = {.pn = 0x1805, .has_tc = true},
+ [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}
};
+struct abx80x_priv {
+ struct rtc_device *rtc;
+ struct i2c_client *client;
+ struct watchdog_device wdog;
+};
+
static int abx80x_is_rc_mode(struct i2c_client *client)
{
int flags = 0;
@@ -218,7 +240,8 @@
static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
{
struct i2c_client *client = dev_id;
- struct rtc_device *rtc = i2c_get_clientdata(client);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
+ struct rtc_device *rtc = priv->rtc;
int status;
status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
@@ -228,6 +251,13 @@
if (status & ABX8XX_STATUS_AF)
rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
+ /*
+ * It is unclear if we'll get an interrupt before the external
+ * reset kicks in.
+ */
+ if (status & ABX8XX_STATUS_WDT)
+ dev_alert(&client->dev, "watchdog timeout interrupt.\n");
+
i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0);
return IRQ_HANDLED;
@@ -371,7 +401,7 @@
return -EINVAL;
}
- retval = abx80x_rtc_set_autocalibration(dev, autocalibration);
+ retval = abx80x_rtc_set_autocalibration(dev->parent, autocalibration);
return retval ? retval : count;
}
@@ -381,7 +411,7 @@
{
int autocalibration = 0;
- autocalibration = abx80x_rtc_get_autocalibration(dev);
+ autocalibration = abx80x_rtc_get_autocalibration(dev->parent);
if (autocalibration < 0) {
dev_err(dev, "Failed to read RTC autocalibration\n");
sprintf(buf, "0\n");
@@ -397,7 +427,7 @@
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
int retval, flags, rc_mode = 0;
if (strncmp(buf, "rc", 2) == 0) {
@@ -439,7 +469,7 @@
struct device_attribute *attr, char *buf)
{
int rc_mode = 0;
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
rc_mode = abx80x_is_rc_mode(client);
@@ -482,12 +512,49 @@
return err;
}
+static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int status, tmp;
+
+ switch (cmd) {
+ case RTC_VL_READ:
+ status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
+ if (status < 0)
+ return status;
+
+ tmp = !!(status & ABX8XX_STATUS_BLF);
+
+ if (copy_to_user((void __user *)arg, &tmp, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+
+ case RTC_VL_CLR:
+ status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
+ if (status < 0)
+ return status;
+
+ status &= ~ABX8XX_STATUS_BLF;
+
+ tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0);
+ if (tmp < 0)
+ return tmp;
+
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
static const struct rtc_class_ops abx80x_rtc_ops = {
.read_time = abx80x_rtc_read_time,
.set_time = abx80x_rtc_set_time,
.read_alarm = abx80x_read_alarm,
.set_alarm = abx80x_set_alarm,
.alarm_irq_enable = abx80x_alarm_irq_enable,
+ .ioctl = abx80x_ioctl,
};
static int abx80x_dt_trickle_cfg(struct device_node *np)
@@ -522,18 +589,94 @@
return (trickle_cfg | i);
}
-static void rtc_calib_remove_sysfs_group(void *_dev)
-{
- struct device *dev = _dev;
+#ifdef CONFIG_WATCHDOG
- sysfs_remove_group(&dev->kobj, &rtc_calib_attr_group);
+static inline u8 timeout_bits(unsigned int timeout)
+{
+ return ((timeout << ABX8XX_WDT_BMB_SHIFT) & ABX8XX_WDT_BMB_MASK) |
+ ABX8XX_WDT_WRB_1HZ;
}
+static int __abx80x_wdog_set_timeout(struct watchdog_device *wdog,
+ unsigned int timeout)
+{
+ struct abx80x_priv *priv = watchdog_get_drvdata(wdog);
+ u8 val = ABX8XX_WDT_WDS | timeout_bits(timeout);
+
+ /*
+ * Writing any timeout to the WDT register resets the watchdog timer.
+ * Writing 0 disables it.
+ */
+ return i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_WDT, val);
+}
+
+static int abx80x_wdog_set_timeout(struct watchdog_device *wdog,
+ unsigned int new_timeout)
+{
+ int err = 0;
+
+ if (watchdog_hw_running(wdog))
+ err = __abx80x_wdog_set_timeout(wdog, new_timeout);
+
+ if (err == 0)
+ wdog->timeout = new_timeout;
+
+ return err;
+}
+
+static int abx80x_wdog_ping(struct watchdog_device *wdog)
+{
+ return __abx80x_wdog_set_timeout(wdog, wdog->timeout);
+}
+
+static int abx80x_wdog_start(struct watchdog_device *wdog)
+{
+ return __abx80x_wdog_set_timeout(wdog, wdog->timeout);
+}
+
+static int abx80x_wdog_stop(struct watchdog_device *wdog)
+{
+ return __abx80x_wdog_set_timeout(wdog, 0);
+}
+
+static const struct watchdog_info abx80x_wdog_info = {
+ .identity = "abx80x watchdog",
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops abx80x_wdog_ops = {
+ .owner = THIS_MODULE,
+ .start = abx80x_wdog_start,
+ .stop = abx80x_wdog_stop,
+ .ping = abx80x_wdog_ping,
+ .set_timeout = abx80x_wdog_set_timeout,
+};
+
+static int abx80x_setup_watchdog(struct abx80x_priv *priv)
+{
+ priv->wdog.parent = &priv->client->dev;
+ priv->wdog.ops = &abx80x_wdog_ops;
+ priv->wdog.info = &abx80x_wdog_info;
+ priv->wdog.min_timeout = 1;
+ priv->wdog.max_timeout = ABX8XX_WDT_MAX_TIME;
+ priv->wdog.timeout = ABX8XX_WDT_MAX_TIME;
+
+ watchdog_set_drvdata(&priv->wdog, priv);
+
+ return devm_watchdog_register_device(&priv->client->dev, &priv->wdog);
+}
+#else
+static int abx80x_setup_watchdog(struct abx80x_priv *priv)
+{
+ return 0;
+}
+#endif
+
static int abx80x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device_node *np = client->dev.of_node;
- struct rtc_device *rtc;
+ struct abx80x_priv *priv;
int i, data, err, trickle_cfg = -EINVAL;
char buf[7];
unsigned int part = id->driver_data;
@@ -577,6 +720,62 @@
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++)
@@ -610,13 +809,24 @@
if (err)
return err;
- rtc = devm_rtc_allocate_device(&client->dev);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
- rtc->ops = &abx80x_rtc_ops;
+ priv->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(priv->rtc))
+ return PTR_ERR(priv->rtc);
- i2c_set_clientdata(client, rtc);
+ priv->rtc->ops = &abx80x_rtc_ops;
+ priv->client = client;
+
+ i2c_set_clientdata(client, priv);
+
+ if (abx80x_caps[part].has_wdog) {
+ err = abx80x_setup_watchdog(priv);
+ if (err)
+ return err;
+ }
if (client->irq > 0) {
dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
@@ -631,32 +841,14 @@
}
}
- /* Export sysfs entries */
- err = sysfs_create_group(&(&client->dev)->kobj, &rtc_calib_attr_group);
+ err = rtc_add_group(priv->rtc, &rtc_calib_attr_group);
if (err) {
dev_err(&client->dev, "Failed to create sysfs group: %d\n",
err);
return err;
}
- err = devm_add_action_or_reset(&client->dev,
- rtc_calib_remove_sysfs_group,
- &client->dev);
- if (err) {
- dev_err(&client->dev,
- "Failed to add sysfs cleanup action: %d\n",
- err);
- return err;
- }
-
- err = rtc_register_device(rtc);
-
- return err;
-}
-
-static int abx80x_remove(struct i2c_client *client)
-{
- return 0;
+ return rtc_register_device(priv->rtc);
}
static const struct i2c_device_id abx80x_id[] = {
@@ -669,7 +861,7 @@
{ "ab1803", AB1803 },
{ "ab1804", AB1804 },
{ "ab1805", AB1805 },
- { "rv1805", AB1805 },
+ { "rv1805", RV1805 },
{ }
};
MODULE_DEVICE_TABLE(i2c, abx80x_id);
@@ -679,13 +871,12 @@
.name = "rtc-abx80x",
},
.probe = abx80x_probe,
- .remove = abx80x_remove,
.id_table = abx80x_id,
};
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-ac100.c b/drivers/rtc/rtc-ac100.c
index 784b676..2922393 100644
--- a/drivers/rtc/rtc-ac100.c
+++ b/drivers/rtc/rtc-ac100.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* RTC Driver for X-Powers AC100
*
* Copyright (c) 2016 Chen-Yu Tsai
*
* Chen-Yu Tsai <wens@csie.org>
- *
- * 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.
- *
- * 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/bcd.h>
@@ -586,10 +578,8 @@
chip->regmap = ac100->regmap;
chip->irq = platform_get_irq(pdev, 0);
- if (chip->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource\n");
+ if (chip->irq < 0)
return chip->irq;
- }
chip->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(chip->rtc))
@@ -620,15 +610,7 @@
if (ret)
return ret;
- ret = rtc_register_device(chip->rtc);
- if (ret) {
- dev_err(&pdev->dev, "unable to register device\n");
- return ret;
- }
-
- dev_info(&pdev->dev, "RTC enabled\n");
-
- return 0;
+ return rtc_register_device(chip->rtc);
}
static int ac100_rtc_remove(struct platform_device *pdev)
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index bde53c8..9351bd5 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RTC driver for the Armada 38x Marvell SoCs
*
* Copyright (C) 2015 Marvell
*
* Gregory Clement <gregory.clement@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 as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
*/
#include <linux/delay.h>
@@ -224,7 +219,7 @@
time = rtc->data->read_rtc_reg(rtc, RTC_TIME);
spin_unlock_irqrestore(&rtc->lock, flags);
- rtc_time_to_tm(time, tm);
+ rtc_time64_to_tm(time, tm);
return 0;
}
@@ -249,13 +244,9 @@
static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
- int ret = 0;
unsigned long time, flags;
- ret = rtc_tm_to_time(tm, &time);
-
- if (ret)
- goto out;
+ time = rtc_tm_to_time64(tm);
if (!rtc->initialized)
armada38x_rtc_reset(rtc);
@@ -264,8 +255,7 @@
rtc_delayed_write(time, rtc, RTC_TIME);
spin_unlock_irqrestore(&rtc->lock, flags);
-out:
- return ret;
+ return 0;
}
static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -284,7 +274,7 @@
spin_unlock_irqrestore(&rtc->lock, flags);
alrm->enabled = val ? 1 : 0;
- rtc_time_to_tm(time, &alrm->time);
+ rtc_time64_to_tm(time, &alrm->time);
return 0;
}
@@ -295,12 +285,8 @@
u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
unsigned long time, flags;
- int ret = 0;
- ret = rtc_tm_to_time(&alrm->time, &time);
-
- if (ret)
- goto out;
+ time = rtc_tm_to_time64(&alrm->time);
spin_lock_irqsave(&rtc->lock, flags);
@@ -313,8 +299,7 @@
spin_unlock_irqrestore(&rtc->lock, flags);
-out:
- return ret;
+ return 0;
}
static int armada38x_rtc_alarm_irq_enable(struct device *dev,
@@ -514,11 +499,9 @@
static __init int armada38x_rtc_probe(struct platform_device *pdev)
{
- const struct rtc_class_ops *ops;
struct resource *res;
struct armada38x_rtc *rtc;
const struct of_device_id *match;
- int ret;
match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev);
if (!match)
@@ -546,11 +529,13 @@
return PTR_ERR(rtc->regs_soc);
rtc->irq = platform_get_irq(pdev, 0);
-
- if (rtc->irq < 0) {
- dev_err(&pdev->dev, "no irq\n");
+ if (rtc->irq < 0)
return rtc->irq;
- }
+
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
0, pdev->name, rtc) < 0) {
dev_warn(&pdev->dev, "Interrupt not available.\n");
@@ -560,28 +545,22 @@
if (rtc->irq != -1) {
device_init_wakeup(&pdev->dev, 1);
- ops = &armada38x_rtc_ops;
+ rtc->rtc_dev->ops = &armada38x_rtc_ops;
} else {
/*
* If there is no interrupt available then we can't
* use the alarm
*/
- ops = &armada38x_rtc_ops_noirq;
+ rtc->rtc_dev->ops = &armada38x_rtc_ops_noirq;
}
rtc->data = (struct armada38x_rtc_data *)match->data;
-
/* Update RTC-MBUS bridge timing parameters */
rtc->data->update_mbus_timing(rtc);
- rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
- ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev)) {
- ret = PTR_ERR(rtc->rtc_dev);
- dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
- return ret;
- }
- return 0;
+ rtc->rtc_dev->range_max = U32_MAX;
+
+ return rtc_register_device(rtc->rtc_dev);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c
index 6ef0c88..0f21af2 100644
--- a/drivers/rtc/rtc-as3722.c
+++ b/drivers/rtc/rtc-as3722.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* rtc-as3722.c - Real Time Clock driver for ams AS3722 PMICs
*
@@ -6,16 +7,6 @@
*
* Author: Florian Lobmaier <florian.lobmaier@ams.com>
* Author: Laxman Dewangan <ldewangan@nvidia.com>
- *
- * 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/bcd.h>
diff --git a/drivers/rtc/rtc-asm9260.c b/drivers/rtc/rtc-asm9260.c
index d365349..10413d8 100644
--- a/drivers/rtc/rtc-asm9260.c
+++ b/drivers/rtc/rtc-asm9260.c
@@ -1,10 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Oleksij Rempel <linux@rempel-privat.de>
- *
- * 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.
*/
#include <linux/clk.h>
@@ -261,10 +257,8 @@
platform_set_drvdata(pdev, priv);
irq_alarm = platform_get_irq(pdev, 0);
- if (irq_alarm < 0) {
- dev_err(dev, "No alarm IRQ resource defined\n");
+ if (irq_alarm < 0)
return irq_alarm;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->iobase = devm_ioremap_resource(dev, res);
diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c
new file mode 100644
index 0000000..e351d35
--- /dev/null
+++ b/drivers/rtc/rtc-aspeed.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2015 IBM Corp.
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/io.h>
+
+struct aspeed_rtc {
+ struct rtc_device *rtc_dev;
+ void __iomem *base;
+};
+
+#define RTC_TIME 0x00
+#define RTC_YEAR 0x04
+#define RTC_CTRL 0x10
+
+#define RTC_UNLOCK BIT(1)
+#define RTC_ENABLE BIT(0)
+
+static int aspeed_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct aspeed_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int cent, year;
+ u32 reg1, reg2;
+
+ if (!(readl(rtc->base + RTC_CTRL) & RTC_ENABLE)) {
+ dev_dbg(dev, "%s failing as rtc disabled\n", __func__);
+ return -EINVAL;
+ }
+
+ do {
+ reg2 = readl(rtc->base + RTC_YEAR);
+ reg1 = readl(rtc->base + RTC_TIME);
+ } while (reg2 != readl(rtc->base + RTC_YEAR));
+
+ tm->tm_mday = (reg1 >> 24) & 0x1f;
+ tm->tm_hour = (reg1 >> 16) & 0x1f;
+ tm->tm_min = (reg1 >> 8) & 0x3f;
+ tm->tm_sec = (reg1 >> 0) & 0x3f;
+
+ cent = (reg2 >> 16) & 0x1f;
+ year = (reg2 >> 8) & 0x7f;
+ tm->tm_mon = ((reg2 >> 0) & 0x0f) - 1;
+ tm->tm_year = year + (cent * 100) - 1900;
+
+ dev_dbg(dev, "%s %ptR", __func__, tm);
+
+ return 0;
+}
+
+static int aspeed_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct aspeed_rtc *rtc = dev_get_drvdata(dev);
+ u32 reg1, reg2, ctrl;
+ int year, cent;
+
+ cent = (tm->tm_year + 1900) / 100;
+ year = tm->tm_year % 100;
+
+ reg1 = (tm->tm_mday << 24) | (tm->tm_hour << 16) | (tm->tm_min << 8) |
+ tm->tm_sec;
+
+ reg2 = ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) |
+ ((tm->tm_mon + 1) & 0xf);
+
+ ctrl = readl(rtc->base + RTC_CTRL);
+ writel(ctrl | RTC_UNLOCK, rtc->base + RTC_CTRL);
+
+ writel(reg1, rtc->base + RTC_TIME);
+ writel(reg2, rtc->base + RTC_YEAR);
+
+ /* Re-lock and ensure enable is set now that a time is programmed */
+ writel(ctrl | RTC_ENABLE, rtc->base + RTC_CTRL);
+
+ return 0;
+}
+
+static const struct rtc_class_ops aspeed_rtc_ops = {
+ .read_time = aspeed_rtc_read_time,
+ .set_time = aspeed_rtc_set_time,
+};
+
+static int aspeed_rtc_probe(struct platform_device *pdev)
+{
+ struct aspeed_rtc *rtc;
+ struct resource *res;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->base))
+ return PTR_ERR(rtc->base);
+
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->rtc_dev->ops = &aspeed_rtc_ops;
+ rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_1900;
+ rtc->rtc_dev->range_max = 38814989399LL; /* 3199-12-31 23:59:59 */
+
+ return rtc_register_device(rtc->rtc_dev);
+}
+
+static const struct of_device_id aspeed_rtc_match[] = {
+ { .compatible = "aspeed,ast2400-rtc", },
+ { .compatible = "aspeed,ast2500-rtc", },
+ { .compatible = "aspeed,ast2600-rtc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aspeed_rtc_match);
+
+static struct platform_driver aspeed_rtc_driver = {
+ .driver = {
+ .name = "aspeed-rtc",
+ .of_match_table = of_match_ptr(aspeed_rtc_match),
+ },
+};
+
+module_platform_driver_probe(aspeed_rtc_driver, aspeed_rtc_probe);
+
+MODULE_DESCRIPTION("ASPEED RTC driver");
+MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index caa71d0..d119c6e 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Real Time Clock interface for Linux on Atmel AT91RM9200
*
@@ -10,12 +11,6 @@
*
* Based on sa1100-rtc.c by Nils Faerber
* Based on rtc.c by Paul Gortmaker
- *
- * 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.
- *
*/
#include <linux/bcd.h>
@@ -147,9 +142,7 @@
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
tm->tm_year = tm->tm_year - 1900;
- dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "%s(): %ptR\n", __func__, tm);
return 0;
}
@@ -161,9 +154,7 @@
{
unsigned long cr;
- dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "%s(): %ptR\n", __func__, tm);
wait_for_completion(&at91_rtc_upd_rdy);
@@ -209,8 +200,7 @@
alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM)
? 1 : 0;
- dev_dbg(dev, "%s(): %02d-%02d %02d:%02d:%02d %sabled\n", __func__,
- tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
+ dev_dbg(dev, "%s(): %ptR %sabled\n", __func__, tm,
alrm->enabled ? "en" : "dis");
return 0;
@@ -247,9 +237,7 @@
at91_rtc_write_ier(AT91_RTC_ALARM);
}
- dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
- tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
- tm.tm_min, tm.tm_sec);
+ dev_dbg(dev, "%s(): %ptR\n", __func__, &tm);
return 0;
}
@@ -390,10 +378,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource defined\n");
+ if (irq < 0)
return -ENXIO;
- }
at91_rtc_regs = devm_ioremap(&pdev->dev, regs->start,
resource_size(regs));
diff --git a/drivers/rtc/rtc-at91rm9200.h b/drivers/rtc/rtc-at91rm9200.h
index da1945e..8be5289 100644
--- a/drivers/rtc/rtc-at91rm9200.h
+++ b/drivers/rtc/rtc-at91rm9200.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* arch/arm/mach-at91/include/mach/at91_rtc.h
*
@@ -6,11 +7,6 @@
*
* Real Time Clock (RTC) - System peripheral registers.
* Based on AT91RM9200 datasheet revision E.
- *
- * 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.
*/
#ifndef AT91_RTC_H
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index ee71e64..bb3ba7b 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* "RTT as Real Time Clock" driver for AT91SAM9 SoC family
*
* (C) 2007 Michel Benoit
*
* Based on rtc-at91rm9200.c by Rick Bronson
- *
- * 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.
*/
#include <linux/clk.h>
@@ -47,21 +43,21 @@
* registers available, likewise usable for more than "RTC" support.
*/
-#define AT91_RTT_MR 0x00 /* Real-time Mode Register */
-#define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */
-#define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */
-#define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */
-#define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */
+#define AT91_RTT_MR 0x00 /* Real-time Mode Register */
+#define AT91_RTT_RTPRES (0xffff << 0) /* Timer Prescaler Value */
+#define AT91_RTT_ALMIEN BIT(16) /* Alarm Interrupt Enable */
+#define AT91_RTT_RTTINCIEN BIT(17) /* Increment Interrupt Enable */
+#define AT91_RTT_RTTRST BIT(18) /* Timer Restart */
-#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */
-#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */
+#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */
+#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */
-#define AT91_RTT_VR 0x08 /* Real-time Value Register */
-#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */
+#define AT91_RTT_VR 0x08 /* Real-time Value Register */
+#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */
-#define AT91_RTT_SR 0x0c /* Real-time Status Register */
-#define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */
-#define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */
+#define AT91_RTT_SR 0x0c /* Real-time Status Register */
+#define AT91_RTT_ALMS BIT(0) /* Alarm Status */
+#define AT91_RTT_RTTINC BIT(1) /* Timer Increment */
/*
* We store ALARM_DISABLED in ALMV to record that no alarm is set.
@@ -69,14 +65,13 @@
*/
#define ALARM_DISABLED ((u32)~0)
-
struct sam9_rtc {
void __iomem *rtt;
struct rtc_device *rtcdev;
u32 imr;
struct regmap *gpbr;
unsigned int gpbr_offset;
- int irq;
+ int irq;
struct clk *sclk;
bool suspended;
unsigned long events;
@@ -122,11 +117,9 @@
if (secs != secs2)
secs = rtt_readl(rtc, VR);
- rtc_time_to_tm(offset + secs, tm);
+ rtc_time64_to_tm(offset + secs, tm);
- dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readtime",
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "%s: %ptR\n", __func__, tm);
return 0;
}
@@ -137,17 +130,12 @@
static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct sam9_rtc *rtc = dev_get_drvdata(dev);
- int err;
u32 offset, alarm, mr;
unsigned long secs;
- dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "settime",
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "%s: %ptR\n", __func__, tm);
- err = rtc_tm_to_time(tm, &secs);
- if (err != 0)
- return err;
+ secs = rtc_tm_to_time64(tm);
mr = rtt_readl(rtc, MR);
@@ -197,11 +185,9 @@
memset(alrm, 0, sizeof(*alrm));
if (alarm != ALARM_DISABLED && offset != 0) {
- rtc_time_to_tm(offset + alarm, tm);
+ rtc_time64_to_tm(offset + alarm, tm);
- dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readalarm",
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "%s: %ptR\n", __func__, tm);
if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN)
alrm->enabled = 1;
@@ -217,11 +203,8 @@
unsigned long secs;
u32 offset;
u32 mr;
- int err;
- err = rtc_tm_to_time(tm, &secs);
- if (err != 0)
- return err;
+ secs = rtc_tm_to_time64(tm);
offset = gpbr_readl(rtc);
if (offset == 0) {
@@ -242,9 +225,7 @@
if (alrm->enabled)
rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN);
- dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "setalarm",
- tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour,
- tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "%s: %ptR\n", __func__, tm);
return 0;
}
@@ -271,7 +252,7 @@
u32 mr = rtt_readl(rtc, MR);
seq_printf(seq, "update_IRQ\t: %s\n",
- (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no");
+ (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no");
return 0;
}
@@ -307,7 +288,7 @@
rtc->events = 0;
pr_debug("%s: num=%ld, events=0x%02lx\n", __func__,
- rtc->events >> 8, rtc->events & 0x000000FF);
+ rtc->events >> 8, rtc->events & 0x000000FF);
}
/*
@@ -348,13 +329,6 @@
.alarm_irq_enable = at91_rtc_alarm_irq_enable,
};
-static const struct regmap_config gpbr_regmap_config = {
- .name = "gpbr",
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
-};
-
/*
* Initialize and install RTC driver
*/
@@ -365,12 +339,11 @@
int ret, irq;
u32 mr;
unsigned int sclk_rate;
+ struct of_phandle_args args;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "failed to get interrupt resource\n");
+ if (irq < 0)
return irq;
- }
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
@@ -390,34 +363,14 @@
if (IS_ERR(rtc->rtt))
return PTR_ERR(rtc->rtt);
- if (!pdev->dev.of_node) {
- /*
- * TODO: Remove this code chunk when removing non DT board
- * support. Remember to remove the gpbr_regmap_config
- * variable too.
- */
- void __iomem *gpbr;
+ ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+ "atmel,rtt-rtc-time-reg", 1, 0,
+ &args);
+ if (ret)
+ return ret;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- gpbr = devm_ioremap_resource(&pdev->dev, r);
- if (IS_ERR(gpbr))
- return PTR_ERR(gpbr);
-
- rtc->gpbr = regmap_init_mmio(NULL, gpbr,
- &gpbr_regmap_config);
- } else {
- struct of_phandle_args args;
-
- ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
- "atmel,rtt-rtc-time-reg", 1, 0,
- &args);
- if (ret)
- return ret;
-
- rtc->gpbr = syscon_node_to_regmap(args.np);
- rtc->gpbr_offset = args.args[0];
- }
-
+ rtc->gpbr = syscon_node_to_regmap(args.np);
+ rtc->gpbr_offset = args.args[0];
if (IS_ERR(rtc->gpbr)) {
dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n");
return -ENOMEM;
@@ -452,13 +405,15 @@
mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
rtt_writel(rtc, MR, mr);
- rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name,
- &at91_rtc_ops, THIS_MODULE);
+ rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc->rtcdev)) {
ret = PTR_ERR(rtc->rtcdev);
goto err_clk;
}
+ rtc->rtcdev->ops = &at91_rtc_ops;
+ rtc->rtcdev->range_max = U32_MAX;
+
/* register irq handler after we know what name we'll use */
ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt,
IRQF_SHARED | IRQF_COND_SUSPEND,
@@ -476,9 +431,9 @@
if (gpbr_readl(rtc) == 0)
dev_warn(&pdev->dev, "%s: SET TIME!\n",
- dev_name(&rtc->rtcdev->dev));
+ dev_name(&rtc->rtcdev->dev));
- return 0;
+ return rtc_register_device(rtc->rtcdev);
err_clk:
clk_disable_unprepare(rtc->sclk);
@@ -536,8 +491,9 @@
/* don't let RTTINC cause wakeups */
if (mr & AT91_RTT_RTTINCIEN)
rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
- } else
+ } else {
rtt_writel(rtc, MR, mr & ~rtc->imr);
+ }
}
return 0;
@@ -569,13 +525,11 @@
static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
-#ifdef CONFIG_OF
static const struct of_device_id at91_rtc_dt_ids[] = {
{ .compatible = "atmel,at91sam9260-rtt" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids);
-#endif
static struct platform_driver at91_rtc_driver = {
.probe = at91_rtc_probe,
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
new file mode 100644
index 0000000..7744333
--- /dev/null
+++ b/drivers/rtc/rtc-bd70528.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Copyright (C) 2018 ROHM Semiconductors
+//
+// RTC driver for ROHM BD70528 PMIC
+
+#include <linux/bcd.h>
+#include <linux/mfd/rohm-bd70528.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+
+/*
+ * We read regs RTC_SEC => RTC_YEAR
+ * this struct is ordered according to chip registers.
+ * Keep it u8 only to avoid padding issues.
+ */
+struct bd70528_rtc_day {
+ u8 sec;
+ u8 min;
+ u8 hour;
+} __packed;
+
+struct bd70528_rtc_data {
+ struct bd70528_rtc_day time;
+ u8 week;
+ u8 day;
+ u8 month;
+ u8 year;
+} __packed;
+
+struct bd70528_rtc_wake {
+ struct bd70528_rtc_day time;
+ u8 ctrl;
+} __packed;
+
+struct bd70528_rtc_alm {
+ struct bd70528_rtc_data data;
+ u8 alm_mask;
+ u8 alm_repeat;
+} __packed;
+
+struct bd70528_rtc {
+ struct rohm_regmap_dev *mfd;
+ struct device *dev;
+};
+
+static int bd70528_set_wake(struct rohm_regmap_dev *bd70528,
+ int enable, int *old_state)
+{
+ int ret;
+ unsigned int ctrl_reg;
+
+ ret = regmap_read(bd70528->regmap, BD70528_REG_WAKE_EN, &ctrl_reg);
+ if (ret)
+ return ret;
+
+ if (old_state) {
+ if (ctrl_reg & BD70528_MASK_WAKE_EN)
+ *old_state |= BD70528_WAKE_STATE_BIT;
+ else
+ *old_state &= ~BD70528_WAKE_STATE_BIT;
+
+ if (!enable == !(*old_state & BD70528_WAKE_STATE_BIT))
+ return 0;
+ }
+
+ if (enable)
+ ctrl_reg |= BD70528_MASK_WAKE_EN;
+ else
+ ctrl_reg &= ~BD70528_MASK_WAKE_EN;
+
+ return regmap_write(bd70528->regmap, BD70528_REG_WAKE_EN,
+ ctrl_reg);
+}
+
+static int bd70528_set_elapsed_tmr(struct rohm_regmap_dev *bd70528,
+ int enable, int *old_state)
+{
+ int ret;
+ unsigned int ctrl_reg;
+
+ /*
+ * TBD
+ * What is the purpose of elapsed timer ?
+ * Is the timeout registers counting down, or is the disable - re-enable
+ * going to restart the elapsed-time counting? If counting is restarted
+ * the timeout should be decreased by the amount of time that has
+ * elapsed since starting the timer. Maybe we should store the monotonic
+ * clock value when timer is started so that if RTC is set while timer
+ * is armed we could do the compensation. This is a hack if RTC/system
+ * clk are drifting. OTOH, RTC controlled via I2C is in any case
+ * inaccurate...
+ */
+ ret = regmap_read(bd70528->regmap, BD70528_REG_ELAPSED_TIMER_EN,
+ &ctrl_reg);
+ if (ret)
+ return ret;
+
+ if (old_state) {
+ if (ctrl_reg & BD70528_MASK_ELAPSED_TIMER_EN)
+ *old_state |= BD70528_ELAPSED_STATE_BIT;
+ else
+ *old_state &= ~BD70528_ELAPSED_STATE_BIT;
+
+ if ((!enable) == (!(*old_state & BD70528_ELAPSED_STATE_BIT)))
+ return 0;
+ }
+
+ if (enable)
+ ctrl_reg |= BD70528_MASK_ELAPSED_TIMER_EN;
+ else
+ ctrl_reg &= ~BD70528_MASK_ELAPSED_TIMER_EN;
+
+ return regmap_write(bd70528->regmap, BD70528_REG_ELAPSED_TIMER_EN,
+ ctrl_reg);
+}
+
+static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
+ int *old_state)
+{
+ int ret;
+
+ ret = bd70528_wdt_set(r->mfd, new_state & BD70528_WDT_STATE_BIT,
+ old_state);
+ if (ret) {
+ dev_err(r->dev,
+ "Failed to disable WDG for RTC setting (%d)\n", ret);
+ return ret;
+ }
+ ret = bd70528_set_elapsed_tmr(r->mfd,
+ new_state & BD70528_ELAPSED_STATE_BIT,
+ old_state);
+ if (ret) {
+ dev_err(r->dev,
+ "Failed to disable 'elapsed timer' for RTC setting\n");
+ return ret;
+ }
+ ret = bd70528_set_wake(r->mfd, new_state & BD70528_WAKE_STATE_BIT,
+ old_state);
+ if (ret) {
+ dev_err(r->dev,
+ "Failed to disable 'wake timer' for RTC setting\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int bd70528_re_enable_rtc_based_timers(struct bd70528_rtc *r,
+ int old_state)
+{
+ return bd70528_set_rtc_based_timers(r, old_state, NULL);
+}
+
+static int bd70528_disable_rtc_based_timers(struct bd70528_rtc *r,
+ int *old_state)
+{
+ return bd70528_set_rtc_based_timers(r, 0, old_state);
+}
+
+static inline void tmday2rtc(struct rtc_time *t, struct bd70528_rtc_day *d)
+{
+ d->sec &= ~BD70528_MASK_RTC_SEC;
+ d->min &= ~BD70528_MASK_RTC_MINUTE;
+ d->hour &= ~BD70528_MASK_RTC_HOUR;
+ d->sec |= bin2bcd(t->tm_sec);
+ d->min |= bin2bcd(t->tm_min);
+ d->hour |= bin2bcd(t->tm_hour);
+}
+
+static inline void tm2rtc(struct rtc_time *t, struct bd70528_rtc_data *r)
+{
+ r->day &= ~BD70528_MASK_RTC_DAY;
+ r->week &= ~BD70528_MASK_RTC_WEEK;
+ r->month &= ~BD70528_MASK_RTC_MONTH;
+ /*
+ * PM and 24H bits are not used by Wake - thus we clear them
+ * here and not in tmday2rtc() which is also used by wake.
+ */
+ r->time.hour &= ~(BD70528_MASK_RTC_HOUR_PM | BD70528_MASK_RTC_HOUR_24H);
+
+ tmday2rtc(t, &r->time);
+ /*
+ * We do always set time in 24H mode.
+ */
+ r->time.hour |= BD70528_MASK_RTC_HOUR_24H;
+ r->day |= bin2bcd(t->tm_mday);
+ r->week |= bin2bcd(t->tm_wday);
+ r->month |= bin2bcd(t->tm_mon + 1);
+ r->year = bin2bcd(t->tm_year - 100);
+}
+
+static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t)
+{
+ t->tm_sec = bcd2bin(r->time.sec & BD70528_MASK_RTC_SEC);
+ t->tm_min = bcd2bin(r->time.min & BD70528_MASK_RTC_MINUTE);
+ t->tm_hour = bcd2bin(r->time.hour & BD70528_MASK_RTC_HOUR);
+ /*
+ * If RTC is in 12H mode, then bit BD70528_MASK_RTC_HOUR_PM
+ * is not BCD value but tells whether it is AM or PM
+ */
+ if (!(r->time.hour & BD70528_MASK_RTC_HOUR_24H)) {
+ t->tm_hour %= 12;
+ if (r->time.hour & BD70528_MASK_RTC_HOUR_PM)
+ t->tm_hour += 12;
+ }
+ t->tm_mday = bcd2bin(r->day & BD70528_MASK_RTC_DAY);
+ t->tm_mon = bcd2bin(r->month & BD70528_MASK_RTC_MONTH) - 1;
+ t->tm_year = 100 + bcd2bin(r->year & BD70528_MASK_RTC_YEAR);
+ t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK);
+}
+
+static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ struct bd70528_rtc_wake wake;
+ struct bd70528_rtc_alm alm;
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+
+ ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_WAKE_START,
+ &wake, sizeof(wake));
+ if (ret) {
+ dev_err(dev, "Failed to read wake regs\n");
+ return ret;
+ }
+
+ ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret) {
+ dev_err(dev, "Failed to read alarm regs\n");
+ return ret;
+ }
+
+ tm2rtc(&a->time, &alm.data);
+ tmday2rtc(&a->time, &wake.time);
+
+ if (a->enabled) {
+ alm.alm_mask &= ~BD70528_MASK_ALM_EN;
+ wake.ctrl |= BD70528_MASK_WAKE_EN;
+ } else {
+ alm.alm_mask |= BD70528_MASK_ALM_EN;
+ wake.ctrl &= ~BD70528_MASK_WAKE_EN;
+ }
+
+ ret = regmap_bulk_write(bd70528->regmap,
+ BD70528_REG_RTC_WAKE_START, &wake,
+ sizeof(wake));
+ if (ret) {
+ dev_err(dev, "Failed to set wake time\n");
+ return ret;
+ }
+ ret = regmap_bulk_write(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret)
+ dev_err(dev, "Failed to set alarm time\n");
+
+ return ret;
+}
+
+static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ struct bd70528_rtc_alm alm;
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+
+ ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret) {
+ dev_err(dev, "Failed to read alarm regs\n");
+ return ret;
+ }
+
+ rtc2tm(&alm.data, &a->time);
+ a->time.tm_mday = -1;
+ a->time.tm_mon = -1;
+ a->time.tm_year = -1;
+ a->enabled = !(alm.alm_mask & BD70528_MASK_ALM_EN);
+ a->pending = 0;
+
+ return 0;
+}
+
+static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
+{
+ int ret, tmpret, old_states;
+ struct bd70528_rtc_data rtc_data;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+
+ ret = bd70528_disable_rtc_based_timers(r, &old_states);
+ if (ret)
+ return ret;
+
+ tmpret = regmap_bulk_read(bd70528->regmap,
+ BD70528_REG_RTC_START, &rtc_data,
+ sizeof(rtc_data));
+ if (tmpret) {
+ dev_err(dev, "Failed to read RTC time registers\n");
+ goto renable_out;
+ }
+ tm2rtc(t, &rtc_data);
+
+ tmpret = regmap_bulk_write(bd70528->regmap,
+ BD70528_REG_RTC_START, &rtc_data,
+ sizeof(rtc_data));
+ if (tmpret) {
+ dev_err(dev, "Failed to set RTC time\n");
+ goto renable_out;
+ }
+
+renable_out:
+ ret = bd70528_re_enable_rtc_based_timers(r, old_states);
+ if (tmpret)
+ ret = tmpret;
+
+ return ret;
+}
+
+static int bd70528_set_time(struct device *dev, struct rtc_time *t)
+{
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+
+ bd70528_wdt_lock(r->mfd);
+ ret = bd70528_set_time_locked(dev, t);
+ bd70528_wdt_unlock(r->mfd);
+ return ret;
+}
+
+static int bd70528_get_time(struct device *dev, struct rtc_time *t)
+{
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+ struct bd70528_rtc_data rtc_data;
+ int ret;
+
+ /* read the RTC date and time registers all at once */
+ ret = regmap_bulk_read(bd70528->regmap,
+ BD70528_REG_RTC_START, &rtc_data,
+ sizeof(rtc_data));
+ if (ret) {
+ dev_err(dev, "Failed to read RTC time (err %d)\n", ret);
+ return ret;
+ }
+
+ rtc2tm(&rtc_data, t);
+
+ return 0;
+}
+
+static int bd70528_alm_enable(struct device *dev, unsigned int enabled)
+{
+ int ret;
+ unsigned int enableval = BD70528_MASK_ALM_EN;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+
+ if (enabled)
+ enableval = 0;
+
+ bd70528_wdt_lock(r->mfd);
+ ret = bd70528_set_wake(r->mfd, enabled, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to change wake state\n");
+ goto out_unlock;
+ }
+ ret = regmap_update_bits(r->mfd->regmap, BD70528_REG_RTC_ALM_MASK,
+ BD70528_MASK_ALM_EN, enableval);
+ if (ret)
+ dev_err(dev, "Failed to change alarm state\n");
+
+out_unlock:
+ bd70528_wdt_unlock(r->mfd);
+ return ret;
+}
+
+static const struct rtc_class_ops bd70528_rtc_ops = {
+ .read_time = bd70528_get_time,
+ .set_time = bd70528_set_time,
+ .read_alarm = bd70528_read_alarm,
+ .set_alarm = bd70528_set_alarm,
+ .alarm_irq_enable = bd70528_alm_enable,
+};
+
+static irqreturn_t alm_hndlr(int irq, void *data)
+{
+ struct rtc_device *rtc = data;
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF | RTC_PF);
+ return IRQ_HANDLED;
+}
+
+static int bd70528_probe(struct platform_device *pdev)
+{
+ struct bd70528_rtc *bd_rtc;
+ struct rohm_regmap_dev *mfd;
+ int ret;
+ struct rtc_device *rtc;
+ int irq;
+ unsigned int hr;
+
+ mfd = dev_get_drvdata(pdev->dev.parent);
+ if (!mfd) {
+ dev_err(&pdev->dev, "No MFD driver data\n");
+ return -EINVAL;
+ }
+ bd_rtc = devm_kzalloc(&pdev->dev, sizeof(*bd_rtc), GFP_KERNEL);
+ if (!bd_rtc)
+ return -ENOMEM;
+
+ bd_rtc->mfd = mfd;
+ bd_rtc->dev = &pdev->dev;
+
+ irq = platform_get_irq_byname(pdev, "bd70528-rtc-alm");
+ if (irq < 0)
+ return irq;
+
+ platform_set_drvdata(pdev, bd_rtc);
+
+ ret = regmap_read(mfd->regmap, BD70528_REG_RTC_HOUR, &hr);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to reag RTC clock\n");
+ return ret;
+ }
+
+ if (!(hr & BD70528_MASK_RTC_HOUR_24H)) {
+ struct rtc_time t;
+
+ ret = bd70528_get_time(&pdev->dev, &t);
+
+ if (!ret)
+ ret = bd70528_set_time(&pdev->dev, &t);
+
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Setting 24H clock for RTC failed\n");
+ return ret;
+ }
+ }
+
+ device_set_wakeup_capable(&pdev->dev, true);
+ device_wakeup_enable(&pdev->dev);
+
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "RTC device creation failed\n");
+ return PTR_ERR(rtc);
+ }
+
+ rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->range_max = RTC_TIMESTAMP_END_2099;
+ rtc->ops = &bd70528_rtc_ops;
+
+ /* Request alarm IRQ prior to registerig the RTC */
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr,
+ IRQF_ONESHOT, "bd70528-rtc", rtc);
+ if (ret)
+ return ret;
+
+ /*
+ * BD70528 irq controller is not touching the main mask register.
+ * So enable the RTC block interrupts at main level. We can just
+ * leave them enabled as irq-controller should disable irqs
+ * from sub-registers when IRQ is disabled or freed.
+ */
+ ret = regmap_update_bits(mfd->regmap,
+ BD70528_REG_INT_MAIN_MASK,
+ BD70528_INT_RTC_MASK, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable RTC interrupts\n");
+ return ret;
+ }
+
+ return rtc_register_device(rtc);
+}
+
+static struct platform_driver bd70528_rtc = {
+ .driver = {
+ .name = "bd70528-rtc"
+ },
+ .probe = bd70528_probe,
+};
+
+module_platform_driver(bd70528_rtc);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD70528 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c
index ef52741..4a63f0c 100644
--- a/drivers/rtc/rtc-bq32k.c
+++ b/drivers/rtc/rtc-bq32k.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for TI BQ32000 RTC.
*
* Copyright (C) 2009 Semihalf.
* Copyright (C) 2014 Pavel Machek <pavel@denx.de>
*
- * 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.
- *
* You can get hardware description at
* http://www.ti.com/lit/ds/symlink/bq32000.pdf
*/
diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c
index 113493b..472e756 100644
--- a/drivers/rtc/rtc-bq4802.c
+++ b/drivers/rtc/rtc-bq4802.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* rtc-bq4802.c: TI BQ4802 RTC driver.
*
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c
index f4010a7..3e9800f 100644
--- a/drivers/rtc/rtc-brcmstb-waketimer.c
+++ b/drivers/rtc/rtc-brcmstb-waketimer.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2014-2017 Broadcom
- *
- * 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.
- *
- * 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -132,7 +124,7 @@
wktmr_read(timer, &now);
- rtc_time_to_tm(now.sec, tm);
+ rtc_time64_to_tm(now.sec, tm);
return 0;
}
@@ -263,10 +255,8 @@
timer->rtc->range_max = U32_MAX;
ret = rtc_register_device(timer->rtc);
- if (ret) {
- dev_err(dev, "unable to register device\n");
+ if (ret)
goto err_notifier;
- }
dev_info(dev, "registered, with irq %d\n", timer->irq);
diff --git a/drivers/rtc/rtc-cadence.c b/drivers/rtc/rtc-cadence.c
new file mode 100644
index 0000000..592aae2
--- /dev/null
+++ b/drivers/rtc/rtc-cadence.c
@@ -0,0 +1,416 @@
+// 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))
+ return PTR_ERR(crtc->rtc_dev);
+
+ 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)
+ 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");
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index a5a19ff..0333037 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RTC class driver for "CMOS RTC": PCs, ACPI, etc
*
* Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c)
* Copyright (C) 2006 David Brownell (convert to new framework)
- *
- * 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.
*/
/*
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index fc5cf5c..4ac8508 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007-2009 ST-Ericsson AB
- * License terms: GNU General Public License (GPL) version 2
* Real Time Clock interface for ST-Ericsson AB COH 901 331 RTC.
* Author: Linus Walleij <linus.walleij@stericsson.com>
* Based on rtc-pl031.c by Deepak Saxena <dsaxena@plexity.net>
@@ -80,21 +80,22 @@
clk_enable(rtap->clk);
/* Check if the time is valid */
- if (readl(rtap->virtbase + COH901331_VALID)) {
- rtc_time_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm);
+ if (!readl(rtap->virtbase + COH901331_VALID)) {
clk_disable(rtap->clk);
- return 0;
+ return -EINVAL;
}
+
+ rtc_time64_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm);
clk_disable(rtap->clk);
- return -EINVAL;
+ return 0;
}
-static int coh901331_set_mmss(struct device *dev, unsigned long secs)
+static int coh901331_set_time(struct device *dev, struct rtc_time *tm)
{
struct coh901331_port *rtap = dev_get_drvdata(dev);
clk_enable(rtap->clk);
- writel(secs, rtap->virtbase + COH901331_SET_TIME);
+ writel(rtc_tm_to_time64(tm), rtap->virtbase + COH901331_SET_TIME);
clk_disable(rtap->clk);
return 0;
@@ -105,7 +106,7 @@
struct coh901331_port *rtap = dev_get_drvdata(dev);
clk_enable(rtap->clk);
- rtc_time_to_tm(readl(rtap->virtbase + COH901331_ALARM), &alarm->time);
+ rtc_time64_to_tm(readl(rtap->virtbase + COH901331_ALARM), &alarm->time);
alarm->pending = readl(rtap->virtbase + COH901331_IRQ_EVENT) & 1U;
alarm->enabled = readl(rtap->virtbase + COH901331_IRQ_MASK) & 1U;
clk_disable(rtap->clk);
@@ -116,9 +117,8 @@
static int coh901331_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct coh901331_port *rtap = dev_get_drvdata(dev);
- unsigned long time;
+ unsigned long time = rtc_tm_to_time64(&alarm->time);
- rtc_tm_to_time(&alarm->time, &time);
clk_enable(rtap->clk);
writel(time, rtap->virtbase + COH901331_ALARM);
writel(alarm->enabled, rtap->virtbase + COH901331_IRQ_MASK);
@@ -143,7 +143,7 @@
static const struct rtc_class_ops coh901331_ops = {
.read_time = coh901331_read_time,
- .set_mmss = coh901331_set_mmss,
+ .set_time = coh901331_set_time,
.read_alarm = coh901331_read_alarm,
.set_alarm = coh901331_set_alarm,
.alarm_irq_enable = coh901331_alarm_irq_enable,
@@ -188,6 +188,13 @@
return ret;
}
+ rtap->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtap->rtc))
+ return PTR_ERR(rtap->rtc);
+
+ rtap->rtc->ops = &coh901331_ops;
+ rtap->rtc->range_max = U32_MAX;
+
/* We enable/disable the clock only to assure it works */
ret = clk_prepare_enable(rtap->clk);
if (ret) {
@@ -197,12 +204,10 @@
clk_disable(rtap->clk);
platform_set_drvdata(pdev, rtap);
- rtap->rtc = devm_rtc_device_register(&pdev->dev, "coh901331",
- &coh901331_ops, THIS_MODULE);
- if (IS_ERR(rtap->rtc)) {
- ret = PTR_ERR(rtap->rtc);
+
+ ret = rtc_register_device(rtap->rtc);
+ if (ret)
goto out_no_rtc;
- }
return 0;
@@ -235,9 +240,13 @@
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 {
diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h
index ccc17a2..0abf989 100644
--- a/drivers/rtc/rtc-core.h
+++ b/drivers/rtc/rtc-core.h
@@ -40,23 +40,9 @@
#ifdef CONFIG_RTC_INTF_SYSFS
const struct attribute_group **rtc_get_dev_attribute_groups(void);
-int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp);
-int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps);
#else
static inline const struct attribute_group **rtc_get_dev_attribute_groups(void)
{
return NULL;
}
-
-static inline
-int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp)
-{
- return 0;
-}
-
-static inline
-int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps)
-{
- return 0;
-}
#endif
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
index 6b47717..6a3b70f 100644
--- a/drivers/rtc/rtc-cpcap.c
+++ b/drivers/rtc/rtc-cpcap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Motorola CPCAP PMIC RTC driver
*
@@ -12,15 +13,6 @@
* - remove custom "secure clock daemon" helpers
*
* Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
- *
- * 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.
- *
- * 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/kernel.h>
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c
index e544429..6909e01 100644
--- a/drivers/rtc/rtc-cros-ec.c
+++ b/drivers/rtc/rtc-cros-ec.c
@@ -6,8 +6,9 @@
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/slab.h>
@@ -298,7 +299,7 @@
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);
if (device_may_wakeup(dev))
- enable_irq_wake(cros_ec_rtc->cros_ec->irq);
+ return enable_irq_wake(cros_ec_rtc->cros_ec->irq);
return 0;
}
@@ -309,7 +310,7 @@
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);
if (device_may_wakeup(dev))
- disable_irq_wake(cros_ec_rtc->cros_ec->irq);
+ return disable_irq_wake(cros_ec_rtc->cros_ec->irq);
return 0;
}
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c
index 03044e1..204eb7c 100644
--- a/drivers/rtc/rtc-da9052.c
+++ b/drivers/rtc/rtc-da9052.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Real time clock driver for DA9052
*
* Copyright(c) 2012 Dialog Semiconductor Ltd.
*
* Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
- *
- * 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.
- *
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c
index e08cd81..844168f 100644
--- a/drivers/rtc/rtc-da9055.c
+++ b/drivers/rtc/rtc-da9055.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Real time clock driver for DA9055
*
* Copyright(c) 2012 Dialog Semiconductor Ltd.
*
* Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
- *
- * 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.
- *
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c
index b4e054c..15908d5 100644
--- a/drivers/rtc/rtc-da9063.c
+++ b/drivers/rtc/rtc-da9063.c
@@ -1,15 +1,7 @@
-/* rtc-da9063.c - Real time clock device driver for DA9063
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Real time clock device driver for DA9063
* Copyright (C) 2013-2015 Dialog Semiconductor Ltd.
- *
- * 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/delay.h>
@@ -247,8 +239,8 @@
da9063_data_to_tm(data, tm, rtc);
- rtc_tm_to_time(tm, &tm_secs);
- rtc_tm_to_time(&rtc->alarm_time, &al_secs);
+ tm_secs = rtc_tm_to_time64(tm);
+ al_secs = rtc_tm_to_time64(&rtc->alarm_time);
/* handle the rtc synchronisation delay */
if (rtc->rtc_sync == true && al_secs - tm_secs == 1)
@@ -472,14 +464,24 @@
platform_set_drvdata(pdev, rtc);
- rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, DA9063_DRVNAME_RTC,
- &da9063_rtc_ops, THIS_MODULE);
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc->rtc_dev))
return PTR_ERR(rtc->rtc_dev);
+ rtc->rtc_dev->ops = &da9063_rtc_ops;
+ rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_2063;
+
da9063_data_to_tm(data, &rtc->alarm_time, rtc);
rtc->rtc_sync = false;
+ /*
+ * TODO: some models have alarms on a minute boundary but still support
+ * real hardware interrupts. Add this once the core supports it.
+ */
+ if (config->rtc_data_start != RTC_SEC)
+ rtc->rtc_dev->uie_unsupported = 1;
+
irq_alarm = platform_get_irq_byname(pdev, "ALARM");
ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL,
da9063_alarm_event,
@@ -489,7 +491,7 @@
dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n",
irq_alarm, ret);
- return ret;
+ return rtc_register_device(rtc->rtc_dev);
}
static struct platform_driver da9063_rtc_driver = {
diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c
index caf3556..d8e0db2 100644
--- a/drivers/rtc/rtc-davinci.c
+++ b/drivers/rtc/rtc-davinci.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DaVinci Power Management and Real Time Clock Driver for TI platforms
*
* Copyright (C) 2009 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/init.h>
@@ -490,10 +477,8 @@
return -ENOMEM;
davinci_rtc->irq = platform_get_irq(pdev, 0);
- if (davinci_rtc->irq < 0) {
- dev_err(dev, "no RTC irq\n");
+ if (davinci_rtc->irq < 0)
return davinci_rtc->irq;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
davinci_rtc->base = devm_ioremap_resource(dev, res);
diff --git a/drivers/rtc/rtc-digicolor.c b/drivers/rtc/rtc-digicolor.c
index b253bf1..0aecc3f 100644
--- a/drivers/rtc/rtc-digicolor.c
+++ b/drivers/rtc/rtc-digicolor.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Real Time Clock driver for Conexant Digicolor
*
* Copyright (C) 2015 Paradox Innovation Ltd.
*
* Author: Baruch Siach <baruch@tkos.co.il>
- *
- * 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.
*/
#include <linux/io.h>
@@ -106,11 +102,11 @@
return 0;
}
-static int dc_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int dc_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct dc_rtc *rtc = dev_get_drvdata(dev);
- return dc_rtc_write(rtc, secs);
+ return dc_rtc_write(rtc, rtc_tm_to_time64(tm));
}
static int dc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
@@ -161,7 +157,7 @@
static const struct rtc_class_ops dc_rtc_ops = {
.read_time = dc_rtc_read_time,
- .set_mmss = dc_rtc_set_mmss,
+ .set_time = dc_rtc_set_time,
.read_alarm = dc_rtc_read_alarm,
.set_alarm = dc_rtc_set_alarm,
.alarm_irq_enable = dc_rtc_alarm_irq_enable,
@@ -192,6 +188,10 @@
if (IS_ERR(rtc->regs))
return PTR_ERR(rtc->regs);
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
@@ -200,12 +200,11 @@
return ret;
platform_set_drvdata(pdev, rtc);
- rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
- &dc_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev))
- return PTR_ERR(rtc->rtc_dev);
- return 0;
+ rtc->rtc_dev->ops = &dc_rtc_ops;
+ rtc->rtc_dev->range_max = U32_MAX;
+
+ return rtc_register_device(rtc->rtc_dev);
}
static const struct of_device_id dc_dt_ids[] = {
diff --git a/drivers/rtc/rtc-dm355evm.c b/drivers/rtc/rtc-dm355evm.c
index 97d8259..cd947a2 100644
--- a/drivers/rtc/rtc-dm355evm.c
+++ b/drivers/rtc/rtc-dm355evm.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* rtc-dm355evm.c - access battery-backed counter in MSP430 firmware
*
* Copyright (c) 2008 by David Brownell
- *
- * 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.
*/
#include <linux/kernel.h>
#include <linux/init.h>
@@ -78,7 +74,7 @@
dev_dbg(dev, "read timestamp %08x\n", time.value);
- rtc_time_to_tm(le32_to_cpu(time.value), tm);
+ rtc_time64_to_tm(le32_to_cpu(time.value), tm);
return 0;
}
@@ -88,7 +84,7 @@
unsigned long value;
int status;
- rtc_tm_to_time(tm, &value);
+ value = rtc_tm_to_time64(tm);
time.value = cpu_to_le32(value);
dev_dbg(dev, "write timestamp %08x\n", time.value);
@@ -127,16 +123,16 @@
{
struct rtc_device *rtc;
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &dm355evm_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
- PTR_ERR(rtc));
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc))
return PTR_ERR(rtc);
- }
+
platform_set_drvdata(pdev, rtc);
- return 0;
+ rtc->ops = &dm355evm_rtc_ops;
+ rtc->range_max = U32_MAX;
+
+ return rtc_register_device(rtc);
}
/*
diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c
index 50fabe1..b225bcf 100644
--- a/drivers/rtc/rtc-ds1216.c
+++ b/drivers/rtc/rtc-ds1216.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Dallas DS1216 RTC driver
*
diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c
index 0744916..a06508b 100644
--- a/drivers/rtc/rtc-ds1286.c
+++ b/drivers/rtc/rtc-ds1286.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DS1286 Real Time Clock interface for Linux
*
@@ -5,11 +6,6 @@
* Copyright (C) 2008 Thomas Bogendoerfer
*
* Based on code written by Paul Gortmaker.
- *
- * 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.
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index 2a88115..4faa24c 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Dallas DS1302 RTC Support
*
* Copyright (C) 2002 David McCullough
* Copyright (C) 2003 - 2007 Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License version 2. See the file "COPYING" in the main directory of
- * this archive for more details.
*/
#include <linux/bcd.h>
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index 2d502fc..4420fbf 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rtc-ds1305.c -- driver for DS1305 and DS1306 SPI RTC chips
*
* Copyright (C) 2008 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/kernel.h>
#include <linux/init.h>
@@ -694,19 +690,16 @@
/* register RTC ... from here on, ds1305->ctrl needs locking */
ds1305->rtc = devm_rtc_allocate_device(&spi->dev);
- if (IS_ERR(ds1305->rtc)) {
+ if (IS_ERR(ds1305->rtc))
return PTR_ERR(ds1305->rtc);
- }
ds1305->rtc->ops = &ds1305_ops;
ds1305_nvmem_cfg.priv = ds1305;
ds1305->rtc->nvram_old_abi = true;
status = rtc_register_device(ds1305->rtc);
- if (status) {
- dev_dbg(&spi->dev, "register rtc --> %d\n", status);
+ if (status)
return status;
- }
rtc_nvmem_register(ds1305->rtc, &ds1305_nvmem_cfg);
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 71396b6..1f7e8ae 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
*
@@ -5,10 +6,6 @@
* Copyright (C) 2006 David Brownell
* Copyright (C) 2009 Matthias Fuchs (rx8025 support)
* Copyright (C) 2012 Bertrand Achard (nvram access fixes)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/acpi.h>
@@ -114,6 +111,47 @@
# 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)
+# define M41TXX_BIT_CALIB_SIGN BIT(5)
+# define M41TXX_M_CALIBRATION GENMASK(4, 0)
+
+/* negative offset step is -2.034ppm */
+#define M41TXX_NEG_OFFSET_STEP_PPB 2034
+/* positive offset step is +4.068ppm */
+#define M41TXX_POS_OFFSET_STEP_PPB 4068
+/* Min and max values supported with 'offset' interface by M41TXX */
+#define M41TXX_MIN_OFFSET ((-31) * M41TXX_NEG_OFFSET_STEP_PPB)
+#define M41TXX_MAX_OFFSET ((31) * M41TXX_POS_OFFSET_STEP_PPB)
+
struct ds1307 {
enum ds_type type;
unsigned long flags;
@@ -144,17 +182,668 @@
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 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 const struct chip_desc chips[last_ds_type];
+
+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];
+
+ if (ds1307->type == rx_8130) {
+ unsigned int regflag;
+ ret = regmap_read(ds1307->regmap, RX8130_REG_FLAG, ®flag);
+ if (ret) {
+ dev_err(dev, "%s error %d\n", "read", ret);
+ return ret;
+ }
+
+ if (regflag & RX8130_REG_FLAG_VLF) {
+ dev_warn_once(dev, "oscillator failed, set time!\n");
+ return -EINVAL;
+ }
+ }
+
+ /* 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;
+ }
+
+ tmp = regs[DS1307_REG_SECS];
+ switch (ds1307->type) {
+ case ds_1307:
+ case m41t0:
+ case m41t00:
+ case m41t11:
+ if (tmp & DS1307_BIT_CH)
+ return -EINVAL;
+ break;
+ case ds_1308:
+ case ds_1338:
+ if (tmp & DS1307_BIT_CH)
+ return -EINVAL;
+
+ ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &tmp);
+ if (ret)
+ return ret;
+ if (tmp & DS1338_BIT_OSF)
+ return -EINVAL;
+ break;
+ case ds_1340:
+ if (tmp & DS1340_BIT_nEOSC)
+ return -EINVAL;
+
+ ret = regmap_read(ds1307->regmap, DS1340_REG_FLAG, &tmp);
+ if (ret)
+ return ret;
+ if (tmp & DS1340_BIT_OSF)
+ return -EINVAL;
+ break;
+ case mcp794xx:
+ if (!(tmp & MCP794XX_BIT_ST))
+ return -EINVAL;
+
+ break;
+ default:
+ break;
+ }
+
+ t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f);
+ t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f);
+ tmp = regs[DS1307_REG_HOUR] & 0x3f;
+ t->tm_hour = bcd2bin(tmp);
+ t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1;
+ t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f);
+ tmp = regs[DS1307_REG_MONTH] & 0x1f;
+ t->tm_mon = bcd2bin(tmp) - 1;
+ t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100;
+
+ if (regs[chip->century_reg] & chip->century_bit &&
+ IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY))
+ t->tm_year += 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ return 0;
+}
+
+static int ds1307_set_time(struct device *dev, struct rtc_time *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ const struct chip_desc *chip = &chips[ds1307->type];
+ int result;
+ int tmp;
+ u8 regs[7];
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ if (t->tm_year < 100)
+ return -EINVAL;
+
+#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
+ if (t->tm_year > (chip->century_bit ? 299 : 199))
+ return -EINVAL;
+#else
+ if (t->tm_year > 199)
+ return -EINVAL;
+#endif
+
+ regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
+ regs[DS1307_REG_MIN] = bin2bcd(t->tm_min);
+ regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
+ regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);
+ regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);
+ regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);
+
+ /* assume 20YY not 19YY */
+ tmp = t->tm_year - 100;
+ regs[DS1307_REG_YEAR] = bin2bcd(tmp);
+
+ if (chip->century_enable_bit)
+ regs[chip->century_reg] |= chip->century_enable_bit;
+ if (t->tm_year > 199 && chip->century_bit)
+ regs[chip->century_reg] |= chip->century_bit;
+
+ switch (ds1307->type) {
+ case ds_1308:
+ case ds_1338:
+ regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
+ DS1338_BIT_OSF, 0);
+ break;
+ case ds_1340:
+ regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
+ DS1340_BIT_OSF, 0);
+ break;
+ case mcp794xx:
+ /*
+ * these bits were cleared when preparing the date/time
+ * values and need to be set again before writing the
+ * regsfer out to the device.
+ */
+ regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST;
+ regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN;
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%s: %7ph\n", "write", regs);
+
+ result = regmap_bulk_write(ds1307->regmap, chip->offset, regs,
+ sizeof(regs));
+ if (result) {
+ 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;
+}
+
+static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ int ret;
+ u8 regs[9];
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ /* read all ALARM1, ALARM2, and status registers at once */
+ ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS,
+ regs, sizeof(regs));
+ if (ret) {
+ dev_err(dev, "%s error %d\n", "alarm read", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read",
+ ®s[0], ®s[4], ®s[7]);
+
+ /*
+ * report alarm time (ALARM1); assume 24 hour and day-of-month modes,
+ * and that all four fields are checked matches
+ */
+ t->time.tm_sec = bcd2bin(regs[0] & 0x7f);
+ t->time.tm_min = bcd2bin(regs[1] & 0x7f);
+ t->time.tm_hour = bcd2bin(regs[2] & 0x3f);
+ t->time.tm_mday = bcd2bin(regs[3] & 0x3f);
+
+ /* ... and status */
+ t->enabled = !!(regs[7] & DS1337_BIT_A1IE);
+ t->pending = !!(regs[8] & DS1337_BIT_A1I);
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, enabled=%d, pending=%d\n",
+ "alarm read", t->time.tm_sec, t->time.tm_min,
+ t->time.tm_hour, t->time.tm_mday,
+ t->enabled, t->pending);
+
+ return 0;
+}
+
+static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ unsigned char regs[9];
+ u8 control, status;
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, enabled=%d, pending=%d\n",
+ "alarm set", t->time.tm_sec, t->time.tm_min,
+ t->time.tm_hour, t->time.tm_mday,
+ t->enabled, t->pending);
+
+ /* read current status of both alarms and the chip */
+ ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs,
+ sizeof(regs));
+ if (ret) {
+ dev_err(dev, "%s error %d\n", "alarm write", ret);
+ return ret;
+ }
+ control = regs[7];
+ status = regs[8];
+
+ dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)",
+ ®s[0], ®s[4], control, status);
+
+ /* set ALARM1, using 24 hour and day-of-month modes */
+ regs[0] = bin2bcd(t->time.tm_sec);
+ regs[1] = bin2bcd(t->time.tm_min);
+ regs[2] = bin2bcd(t->time.tm_hour);
+ regs[3] = bin2bcd(t->time.tm_mday);
+
+ /* set ALARM2 to non-garbage */
+ regs[4] = 0;
+ regs[5] = 0;
+ regs[6] = 0;
+
+ /* disable alarms */
+ regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE);
+ regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);
+
+ ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs,
+ sizeof(regs));
+ if (ret) {
+ dev_err(dev, "can't set alarm time\n");
+ return ret;
+ }
+
+ /* optionally enable ALARM1 */
+ if (t->enabled) {
+ dev_dbg(dev, "alarm IRQ armed\n");
+ regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */
+ regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]);
+ }
+
+ return 0;
+}
+
+static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -ENOTTY;
+
+ return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
+ DS1337_BIT_A1IE,
+ enabled ? DS1337_BIT_A1IE : 0);
+}
+
+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 irqreturn_t rx8130_irq(int irq, void *dev_id)
+{
+ struct ds1307 *ds1307 = dev_id;
+ struct mutex *lock = &ds1307->rtc->ops_lock;
+ u8 ctl[3];
+ int ret;
+
+ mutex_lock(lock);
+
+ /* Read control registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
+ sizeof(ctl));
+ if (ret < 0)
+ goto out;
+ if (!(ctl[1] & RX8130_REG_FLAG_AF))
+ goto out;
+ ctl[1] &= ~RX8130_REG_FLAG_AF;
+ ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
+
+ ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
+ sizeof(ctl));
+ if (ret < 0)
+ goto out;
+
+ rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+
+out:
+ mutex_unlock(lock);
+
+ return IRQ_HANDLED;
+}
+
+static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ u8 ald[3], ctl[3];
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ /* Read alarm registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald,
+ sizeof(ald));
+ if (ret < 0)
+ return ret;
+
+ /* Read control registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
+ sizeof(ctl));
+ if (ret < 0)
+ return ret;
+
+ t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE);
+ t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF);
+
+ /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
+ t->time.tm_sec = -1;
+ t->time.tm_min = bcd2bin(ald[0] & 0x7f);
+ t->time.tm_hour = bcd2bin(ald[1] & 0x7f);
+ t->time.tm_wday = -1;
+ t->time.tm_mday = bcd2bin(ald[2] & 0x7f);
+ t->time.tm_mon = -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\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);
+
+ return 0;
+}
+
+static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ u8 ald[3], ctl[3];
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ 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 registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
+ sizeof(ctl));
+ if (ret < 0)
+ return ret;
+
+ 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,
+ sizeof(ctl));
+ if (ret < 0)
+ return ret;
+
+ /* Hardware alarm precision is 1 minute! */
+ ald[0] = bin2bcd(t->time.tm_min);
+ ald[1] = bin2bcd(t->time.tm_hour);
+ ald[2] = bin2bcd(t->time.tm_mday);
+
+ ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald,
+ sizeof(ald));
+ if (ret < 0)
+ return ret;
+
+ if (!t->enabled)
+ return 0;
+
+ ctl[2] |= RX8130_REG_CONTROL0_AIE;
+
+ return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]);
+}
+
+static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ int ret, reg;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®);
+ if (ret < 0)
+ return ret;
+
+ if (enabled)
+ reg |= RX8130_REG_CONTROL0_AIE;
+ else
+ reg &= ~RX8130_REG_CONTROL0_AIE;
+
+ 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, ®);
+ 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,
@@ -172,6 +861,16 @@
.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,
@@ -228,10 +927,17 @@
.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,
@@ -406,230 +1112,6 @@
/*----------------------------------------------------------------------*/
-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;
- }
-
- t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f);
- t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f);
- tmp = regs[DS1307_REG_HOUR] & 0x3f;
- t->tm_hour = bcd2bin(tmp);
- t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1;
- t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f);
- tmp = regs[DS1307_REG_MONTH] & 0x1f;
- t->tm_mon = bcd2bin(tmp) - 1;
- t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100;
-
- if (regs[chip->century_reg] & chip->century_bit &&
- IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY))
- t->tm_year += 100;
-
- dev_dbg(dev, "%s secs=%d, mins=%d, "
- "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
- "read", t->tm_sec, t->tm_min,
- t->tm_hour, t->tm_mday,
- t->tm_mon, t->tm_year, t->tm_wday);
-
- return 0;
-}
-
-static int ds1307_set_time(struct device *dev, struct rtc_time *t)
-{
- struct ds1307 *ds1307 = dev_get_drvdata(dev);
- const struct chip_desc *chip = &chips[ds1307->type];
- int result;
- int tmp;
- u8 regs[7];
-
- dev_dbg(dev, "%s secs=%d, mins=%d, "
- "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
- "write", t->tm_sec, t->tm_min,
- t->tm_hour, t->tm_mday,
- t->tm_mon, t->tm_year, t->tm_wday);
-
- if (t->tm_year < 100)
- return -EINVAL;
-
-#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
- if (t->tm_year > (chip->century_bit ? 299 : 199))
- return -EINVAL;
-#else
- if (t->tm_year > 199)
- return -EINVAL;
-#endif
-
- regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
- regs[DS1307_REG_MIN] = bin2bcd(t->tm_min);
- regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
- regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);
- regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);
- regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);
-
- /* assume 20YY not 19YY */
- tmp = t->tm_year - 100;
- regs[DS1307_REG_YEAR] = bin2bcd(tmp);
-
- if (chip->century_enable_bit)
- regs[chip->century_reg] |= chip->century_enable_bit;
- if (t->tm_year > 199 && chip->century_bit)
- regs[chip->century_reg] |= chip->century_bit;
-
- if (ds1307->type == mcp794xx) {
- /*
- * these bits were cleared when preparing the date/time
- * values and need to be set again before writing the
- * regsfer out to the device.
- */
- regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST;
- regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN;
- }
-
- dev_dbg(dev, "%s: %7ph\n", "write", regs);
-
- result = regmap_bulk_write(ds1307->regmap, chip->offset, regs,
- sizeof(regs));
- if (result) {
- dev_err(dev, "%s error %d\n", "write", result);
- return result;
- }
- return 0;
-}
-
-static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
-{
- struct ds1307 *ds1307 = dev_get_drvdata(dev);
- int ret;
- u8 regs[9];
-
- if (!test_bit(HAS_ALARM, &ds1307->flags))
- return -EINVAL;
-
- /* read all ALARM1, ALARM2, and status registers at once */
- ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS,
- regs, sizeof(regs));
- if (ret) {
- dev_err(dev, "%s error %d\n", "alarm read", ret);
- return ret;
- }
-
- dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read",
- ®s[0], ®s[4], ®s[7]);
-
- /*
- * report alarm time (ALARM1); assume 24 hour and day-of-month modes,
- * and that all four fields are checked matches
- */
- t->time.tm_sec = bcd2bin(regs[0] & 0x7f);
- t->time.tm_min = bcd2bin(regs[1] & 0x7f);
- t->time.tm_hour = bcd2bin(regs[2] & 0x3f);
- t->time.tm_mday = bcd2bin(regs[3] & 0x3f);
-
- /* ... and status */
- t->enabled = !!(regs[7] & DS1337_BIT_A1IE);
- t->pending = !!(regs[8] & DS1337_BIT_A1I);
-
- dev_dbg(dev, "%s secs=%d, mins=%d, "
- "hours=%d, mday=%d, enabled=%d, pending=%d\n",
- "alarm read", t->time.tm_sec, t->time.tm_min,
- t->time.tm_hour, t->time.tm_mday,
- t->enabled, t->pending);
-
- return 0;
-}
-
-static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
-{
- struct ds1307 *ds1307 = dev_get_drvdata(dev);
- unsigned char regs[9];
- u8 control, status;
- int ret;
-
- if (!test_bit(HAS_ALARM, &ds1307->flags))
- return -EINVAL;
-
- dev_dbg(dev, "%s secs=%d, mins=%d, "
- "hours=%d, mday=%d, enabled=%d, pending=%d\n",
- "alarm set", t->time.tm_sec, t->time.tm_min,
- t->time.tm_hour, t->time.tm_mday,
- t->enabled, t->pending);
-
- /* read current status of both alarms and the chip */
- ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs,
- sizeof(regs));
- if (ret) {
- dev_err(dev, "%s error %d\n", "alarm write", ret);
- return ret;
- }
- control = regs[7];
- status = regs[8];
-
- dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)",
- ®s[0], ®s[4], control, status);
-
- /* set ALARM1, using 24 hour and day-of-month modes */
- regs[0] = bin2bcd(t->time.tm_sec);
- regs[1] = bin2bcd(t->time.tm_min);
- regs[2] = bin2bcd(t->time.tm_hour);
- regs[3] = bin2bcd(t->time.tm_mday);
-
- /* set ALARM2 to non-garbage */
- regs[4] = 0;
- regs[5] = 0;
- regs[6] = 0;
-
- /* disable alarms */
- regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE);
- regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);
-
- ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs,
- sizeof(regs));
- if (ret) {
- dev_err(dev, "can't set alarm time\n");
- return ret;
- }
-
- /* optionally enable ALARM1 */
- if (t->enabled) {
- dev_dbg(dev, "alarm IRQ armed\n");
- regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */
- regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]);
- }
-
- return 0;
-}
-
-static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled)
-{
- struct ds1307 *ds1307 = dev_get_drvdata(dev);
-
- if (!test_bit(HAS_ALARM, &ds1307->flags))
- return -ENOTTY;
-
- return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
- DS1337_BIT_A1IE,
- enabled ? DS1337_BIT_A1IE : 0);
-}
-
static const struct rtc_class_ops ds13xx_rtc_ops = {
.read_time = ds1307_get_time,
.set_time = ds1307_set_time,
@@ -638,341 +1120,69 @@
.alarm_irq_enable = ds1307_alarm_irq_enable,
};
-/*----------------------------------------------------------------------*/
-
-/*
- * Alarm support for rx8130 devices.
- */
-
-#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)
-
-static irqreturn_t rx8130_irq(int irq, void *dev_id)
+static ssize_t frequency_test_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct ds1307 *ds1307 = dev_id;
- struct mutex *lock = &ds1307->rtc->ops_lock;
- u8 ctl[3];
+ struct ds1307 *ds1307 = dev_get_drvdata(dev->parent);
+ bool freq_test_en;
int ret;
- mutex_lock(lock);
+ ret = kstrtobool(buf, &freq_test_en);
+ if (ret) {
+ dev_err(dev, "Failed to store RTC Frequency Test attribute\n");
+ return ret;
+ }
- /* Read control registers. */
- ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
- sizeof(ctl));
- if (ret < 0)
- goto out;
- if (!(ctl[1] & RX8130_REG_FLAG_AF))
- goto out;
- ctl[1] &= ~RX8130_REG_FLAG_AF;
- ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
+ regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, M41TXX_BIT_FT,
+ freq_test_en ? M41TXX_BIT_FT : 0);
- ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
- sizeof(ctl));
- if (ret < 0)
- goto out;
-
- rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
-
-out:
- mutex_unlock(lock);
-
- return IRQ_HANDLED;
+ return count;
}
-static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+static ssize_t frequency_test_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct ds1307 *ds1307 = dev_get_drvdata(dev);
- u8 ald[3], ctl[3];
- int ret;
+ struct ds1307 *ds1307 = dev_get_drvdata(dev->parent);
+ unsigned int ctrl_reg;
- if (!test_bit(HAS_ALARM, &ds1307->flags))
- return -EINVAL;
+ regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg);
- /* Read alarm registers. */
- ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald,
- sizeof(ald));
- if (ret < 0)
- return ret;
+ return scnprintf(buf, PAGE_SIZE, (ctrl_reg & M41TXX_BIT_FT) ? "on\n" :
+ "off\n");
+}
- /* Read control registers. */
- ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
- sizeof(ctl));
- if (ret < 0)
- return ret;
+static DEVICE_ATTR_RW(frequency_test);
- t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE);
- t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF);
+static struct attribute *rtc_freq_test_attrs[] = {
+ &dev_attr_frequency_test.attr,
+ NULL,
+};
- /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
- t->time.tm_sec = -1;
- t->time.tm_min = bcd2bin(ald[0] & 0x7f);
- t->time.tm_hour = bcd2bin(ald[1] & 0x7f);
- t->time.tm_wday = -1;
- t->time.tm_mday = bcd2bin(ald[2] & 0x7f);
- t->time.tm_mon = -1;
- t->time.tm_year = -1;
- t->time.tm_yday = -1;
- t->time.tm_isdst = -1;
+static const struct attribute_group rtc_freq_test_attr_group = {
+ .attrs = rtc_freq_test_attrs,
+};
- dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d enabled=%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);
+static int ds1307_add_frequency_test(struct ds1307 *ds1307)
+{
+ int err;
+
+ switch (ds1307->type) {
+ case m41t0:
+ case m41t00:
+ case m41t11:
+ err = rtc_add_group(ds1307->rtc, &rtc_freq_test_attr_group);
+ if (err)
+ return err;
+ break;
+ default:
+ break;
+ }
return 0;
}
-static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
-{
- struct ds1307 *ds1307 = dev_get_drvdata(dev);
- u8 ald[3], ctl[3];
- int ret;
-
- if (!test_bit(HAS_ALARM, &ds1307->flags))
- return -EINVAL;
-
- 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 registers. */
- ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
- sizeof(ctl));
- if (ret < 0)
- return ret;
-
- 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,
- sizeof(ctl));
- if (ret < 0)
- return ret;
-
- /* Hardware alarm precision is 1 minute! */
- ald[0] = bin2bcd(t->time.tm_min);
- ald[1] = bin2bcd(t->time.tm_hour);
- ald[2] = bin2bcd(t->time.tm_mday);
-
- ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald,
- sizeof(ald));
- if (ret < 0)
- return ret;
-
- if (!t->enabled)
- return 0;
-
- ctl[2] |= RX8130_REG_CONTROL0_AIE;
-
- return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
- sizeof(ctl));
-}
-
-static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
-{
- struct ds1307 *ds1307 = dev_get_drvdata(dev);
- int ret, reg;
-
- if (!test_bit(HAS_ALARM, &ds1307->flags))
- return -EINVAL;
-
- ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®);
- if (ret < 0)
- return ret;
-
- if (enabled)
- reg |= RX8130_REG_CONTROL0_AIE;
- else
- reg &= ~RX8130_REG_CONTROL0_AIE;
-
- return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg);
-}
-
-/*----------------------------------------------------------------------*/
-
-/*
- * Alarm support for mcp794xx devices.
- */
-
-#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)
-{
- 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, ®);
- 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 ds1307_nvram_read(void *priv, unsigned int offset, void *val,
@@ -997,30 +1207,6 @@
/*----------------------------------------------------------------------*/
-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)
{
@@ -1568,7 +1754,6 @@
break;
}
-read_rtc:
/* read RTC registers */
err = regmap_bulk_read(ds1307->regmap, chip->offset, regs,
sizeof(regs));
@@ -1577,75 +1762,11 @@
goto exit;
}
- /*
- * minimal sanity checking; some chips (like DS1340) don't
- * specify the extra bits as must-be-zero, but there are
- * still a few values that are clearly out-of-range.
- */
- tmp = regs[DS1307_REG_SECS];
- switch (ds1307->type) {
- case ds_1307:
- case m41t0:
- case m41t00:
- case m41t11:
- /* clock halted? turn it on, so clock can tick. */
- if (tmp & DS1307_BIT_CH) {
- regmap_write(ds1307->regmap, DS1307_REG_SECS, 0);
- dev_warn(ds1307->dev, "SET TIME!\n");
- goto read_rtc;
- }
- break;
- case ds_1308:
- case ds_1338:
- /* clock halted? turn it on, so clock can tick. */
- if (tmp & DS1307_BIT_CH)
- regmap_write(ds1307->regmap, DS1307_REG_SECS, 0);
-
- /* oscillator fault? clear flag, and warn */
- if (regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) {
- regmap_write(ds1307->regmap, DS1307_REG_CONTROL,
- regs[DS1307_REG_CONTROL] &
- ~DS1338_BIT_OSF);
- dev_warn(ds1307->dev, "SET TIME!\n");
- goto read_rtc;
- }
- break;
- case ds_1340:
- /* clock halted? turn it on, so clock can tick. */
- if (tmp & DS1340_BIT_nEOSC)
- regmap_write(ds1307->regmap, DS1307_REG_SECS, 0);
-
- err = regmap_read(ds1307->regmap, DS1340_REG_FLAG, &tmp);
- if (err) {
- dev_dbg(ds1307->dev, "read error %d\n", err);
- goto exit;
- }
-
- /* oscillator fault? clear flag, and warn */
- if (tmp & DS1340_BIT_OSF) {
- regmap_write(ds1307->regmap, DS1340_REG_FLAG, 0);
- dev_warn(ds1307->dev, "SET TIME!\n");
- }
- break;
- case mcp794xx:
- /* make sure that the backup battery is enabled */
- if (!(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) {
- regmap_write(ds1307->regmap, DS1307_REG_WDAY,
- regs[DS1307_REG_WDAY] |
- MCP794XX_BIT_VBATEN);
- }
-
- /* clock halted? turn it on, so clock can tick. */
- if (!(tmp & MCP794XX_BIT_ST)) {
- regmap_write(ds1307->regmap, DS1307_REG_SECS,
- MCP794XX_BIT_ST);
- dev_warn(ds1307->dev, "SET TIME!\n");
- goto read_rtc;
- }
-
- break;
- default:
- break;
+ if (ds1307->type == mcp794xx &&
+ !(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) {
+ regmap_write(ds1307->regmap, DS1307_REG_WDAY,
+ regs[DS1307_REG_WDAY] |
+ MCP794XX_BIT_VBATEN);
}
tmp = regs[DS1307_REG_HOUR];
@@ -1710,6 +1831,10 @@
}
ds1307->rtc->ops = chip->rtc_ops ?: &ds13xx_rtc_ops;
+ err = ds1307_add_frequency_test(ds1307);
+ if (err)
+ return err;
+
err = rtc_register_device(ds1307->rtc);
if (err)
return err;
diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c
index 5208da4..fa6de31 100644
--- a/drivers/rtc/rtc-ds1343.c
+++ b/drivers/rtc/rtc-ds1343.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* rtc-ds1343.c
*
* Driver for Dallas Semiconductor DS1343 Low Current, SPI Compatible
@@ -5,11 +6,6 @@
*
* Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>
* Ankur Srivastava <sankurece@gmail.com> : DS1343 Nvram Support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/init.h>
diff --git a/drivers/rtc/rtc-ds1347.c b/drivers/rtc/rtc-ds1347.c
index 938512c..d392a7b 100644
--- a/drivers/rtc/rtc-ds1347.c
+++ b/drivers/rtc/rtc-ds1347.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* rtc-ds1347.c
*
* Driver for Dallas Semiconductor DS1347 Low Current, SPI Compatible
* Real Time Clock
*
* Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.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.
- *
*/
#include <linux/init.h>
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 38a2e9e..3674979 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -14,7 +14,7 @@
*/
/*
* It would be more efficient to use i2c msgs/i2c_transfer directly but, as
- * recommened in .../Documentation/i2c/writing-clients section
+ * recommended in .../Documentation/i2c/writing-clients.rst section
* "Sending and receiving", using SMBus level communication is preferred.
*/
@@ -467,7 +467,7 @@
*/
wdt_is_open = 1;
mutex_unlock(&ds1374->mutex);
- return nonseekable_open(inode, file);
+ return stream_open(inode, file);
}
return -ENODEV;
}
diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c
index 3b09540..66fc861 100644
--- a/drivers/rtc/rtc-ds1390.c
+++ b/drivers/rtc/rtc-ds1390.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rtc-ds1390.c -- driver for the Dallas/Maxim DS1390/93/94 SPI RTC
*
* Copyright (C) 2008 Mercury IMC Ltd
* Written by Mark Jackson <mpfj@mimc.co.uk>
*
- * 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.
- *
* NOTE: Currently this driver only supports the bare minimum for read
* and write the RTC. The extra features provided by the chip family
* (alarms, trickle charger, different control registers) are unavailable.
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index b8b6e51..b6a4775 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An rtc driver for the Dallas DS1511
*
* Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
* Copyright (C) 2007 Andrew Sharp <andy.sharp@lsi.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.
- *
* Real time clock driver for the Dallas 1511 chip, which also
* contains a watchdog timer. There is a tiny amount of code that
* platform code could use to mess with the watchdog device a little
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 34af7a8..219d6b5 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An rtc driver for the Dallas DS1553
*
* Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/bcd.h>
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index 9caaccc..9da84df 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* An rtc/i2c driver for the Dallas DS1672
* Copyright 2005-06 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/i2c.h>
@@ -21,17 +18,16 @@
#define DS1672_REG_CONTROL_EOSC 0x80
-static struct i2c_driver ds1672_driver;
-
/*
* In the routines that deal directly with the ds1672 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
- * Epoch is initialized as 2000. Time is set to UTC.
+ * Time is set to UTC.
*/
-static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int ds1672_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
unsigned long time;
- unsigned char addr = DS1672_REG_CNT_BASE;
+ unsigned char addr = DS1672_REG_CONTROL;
unsigned char buf[4];
struct i2c_msg msgs[] = {
@@ -43,11 +39,25 @@
{/* read date */
.addr = client->addr,
.flags = I2C_M_RD,
- .len = 4,
+ .len = 1,
.buf = buf
},
};
+ /* read control register */
+ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ dev_warn(&client->dev, "Unable to read the control register\n");
+ return -EIO;
+ }
+
+ if (buf[0] & DS1672_REG_CONTROL_EOSC) {
+ dev_warn(&client->dev, "Oscillator not enabled. Set time to enable.\n");
+ return -EINVAL;
+ }
+
+ addr = DS1672_REG_CNT_BASE;
+ msgs[1].len = 4;
+
/* read date registers */
if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
@@ -58,22 +68,22 @@
"%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);
+ rtc_time64_to_tm(time, tm);
- dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
- "mday=%d, mon=%d, year=%d, wday=%d\n",
- __func__, tm->tm_sec, tm->tm_min, tm->tm_hour,
- tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+ dev_dbg(&client->dev, "%s: tm is %ptR\n", __func__, tm);
return 0;
}
-static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs)
+static int ds1672_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
int xfer;
unsigned char buf[6];
+ unsigned long secs = rtc_tm_to_time64(tm);
buf[0] = DS1672_REG_CNT_BASE;
buf[1] = secs & 0x000000FF;
@@ -91,71 +101,15 @@
return 0;
}
-static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return ds1672_get_datetime(to_i2c_client(dev), tm);
-}
-
-static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs)
-{
- return ds1672_set_mmss(to_i2c_client(dev), secs);
-}
-
-static int ds1672_get_control(struct i2c_client *client, u8 *status)
-{
- unsigned char addr = DS1672_REG_CONTROL;
-
- struct i2c_msg msgs[] = {
- {/* setup read ptr */
- .addr = client->addr,
- .len = 1,
- .buf = &addr
- },
- {/* read control */
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = status
- },
- };
-
- /* read control register */
- if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
- dev_err(&client->dev, "%s: read error\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-/* following are the sysfs callback functions */
-static ssize_t show_control(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct i2c_client *client = to_i2c_client(dev);
- u8 control;
- int err;
-
- err = ds1672_get_control(client, &control);
- if (err)
- return err;
-
- return sprintf(buf, "%s\n", (control & DS1672_REG_CONTROL_EOSC)
- ? "disabled" : "enabled");
-}
-
-static DEVICE_ATTR(control, S_IRUGO, show_control, NULL);
-
static const struct rtc_class_ops ds1672_rtc_ops = {
- .read_time = ds1672_rtc_read_time,
- .set_mmss = ds1672_rtc_set_mmss,
+ .read_time = ds1672_read_time,
+ .set_time = ds1672_set_time,
};
static int ds1672_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err = 0;
- u8 control;
struct rtc_device *rtc;
dev_dbg(&client->dev, "%s\n", __func__);
@@ -163,29 +117,18 @@
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
- rtc = devm_rtc_device_register(&client->dev, ds1672_driver.driver.name,
- &ds1672_rtc_ops, THIS_MODULE);
-
+ rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
- i2c_set_clientdata(client, rtc);
+ rtc->ops = &ds1672_rtc_ops;
+ rtc->range_max = U32_MAX;
- /* read control register */
- err = ds1672_get_control(client, &control);
- if (err) {
- dev_warn(&client->dev, "Unable to read the control register\n");
- }
-
- if (control & DS1672_REG_CONTROL_EOSC)
- dev_warn(&client->dev, "Oscillator not enabled. "
- "Set time to enable.\n");
-
- /* Register sysfs hooks */
- err = device_create_file(&client->dev, &dev_attr_control);
+ err = rtc_register_device(rtc);
if (err)
- dev_err(&client->dev, "Unable to create sysfs entry: %s\n",
- dev_attr_control.attr.name);
+ return err;
+
+ i2c_set_clientdata(client, rtc);
return 0;
}
diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c
index 6f39f68..184e4a3 100644
--- a/drivers/rtc/rtc-ds1685.c
+++ b/drivers/rtc/rtc-ds1685.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An rtc driver for the Dallas/Maxim DS1685/DS1687 and related real-time
* chips.
@@ -10,10 +11,6 @@
* DS17x85/DS17x87 3V/5V Real-Time Clocks, 19-5222, Rev 4/10.
* DS1689/DS1693 3V/5V Serialized Real-Time Clocks, Rev 112105.
* Application Note 90, Using the Multiplex Bus RTC Extended Features.
- *
- * 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -192,42 +189,6 @@
}
/**
- * ds1685_rtc_begin_ctrl_access - prepare the rtc for ctrl access.
- * @rtc: pointer to the ds1685 rtc structure.
- * @flags: irq flags variable for spin_lock_irqsave.
- *
- * This takes several steps to prepare the rtc for access to read just the
- * control registers:
- * - Sets a spinlock on the rtc IRQ.
- * - Switches the rtc to bank 1. This allows access to the two extended
- * control registers.
- *
- * Only use this where you are certain another lock will not be held.
- */
-static inline void
-ds1685_rtc_begin_ctrl_access(struct ds1685_priv *rtc, unsigned long *flags)
-{
- spin_lock_irqsave(&rtc->lock, *flags);
- ds1685_rtc_switch_to_bank1(rtc);
-}
-
-/**
- * ds1685_rtc_end_ctrl_access - end ctrl access on the rtc.
- * @rtc: pointer to the ds1685 rtc structure.
- * @flags: irq flags variable for spin_unlock_irqrestore.
- *
- * This ends what was started by ds1685_rtc_begin_ctrl_access:
- * - Switches the rtc back to bank 0.
- * - Unsets the spinlock on the rtc IRQ.
- */
-static inline void
-ds1685_rtc_end_ctrl_access(struct ds1685_priv *rtc, unsigned long flags)
-{
- ds1685_rtc_switch_to_bank0(rtc);
- spin_unlock_irqrestore(&rtc->lock, flags);
-}
-
-/**
* ds1685_rtc_get_ssn - retrieve the silicon serial number.
* @rtc: pointer to the ds1685 rtc structure.
* @ssn: u8 array to hold the bits of the silicon serial number.
@@ -546,10 +507,6 @@
ds1685_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct ds1685_priv *rtc = dev_get_drvdata(dev);
- unsigned long flags = 0;
-
- /* Enable/disable the Alarm IRQ-Enable flag. */
- spin_lock_irqsave(&rtc->lock, flags);
/* Flip the requisite interrupt-enable bit. */
if (enabled)
@@ -561,7 +518,6 @@
/* Read Control C to clear all the flag bits. */
rtc->read(rtc, RTC_CTRL_C);
- spin_unlock_irqrestore(&rtc->lock, flags);
return 0;
}
@@ -569,98 +525,18 @@
/* ----------------------------------------------------------------------- */
-/* IRQ handler & workqueue. */
+/* IRQ handler */
/**
- * ds1685_rtc_irq_handler - IRQ handler.
- * @irq: IRQ number.
- * @dev_id: platform device pointer.
- */
-static irqreturn_t
-ds1685_rtc_irq_handler(int irq, void *dev_id)
-{
- struct platform_device *pdev = dev_id;
- struct ds1685_priv *rtc = platform_get_drvdata(pdev);
- u8 ctrlb, ctrlc;
- unsigned long events = 0;
- u8 num_irqs = 0;
-
- /* Abort early if the device isn't ready yet (i.e., DEBUG_SHIRQ). */
- if (unlikely(!rtc))
- return IRQ_HANDLED;
-
- /* Ctrlb holds the interrupt-enable bits and ctrlc the flag bits. */
- spin_lock(&rtc->lock);
- ctrlb = rtc->read(rtc, RTC_CTRL_B);
- ctrlc = rtc->read(rtc, RTC_CTRL_C);
-
- /* Is the IRQF bit set? */
- if (likely(ctrlc & RTC_CTRL_C_IRQF)) {
- /*
- * We need to determine if it was one of the standard
- * events: PF, AF, or UF. If so, we handle them and
- * update the RTC core.
- */
- if (likely(ctrlc & RTC_CTRL_B_PAU_MASK)) {
- events = RTC_IRQF;
-
- /* Check for a periodic interrupt. */
- if ((ctrlb & RTC_CTRL_B_PIE) &&
- (ctrlc & RTC_CTRL_C_PF)) {
- events |= RTC_PF;
- num_irqs++;
- }
-
- /* Check for an alarm interrupt. */
- if ((ctrlb & RTC_CTRL_B_AIE) &&
- (ctrlc & RTC_CTRL_C_AF)) {
- events |= RTC_AF;
- num_irqs++;
- }
-
- /* Check for an update interrupt. */
- if ((ctrlb & RTC_CTRL_B_UIE) &&
- (ctrlc & RTC_CTRL_C_UF)) {
- events |= RTC_UF;
- num_irqs++;
- }
-
- rtc_update_irq(rtc->dev, num_irqs, events);
- } else {
- /*
- * One of the "extended" interrupts was received that
- * is not recognized by the RTC core. These need to
- * be handled in task context as they can call other
- * functions and the time spent in irq context needs
- * to be minimized. Schedule them into a workqueue
- * and inform the RTC core that the IRQs were handled.
- */
- spin_unlock(&rtc->lock);
- schedule_work(&rtc->work);
- rtc_update_irq(rtc->dev, 0, 0);
- return IRQ_HANDLED;
- }
- }
- spin_unlock(&rtc->lock);
-
- return events ? IRQ_HANDLED : IRQ_NONE;
-}
-
-/**
- * ds1685_rtc_work_queue - work queue handler.
- * @work: work_struct containing data to work on in task context.
+ * ds1685_rtc_extended_irq - take care of extended interrupts
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @pdev: platform device pointer.
*/
static void
-ds1685_rtc_work_queue(struct work_struct *work)
+ds1685_rtc_extended_irq(struct ds1685_priv *rtc, struct platform_device *pdev)
{
- struct ds1685_priv *rtc = container_of(work,
- struct ds1685_priv, work);
- struct platform_device *pdev = to_platform_device(&rtc->dev->dev);
- struct mutex *rtc_mutex = &rtc->dev->ops_lock;
u8 ctrl4a, ctrl4b;
- mutex_lock(rtc_mutex);
-
ds1685_rtc_switch_to_bank1(rtc);
ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A);
ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B);
@@ -739,8 +615,76 @@
"RAM-Clear IRQ just occurred!\n");
}
ds1685_rtc_switch_to_bank0(rtc);
+}
+/**
+ * ds1685_rtc_irq_handler - IRQ handler.
+ * @irq: IRQ number.
+ * @dev_id: platform device pointer.
+ */
+static irqreturn_t
+ds1685_rtc_irq_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ struct mutex *rtc_mutex;
+ u8 ctrlb, ctrlc;
+ unsigned long events = 0;
+ u8 num_irqs = 0;
+
+ /* Abort early if the device isn't ready yet (i.e., DEBUG_SHIRQ). */
+ if (unlikely(!rtc))
+ return IRQ_HANDLED;
+
+ rtc_mutex = &rtc->dev->ops_lock;
+ mutex_lock(rtc_mutex);
+
+ /* Ctrlb holds the interrupt-enable bits and ctrlc the flag bits. */
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ ctrlc = rtc->read(rtc, RTC_CTRL_C);
+
+ /* Is the IRQF bit set? */
+ if (likely(ctrlc & RTC_CTRL_C_IRQF)) {
+ /*
+ * We need to determine if it was one of the standard
+ * events: PF, AF, or UF. If so, we handle them and
+ * update the RTC core.
+ */
+ if (likely(ctrlc & RTC_CTRL_B_PAU_MASK)) {
+ events = RTC_IRQF;
+
+ /* Check for a periodic interrupt. */
+ if ((ctrlb & RTC_CTRL_B_PIE) &&
+ (ctrlc & RTC_CTRL_C_PF)) {
+ events |= RTC_PF;
+ num_irqs++;
+ }
+
+ /* Check for an alarm interrupt. */
+ if ((ctrlb & RTC_CTRL_B_AIE) &&
+ (ctrlc & RTC_CTRL_C_AF)) {
+ events |= RTC_AF;
+ num_irqs++;
+ }
+
+ /* Check for an update interrupt. */
+ if ((ctrlb & RTC_CTRL_B_UIE) &&
+ (ctrlc & RTC_CTRL_C_UF)) {
+ events |= RTC_UF;
+ num_irqs++;
+ }
+ } else {
+ /*
+ * One of the "extended" interrupts was received that
+ * is not recognized by the RTC core.
+ */
+ ds1685_rtc_extended_irq(rtc, pdev);
+ }
+ }
+ rtc_update_irq(rtc->dev, num_irqs, events);
mutex_unlock(rtc_mutex);
+
+ return events ? IRQ_HANDLED : IRQ_NONE;
}
/* ----------------------------------------------------------------------- */
@@ -770,33 +714,6 @@
"512Hz", "256Hz", "128Hz", "64Hz", "32Hz", "16Hz", "8Hz", "4Hz", "2Hz"
};
-#ifdef CONFIG_RTC_DS1685_PROC_REGS
-/**
- * ds1685_rtc_print_regs - helper function to print register values.
- * @hex: hex byte to convert into binary bits.
- * @dest: destination char array.
- *
- * This is basically a hex->binary function, just with extra spacing between
- * the digits. It only works on 1-byte values (8 bits).
- */
-static char*
-ds1685_rtc_print_regs(u8 hex, char *dest)
-{
- u32 i, j;
- char *tmp = dest;
-
- for (i = 0; i < NUM_BITS; i++) {
- *tmp++ = ((hex & 0x80) != 0 ? '1' : '0');
- for (j = 0; j < NUM_SPACES; j++)
- *tmp++ = ' ';
- hex <<= 1;
- }
- *tmp++ = '\0';
-
- return dest;
-}
-#endif
-
/**
* ds1685_rtc_proc - procfs access function.
* @dev: pointer to device structure.
@@ -805,13 +722,9 @@
static int
ds1685_rtc_proc(struct device *dev, struct seq_file *seq)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ struct ds1685_priv *rtc = dev_get_drvdata(dev);
u8 ctrla, ctrlb, ctrlc, ctrld, ctrl4a, ctrl4b, ssn[8];
char *model;
-#ifdef CONFIG_RTC_DS1685_PROC_REGS
- char bits[NUM_REGS][(NUM_BITS * NUM_SPACES) + NUM_BITS + 1];
-#endif
/* Read all the relevant data from the control registers. */
ds1685_rtc_switch_to_bank1(rtc);
@@ -859,28 +772,7 @@
"Periodic IRQ\t: %s\n"
"Periodic Rate\t: %s\n"
"SQW Freq\t: %s\n"
-#ifdef CONFIG_RTC_DS1685_PROC_REGS
- "Serial #\t: %8phC\n"
- "Register Status\t:\n"
- " Ctrl A\t: UIP DV2 DV1 DV0 RS3 RS2 RS1 RS0\n"
- "\t\t: %s\n"
- " Ctrl B\t: SET PIE AIE UIE SQWE DM 2412 DSE\n"
- "\t\t: %s\n"
- " Ctrl C\t: IRQF PF AF UF --- --- --- ---\n"
- "\t\t: %s\n"
- " Ctrl D\t: VRT --- --- --- --- --- --- ---\n"
- "\t\t: %s\n"
-#if !defined(CONFIG_RTC_DRV_DS1685) && !defined(CONFIG_RTC_DRV_DS1689)
- " Ctrl 4A\t: VRT2 INCR BME --- PAB RF WF KF\n"
-#else
- " Ctrl 4A\t: VRT2 INCR --- --- PAB RF WF KF\n"
-#endif
- "\t\t: %s\n"
- " Ctrl 4B\t: ABE E32k CS RCE PRS RIE WIE KSE\n"
- "\t\t: %s\n",
-#else
"Serial #\t: %8phC\n",
-#endif
model,
((ctrla & RTC_CTRL_A_DV1) ? "enabled" : "disabled"),
((ctrlb & RTC_CTRL_B_2412) ? "24-hour" : "12-hour"),
@@ -894,17 +786,7 @@
ds1685_rtc_pirq_rate[(ctrla & RTC_CTRL_A_RS_MASK)] : "none"),
(!((ctrl4b & RTC_CTRL_4B_E32K)) ?
ds1685_rtc_sqw_freq[(ctrla & RTC_CTRL_A_RS_MASK)] : "32768Hz"),
-#ifdef CONFIG_RTC_DS1685_PROC_REGS
- ssn,
- ds1685_rtc_print_regs(ctrla, bits[0]),
- ds1685_rtc_print_regs(ctrlb, bits[1]),
- ds1685_rtc_print_regs(ctrlc, bits[2]),
- ds1685_rtc_print_regs(ctrld, bits[3]),
- ds1685_rtc_print_regs(ctrl4a, bits[4]),
- ds1685_rtc_print_regs(ctrl4b, bits[5]));
-#else
ssn);
-#endif
return 0;
}
#else
@@ -927,32 +809,19 @@
};
/* ----------------------------------------------------------------------- */
-
-/* ----------------------------------------------------------------------- */
-/* SysFS interface */
-
-#ifdef CONFIG_SYSFS
-/**
- * ds1685_rtc_sysfs_nvram_read - reads rtc nvram via sysfs.
- * @file: pointer to file structure.
- * @kobj: pointer to kobject structure.
- * @bin_attr: pointer to bin_attribute structure.
- * @buf: pointer to char array to hold the output.
- * @pos: current file position pointer.
- * @size: size of the data to read.
- */
-static ssize_t
-ds1685_rtc_sysfs_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t pos, size_t size)
+static int ds1685_nvram_read(void *priv, unsigned int pos, void *val,
+ size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
- struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ struct ds1685_priv *rtc = priv;
+ struct mutex *rtc_mutex = &rtc->dev->ops_lock;
ssize_t count;
- unsigned long flags = 0;
+ u8 *buf = val;
+ int err;
- spin_lock_irqsave(&rtc->lock, flags);
+ err = mutex_lock_interruptible(rtc_mutex);
+ if (err)
+ return err;
+
ds1685_rtc_switch_to_bank0(rtc);
/* Read NVRAM in time and bank0 registers. */
@@ -1002,37 +871,24 @@
ds1685_rtc_switch_to_bank0(rtc);
}
#endif /* !CONFIG_RTC_DRV_DS1689 */
- spin_unlock_irqrestore(&rtc->lock, flags);
+ mutex_unlock(rtc_mutex);
- /*
- * XXX: Bug? this appears to cause the function to get executed
- * several times in succession. But it's the only way to actually get
- * data written out to a file.
- */
- return count;
+ return 0;
}
-/**
- * ds1685_rtc_sysfs_nvram_write - writes rtc nvram via sysfs.
- * @file: pointer to file structure.
- * @kobj: pointer to kobject structure.
- * @bin_attr: pointer to bin_attribute structure.
- * @buf: pointer to char array to hold the input.
- * @pos: current file position pointer.
- * @size: size of the data to write.
- */
-static ssize_t
-ds1685_rtc_sysfs_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t pos, size_t size)
+static int ds1685_nvram_write(void *priv, unsigned int pos, void *val,
+ size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
- struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ struct ds1685_priv *rtc = priv;
+ struct mutex *rtc_mutex = &rtc->dev->ops_lock;
ssize_t count;
- unsigned long flags = 0;
+ u8 *buf = val;
+ int err;
- spin_lock_irqsave(&rtc->lock, flags);
+ err = mutex_lock_interruptible(rtc_mutex);
+ if (err)
+ return err;
+
ds1685_rtc_switch_to_bank0(rtc);
/* Write NVRAM in time and bank0 registers. */
@@ -1082,28 +938,13 @@
ds1685_rtc_switch_to_bank0(rtc);
}
#endif /* !CONFIG_RTC_DRV_DS1689 */
- spin_unlock_irqrestore(&rtc->lock, flags);
+ mutex_unlock(rtc_mutex);
- return count;
+ return 0;
}
-/**
- * struct ds1685_rtc_sysfs_nvram_attr - sysfs attributes for rtc nvram.
- * @attr: nvram attributes.
- * @read: nvram read function.
- * @write: nvram write function.
- * @size: nvram total size (bank0 + extended).
- */
-static struct bin_attribute
-ds1685_rtc_sysfs_nvram_attr = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
- .read = ds1685_rtc_sysfs_nvram_read,
- .write = ds1685_rtc_sysfs_nvram_write,
- .size = NVRAM_TOTAL_SZ
-};
+/* ----------------------------------------------------------------------- */
+/* SysFS interface */
/**
* ds1685_rtc_sysfs_battery_show - sysfs file for main battery status.
@@ -1115,7 +956,7 @@
ds1685_rtc_sysfs_battery_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ struct ds1685_priv *rtc = dev_get_drvdata(dev->parent);
u8 ctrld;
ctrld = rtc->read(rtc, RTC_CTRL_D);
@@ -1135,7 +976,7 @@
ds1685_rtc_sysfs_auxbatt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ struct ds1685_priv *rtc = dev_get_drvdata(dev->parent);
u8 ctrl4a;
ds1685_rtc_switch_to_bank1(rtc);
@@ -1157,7 +998,7 @@
ds1685_rtc_sysfs_serial_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ struct ds1685_priv *rtc = dev_get_drvdata(dev->parent);
u8 ssn[8];
ds1685_rtc_switch_to_bank1(rtc);
@@ -1188,43 +1029,6 @@
.attrs = ds1685_rtc_sysfs_misc_attrs,
};
-/**
- * ds1685_rtc_sysfs_register - register sysfs files.
- * @dev: pointer to device structure.
- */
-static int
-ds1685_rtc_sysfs_register(struct device *dev)
-{
- int ret = 0;
-
- sysfs_bin_attr_init(&ds1685_rtc_sysfs_nvram_attr);
- ret = sysfs_create_bin_file(&dev->kobj, &ds1685_rtc_sysfs_nvram_attr);
- if (ret)
- return ret;
-
- ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_misc_grp);
- if (ret)
- return ret;
-
- return 0;
-}
-
-/**
- * ds1685_rtc_sysfs_unregister - unregister sysfs files.
- * @dev: pointer to device structure.
- */
-static int
-ds1685_rtc_sysfs_unregister(struct device *dev)
-{
- sysfs_remove_bin_file(&dev->kobj, &ds1685_rtc_sysfs_nvram_attr);
- sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_misc_grp);
-
- return 0;
-}
-#endif /* CONFIG_SYSFS */
-
-
-
/* ----------------------------------------------------------------------- */
/* Driver Probe/Removal */
@@ -1242,6 +1046,12 @@
u8 ctrla, ctrlb, hours;
unsigned char am_pm;
int ret = 0;
+ struct nvmem_config nvmem_cfg = {
+ .name = "ds1685_nvram",
+ .size = NVRAM_TOTAL_SZ,
+ .reg_read = ds1685_nvram_read,
+ .reg_write = ds1685_nvram_write,
+ };
/* Get the platform data. */
pdata = (struct ds1685_rtc_platform_data *) pdev->dev.platform_data;
@@ -1319,9 +1129,7 @@
if (pdata->plat_post_ram_clear)
rtc->post_ram_clear = pdata->plat_post_ram_clear;
- /* Init the spinlock, workqueue, & set the driver data. */
- spin_lock_init(&rtc->lock);
- INIT_WORK(&rtc->work, ds1685_rtc_work_queue);
+ /* set the driver data. */
platform_set_drvdata(pdev, rtc);
/* Turn the oscillator on if is not already on (DV1 = 1). */
@@ -1477,33 +1285,38 @@
*/
if (!pdata->no_irq) {
ret = platform_get_irq(pdev, 0);
- if (ret > 0) {
- rtc->irq_num = ret;
-
- /* Request an IRQ. */
- ret = devm_request_irq(&pdev->dev, rtc->irq_num,
- ds1685_rtc_irq_handler,
- IRQF_SHARED, pdev->name, pdev);
-
- /* Check to see if something came back. */
- if (unlikely(ret)) {
- dev_warn(&pdev->dev,
- "RTC interrupt not available\n");
- rtc->irq_num = 0;
- }
- } else
+ if (ret <= 0)
return ret;
+
+ rtc->irq_num = ret;
+
+ /* Request an IRQ. */
+ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_num,
+ NULL, ds1685_rtc_irq_handler,
+ IRQF_SHARED | IRQF_ONESHOT,
+ pdev->name, pdev);
+
+ /* Check to see if something came back. */
+ if (unlikely(ret)) {
+ dev_warn(&pdev->dev,
+ "RTC interrupt not available\n");
+ rtc->irq_num = 0;
+ }
}
rtc->no_irq = pdata->no_irq;
/* Setup complete. */
ds1685_rtc_switch_to_bank0(rtc);
-#ifdef CONFIG_SYSFS
- ret = ds1685_rtc_sysfs_register(&pdev->dev);
+ ret = rtc_add_group(rtc_dev, &ds1685_rtc_sysfs_misc_grp);
if (ret)
return ret;
-#endif
+
+ rtc_dev->nvram_old_abi = true;
+ nvmem_cfg.priv = rtc;
+ ret = rtc_nvmem_register(rtc_dev, &nvmem_cfg);
+ if (ret)
+ return ret;
return rtc_register_device(rtc_dev);
}
@@ -1517,10 +1330,6 @@
{
struct ds1685_priv *rtc = platform_get_drvdata(pdev);
-#ifdef CONFIG_SYSFS
- ds1685_rtc_sysfs_unregister(&pdev->dev);
-#endif
-
/* Read Ctrl B and clear PIE/AIE/UIE. */
rtc->write(rtc, RTC_CTRL_B,
(rtc->read(rtc, RTC_CTRL_B) &
@@ -1539,8 +1348,6 @@
(rtc->read(rtc, RTC_EXT_CTRL_4A) &
~(RTC_CTRL_4A_RWK_MASK)));
- cancel_work_sync(&rtc->work);
-
return 0;
}
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 5a4c2c5..2b949f0 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An rtc driver for the Dallas DS1742
*
* Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
*
- * 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.
- *
* Copyright (C) 2006 Torsten Ertbjerg Rasmussen <tr@newtec.dk>
* - nvram size determined from resource
* - this ds1742 driver now supports ds1743.
diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c
index b886b6a..9df0c44 100644
--- a/drivers/rtc/rtc-ds2404.c
+++ b/drivers/rtc/rtc-ds2404.c
@@ -1,11 +1,5 @@
-/*
- * Copyright (C) 2012 Sven Schnelle <svens@stackframe.org>
- *
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2012 Sven Schnelle <svens@stackframe.org>
#include <linux/platform_device.h>
#include <linux/module.h>
@@ -29,14 +23,6 @@
#define DS2404_COPY_SCRATCHPAD_CMD 0x55
#define DS2404_READ_MEMORY_CMD 0xf0
-struct ds2404;
-
-struct ds2404_chip_ops {
- int (*map_io)(struct ds2404 *chip, struct platform_device *pdev,
- struct ds2404_platform_data *pdata);
- void (*unmap_io)(struct ds2404 *chip);
-};
-
#define DS2404_RST 0
#define DS2404_CLK 1
#define DS2404_DQ 2
@@ -48,7 +34,6 @@
struct ds2404 {
struct ds2404_gpio *gpio;
- const struct ds2404_chip_ops *ops;
struct rtc_device *rtc;
};
@@ -87,7 +72,7 @@
return err;
}
-static void ds2404_gpio_unmap(struct ds2404 *chip)
+static void ds2404_gpio_unmap(void *data)
{
int i;
@@ -95,11 +80,6 @@
gpio_free(ds2404_gpio[i].gpio);
}
-static const struct ds2404_chip_ops ds2404_gpio_ops = {
- .map_io = ds2404_gpio_map,
- .unmap_io = ds2404_gpio_unmap,
-};
-
static void ds2404_reset(struct device *dev)
{
gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 0);
@@ -202,24 +182,25 @@
static int ds2404_read_time(struct device *dev, struct rtc_time *dt)
{
unsigned long time = 0;
+ __le32 hw_time = 0;
- ds2404_read_memory(dev, 0x203, 4, (u8 *)&time);
- time = le32_to_cpu(time);
+ ds2404_read_memory(dev, 0x203, 4, (u8 *)&hw_time);
+ time = le32_to_cpu(hw_time);
- rtc_time_to_tm(time, dt);
+ rtc_time64_to_tm(time, dt);
return 0;
}
-static int ds2404_set_mmss(struct device *dev, unsigned long secs)
+static int ds2404_set_time(struct device *dev, struct rtc_time *dt)
{
- u32 time = cpu_to_le32(secs);
+ u32 time = cpu_to_le32(rtc_tm_to_time64(dt));
ds2404_write_memory(dev, 0x203, 4, (u8 *)&time);
return 0;
}
static const struct rtc_class_ops ds2404_rtc_ops = {
.read_time = ds2404_read_time,
- .set_mmss = ds2404_set_mmss,
+ .set_time = ds2404_set_time,
};
static int rtc_probe(struct platform_device *pdev)
@@ -232,11 +213,17 @@
if (!chip)
return -ENOMEM;
- chip->ops = &ds2404_gpio_ops;
+ chip->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(chip->rtc))
+ return PTR_ERR(chip->rtc);
- retval = chip->ops->map_io(chip, pdev, pdata);
+ retval = ds2404_gpio_map(chip, pdev, pdata);
if (retval)
- goto err_chip;
+ return retval;
+
+ retval = devm_add_action_or_reset(&pdev->dev, ds2404_gpio_unmap, chip);
+ if (retval)
+ return retval;
dev_info(&pdev->dev, "using GPIOs RST:%d, CLK:%d, DQ:%d\n",
chip->gpio[DS2404_RST].gpio, chip->gpio[DS2404_CLK].gpio,
@@ -244,34 +231,19 @@
platform_set_drvdata(pdev, chip);
- chip->rtc = devm_rtc_device_register(&pdev->dev, "ds2404",
- &ds2404_rtc_ops, THIS_MODULE);
- if (IS_ERR(chip->rtc)) {
- retval = PTR_ERR(chip->rtc);
- goto err_io;
- }
+ chip->rtc->ops = &ds2404_rtc_ops;
+ chip->rtc->range_max = U32_MAX;
+
+ retval = rtc_register_device(chip->rtc);
+ if (retval)
+ return retval;
ds2404_enable_osc(&pdev->dev);
return 0;
-
-err_io:
- chip->ops->unmap_io(chip);
-err_chip:
- return retval;
-}
-
-static int rtc_remove(struct platform_device *dev)
-{
- struct ds2404 *chip = platform_get_drvdata(dev);
-
- chip->ops->unmap_io(chip);
-
- return 0;
}
static struct platform_driver rtc_device_driver = {
.probe = rtc_probe,
- .remove = rtc_remove,
.driver = {
.name = "ds2404",
},
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
index 7184e51..69c37ab 100644
--- a/drivers/rtc/rtc-ds3232.c
+++ b/drivers/rtc/rtc-ds3232.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RTC client/driver for the Maxim/Dallas DS3232/DS3234 Real-Time Clock
*
* Copyright (C) 2009-2011 Freescale Semiconductor.
* Author: Jack Lan <jack.lan@freescale.com>
* Copyright (C) 2008 MIMOMax Wireless Ltd.
- *
- * 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -48,6 +44,10 @@
# define DS3232_REG_SR_A1F 0x01
#define DS3232_REG_TEMPERATURE 0x11
+#define DS3232_REG_SRAM_START 0x14
+#define DS3232_REG_SRAM_END 0xFF
+
+#define DS3232_REG_SRAM_SIZE 236
struct ds3232 {
struct device *dev;
@@ -461,11 +461,39 @@
.alarm_irq_enable = ds3232_alarm_irq_enable,
};
+static int ds3232_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct regmap *ds3232_regmap = (struct regmap *)priv;
+
+ return regmap_bulk_read(ds3232_regmap, DS3232_REG_SRAM_START + offset,
+ val, bytes);
+}
+
+static int ds3232_nvmem_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct regmap *ds3232_regmap = (struct regmap *)priv;
+
+ return regmap_bulk_write(ds3232_regmap, DS3232_REG_SRAM_START + offset,
+ val, bytes);
+}
+
static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
const char *name)
{
struct ds3232 *ds3232;
int ret;
+ struct nvmem_config nvmem_cfg = {
+ .name = "ds3232_sram",
+ .stride = 1,
+ .size = DS3232_REG_SRAM_SIZE,
+ .word_size = 1,
+ .reg_read = ds3232_nvmem_read,
+ .reg_write = ds3232_nvmem_write,
+ .priv = regmap,
+ .type = NVMEM_TYPE_BATTERY_BACKED
+ };
ds3232 = devm_kzalloc(dev, sizeof(*ds3232), GFP_KERNEL);
if (!ds3232)
@@ -490,6 +518,10 @@
if (IS_ERR(ds3232->rtc))
return PTR_ERR(ds3232->rtc);
+ ret = rtc_nvmem_register(ds3232->rtc, &nvmem_cfg);
+ if(ret)
+ return ret;
+
if (ds3232->irq > 0) {
ret = devm_request_threaded_irq(dev, ds3232->irq, NULL,
ds3232_irq,
@@ -542,7 +574,7 @@
static const struct regmap_config config = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = 0x13,
+ .max_register = DS3232_REG_SRAM_END,
};
regmap = devm_regmap_init_i2c(client, &config);
@@ -609,7 +641,7 @@
static const struct regmap_config config = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = 0x13,
+ .max_register = DS3232_REG_SRAM_END,
.write_flag_mask = 0x80,
};
struct regmap *regmap;
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index 3454e78..edb64de 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* rtc-efi: RTC Class Driver for EFI-based systems
*
@@ -5,12 +6,6 @@
*
* Author: dann frazier <dannf@dannf.org>
* Based on efirtc.c by Stephane Eranian
- *
- * 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.
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c
index b0ef8cf..77cca13 100644
--- a/drivers/rtc/rtc-em3027.c
+++ b/drivers/rtc/rtc-em3027.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An rtc/i2c driver for the EM Microelectronic EM3027
* Copyright 2011 CompuLab, Ltd.
@@ -5,10 +6,6 @@
* Author: Mike Rapoport <mike@compulab.co.il>
*
* Based on rtc-ds1672.c by Alessandro Zummo <a.zummo@towertech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/i2c.h>
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index 6940382..1766496 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* A driver for the RTC embedded in the Cirrus Logic EP93XX processors
* Copyright (c) 2006 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -18,27 +15,23 @@
#define EP93XX_RTC_DATA 0x000
#define EP93XX_RTC_MATCH 0x004
#define EP93XX_RTC_STATUS 0x008
-#define EP93XX_RTC_STATUS_INTR (1<<0)
+#define EP93XX_RTC_STATUS_INTR BIT(0)
#define EP93XX_RTC_LOAD 0x00C
#define EP93XX_RTC_CONTROL 0x010
-#define EP93XX_RTC_CONTROL_MIE (1<<0)
+#define EP93XX_RTC_CONTROL_MIE BIT(0)
#define EP93XX_RTC_SWCOMP 0x108
#define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000
#define EP93XX_RTC_SWCOMP_DEL_SHIFT 16
#define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff
#define EP93XX_RTC_SWCOMP_INT_SHIFT 0
-/*
- * struct device dev.platform_data is used to store our private data
- * because struct rtc_device does not have a variable to hold it.
- */
struct ep93xx_rtc {
void __iomem *mmio_base;
struct rtc_device *rtc;
};
static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload,
- unsigned short *delete)
+ unsigned short *delete)
{
struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
unsigned long comp;
@@ -61,15 +54,16 @@
struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
unsigned long time;
- time = readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
+ time = readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
- rtc_time_to_tm(time, tm);
+ rtc_time64_to_tm(time, tm);
return 0;
}
-static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int ep93xx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
+ unsigned long secs = rtc_tm_to_time64(tm);
writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD);
return 0;
@@ -89,31 +83,31 @@
static const struct rtc_class_ops ep93xx_rtc_ops = {
.read_time = ep93xx_rtc_read_time,
- .set_mmss = ep93xx_rtc_set_mmss,
+ .set_time = ep93xx_rtc_set_time,
.proc = ep93xx_rtc_proc,
};
-static ssize_t ep93xx_rtc_show_comp_preload(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t comp_preload_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
unsigned short preload;
- ep93xx_rtc_get_swcomp(dev, &preload, NULL);
+ ep93xx_rtc_get_swcomp(dev->parent, &preload, NULL);
return sprintf(buf, "%d\n", preload);
}
-static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_rtc_show_comp_preload, NULL);
+static DEVICE_ATTR_RO(comp_preload);
-static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t comp_delete_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
unsigned short delete;
- ep93xx_rtc_get_swcomp(dev, NULL, &delete);
+ ep93xx_rtc_get_swcomp(dev->parent, NULL, &delete);
return sprintf(buf, "%d\n", delete);
}
-static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL);
+static DEVICE_ATTR_RO(comp_delete);
static struct attribute *ep93xx_rtc_attrs[] = {
&dev_attr_comp_preload.attr,
@@ -140,33 +134,20 @@
if (IS_ERR(ep93xx_rtc->mmio_base))
return PTR_ERR(ep93xx_rtc->mmio_base);
- pdev->dev.platform_data = ep93xx_rtc;
platform_set_drvdata(pdev, ep93xx_rtc);
- ep93xx_rtc->rtc = devm_rtc_device_register(&pdev->dev,
- pdev->name, &ep93xx_rtc_ops, THIS_MODULE);
- if (IS_ERR(ep93xx_rtc->rtc)) {
- err = PTR_ERR(ep93xx_rtc->rtc);
- goto exit;
- }
+ ep93xx_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(ep93xx_rtc->rtc))
+ return PTR_ERR(ep93xx_rtc->rtc);
- err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
+ ep93xx_rtc->rtc->ops = &ep93xx_rtc_ops;
+ ep93xx_rtc->rtc->range_max = U32_MAX;
+
+ err = rtc_add_group(ep93xx_rtc->rtc, &ep93xx_rtc_sysfs_files);
if (err)
- goto exit;
+ return err;
- return 0;
-
-exit:
- pdev->dev.platform_data = NULL;
- return err;
-}
-
-static int ep93xx_rtc_remove(struct platform_device *pdev)
-{
- sysfs_remove_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
- pdev->dev.platform_data = NULL;
-
- return 0;
+ return rtc_register_device(ep93xx_rtc->rtc);
}
static struct platform_driver ep93xx_rtc_driver = {
@@ -174,7 +155,6 @@
.name = "ep93xx-rtc",
},
.probe = ep93xx_rtc_probe,
- .remove = ep93xx_rtc_remove,
};
module_platform_driver(ep93xx_rtc_driver);
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c
index e113767..677ec2d 100644
--- a/drivers/rtc/rtc-fm3130.c
+++ b/drivers/rtc/rtc-fm3130.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rtc-fm3130.c - RTC driver for Ramtron FM3130 I2C chip.
*
* Copyright (C) 2008 Sergey Lapin
* Based on ds1307 driver by James Chapman and David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -107,8 +104,7 @@
fm3130_rtc_mode(dev, FM3130_MODE_READ);
/* read the RTC date and time registers all at once */
- tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
- fm3130->msg, 2);
+ tmp = i2c_transfer(fm3130->client->adapter, fm3130->msg, 2);
if (tmp != 2) {
dev_err(dev, "%s error %d\n", "read", tmp);
return -EIO;
@@ -200,8 +196,7 @@
}
/* read the RTC alarm registers all at once */
- tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
- &fm3130->msg[2], 2);
+ tmp = i2c_transfer(fm3130->client->adapter, &fm3130->msg[2], 2);
if (tmp != 2) {
dev_err(dev, "%s error %d\n", "read", tmp);
return -EIO;
@@ -351,7 +346,7 @@
struct fm3130 *fm3130;
int err = -ENODEV;
int tmp;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct i2c_adapter *adapter = client->adapter;
if (!i2c_check_functionality(adapter,
I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
diff --git a/drivers/rtc/rtc-fsl-ftm-alarm.c b/drivers/rtc/rtc-fsl-ftm-alarm.c
new file mode 100644
index 0000000..8df2075
--- /dev/null
+++ b/drivers/rtc/rtc-fsl-ftm-alarm.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Freescale FlexTimer Module (FTM) alarm device driver.
+ *
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2019 NXP
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/fsl/ftm.h>
+#include <linux/rtc.h>
+#include <linux/time.h>
+
+#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT)
+
+/*
+ * Select Fixed frequency clock (32KHz) as clock source
+ * of FlexTimer Module
+ */
+#define FTM_SC_CLKS_FIXED_FREQ 0x02
+#define FIXED_FREQ_CLK 32000
+
+/* Select 128 (2^7) as divider factor */
+#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
+
+/* Maximum counter value in FlexTimer's CNT registers */
+#define MAX_COUNT_VAL 0xffff
+
+struct ftm_rtc {
+ struct rtc_device *rtc_dev;
+ void __iomem *base;
+ bool big_endian;
+ u32 alarm_freq;
+};
+
+static inline u32 rtc_readl(struct ftm_rtc *dev, u32 reg)
+{
+ if (dev->big_endian)
+ return ioread32be(dev->base + reg);
+ else
+ return ioread32(dev->base + reg);
+}
+
+static inline void rtc_writel(struct ftm_rtc *dev, u32 reg, u32 val)
+{
+ if (dev->big_endian)
+ iowrite32be(val, dev->base + reg);
+ else
+ iowrite32(val, dev->base + reg);
+}
+
+static inline void ftm_counter_enable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ /* select and enable counter clock source */
+ val = rtc_readl(rtc, FTM_SC);
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
+ val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_counter_disable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ /* disable counter clock source */
+ val = rtc_readl(rtc, FTM_SC);
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_irq_acknowledge(struct ftm_rtc *rtc)
+{
+ unsigned int timeout = 100;
+
+ /*
+ *Fix errata A-007728 for flextimer
+ * If the FTM counter reaches the FTM_MOD value between
+ * the reading of the TOF bit and the writing of 0 to
+ * the TOF bit, the process of clearing the TOF bit
+ * does not work as expected when FTMx_CONF[NUMTOF] != 0
+ * and the current TOF count is less than FTMx_CONF[NUMTOF].
+ * If the above condition is met, the TOF bit remains set.
+ * If the TOF interrupt is enabled (FTMx_SC[TOIE] = 1),the
+ * TOF interrupt also remains asserted.
+ *
+ * Above is the errata discription
+ *
+ * In one word: software clearing TOF bit not works when
+ * FTMx_CONF[NUMTOF] was seted as nonzero and FTM counter
+ * reaches the FTM_MOD value.
+ *
+ * The workaround is clearing TOF bit until it works
+ * (FTM counter doesn't always reache the FTM_MOD anyway),
+ * which may cost some cycles.
+ */
+ while ((FTM_SC_TOF & rtc_readl(rtc, FTM_SC)) && timeout--)
+ rtc_writel(rtc, FTM_SC, rtc_readl(rtc, FTM_SC) & (~FTM_SC_TOF));
+}
+
+static inline void ftm_irq_enable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ val = rtc_readl(rtc, FTM_SC);
+ val |= FTM_SC_TOIE;
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_irq_disable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ val = rtc_readl(rtc, FTM_SC);
+ val &= ~FTM_SC_TOIE;
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_reset_counter(struct ftm_rtc *rtc)
+{
+ /*
+ * The CNT register contains the FTM counter value.
+ * Reset clears the CNT register. Writing any value to COUNT
+ * updates the counter with its initial value, CNTIN.
+ */
+ rtc_writel(rtc, FTM_CNT, 0x00);
+}
+
+static void ftm_clean_alarm(struct ftm_rtc *rtc)
+{
+ ftm_counter_disable(rtc);
+
+ rtc_writel(rtc, FTM_CNTIN, 0x00);
+ rtc_writel(rtc, FTM_MOD, ~0U);
+
+ ftm_reset_counter(rtc);
+}
+
+static irqreturn_t ftm_rtc_alarm_interrupt(int irq, void *dev)
+{
+ struct ftm_rtc *rtc = dev;
+
+ ftm_irq_acknowledge(rtc);
+ ftm_irq_disable(rtc);
+ ftm_clean_alarm(rtc);
+
+ return IRQ_HANDLED;
+}
+
+static int ftm_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct ftm_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ ftm_irq_enable(rtc);
+ else
+ ftm_irq_disable(rtc);
+
+ return 0;
+}
+
+/*
+ * Note:
+ * The function is not really getting time from the RTC
+ * since FlexTimer is not a RTC device, but we need to
+ * get time to setup alarm, so we are using system time
+ * for now.
+ */
+static int ftm_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct timespec64 ts64;
+
+ ktime_get_real_ts64(&ts64);
+ rtc_time_to_tm(ts64.tv_sec, tm);
+
+ return 0;
+}
+
+static int ftm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ return 0;
+}
+
+/*
+ * 1. Select fixed frequency clock (32KHz) as clock source;
+ * 2. Select 128 (2^7) as divider factor;
+ * So clock is 250 Hz (32KHz/128).
+ *
+ * 3. FlexTimer's CNT register is a 32bit register,
+ * but the register's 16 bit as counter value,it's other 16 bit
+ * is reserved.So minimum counter value is 0x0,maximum counter
+ * value is 0xffff.
+ * So max alarm value is 262 (65536 / 250) seconds
+ */
+static int ftm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_time tm;
+ unsigned long now, alm_time, cycle;
+ struct ftm_rtc *rtc = dev_get_drvdata(dev);
+
+ ftm_rtc_read_time(dev, &tm);
+ rtc_tm_to_time(&tm, &now);
+ rtc_tm_to_time(&alm->time, &alm_time);
+
+ ftm_clean_alarm(rtc);
+ cycle = (alm_time - now) * rtc->alarm_freq;
+ if (cycle > MAX_COUNT_VAL) {
+ pr_err("Out of alarm range {0~262} seconds.\n");
+ return -ERANGE;
+ }
+
+ ftm_irq_disable(rtc);
+
+ /*
+ * The counter increments until the value of MOD is reached,
+ * at which point the counter is reloaded with the value of CNTIN.
+ * The TOF (the overflow flag) bit is set when the FTM counter
+ * changes from MOD to CNTIN. So we should using the cycle - 1.
+ */
+ rtc_writel(rtc, FTM_MOD, cycle - 1);
+
+ ftm_counter_enable(rtc);
+ ftm_irq_enable(rtc);
+
+ return 0;
+
+}
+
+static const struct rtc_class_ops ftm_rtc_ops = {
+ .read_time = ftm_rtc_read_time,
+ .read_alarm = ftm_rtc_read_alarm,
+ .set_alarm = ftm_rtc_set_alarm,
+ .alarm_irq_enable = ftm_rtc_alarm_irq_enable,
+};
+
+static int ftm_rtc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *r;
+ int irq;
+ int ret;
+ struct ftm_rtc *rtc;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (unlikely(!rtc)) {
+ dev_err(&pdev->dev, "cannot alloc memory for rtc\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "cannot get resource for rtc\n");
+ return -ENODEV;
+ }
+
+ rtc->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(rtc->base)) {
+ dev_err(&pdev->dev, "cannot ioremap resource for rtc\n");
+ return PTR_ERR(rtc->base);
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "unable to get IRQ from DT, %d\n", irq);
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, ftm_rtc_alarm_interrupt,
+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ rtc->big_endian = of_property_read_bool(np, "big-endian");
+ rtc->alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
+ rtc->rtc_dev->ops = &ftm_rtc_ops;
+
+ device_init_wakeup(&pdev->dev, true);
+
+ ret = rtc_register_device(rtc->rtc_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't register rtc device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ftm_rtc_match[] = {
+ { .compatible = "fsl,ls1012a-ftm-alarm", },
+ { .compatible = "fsl,ls1021a-ftm-alarm", },
+ { .compatible = "fsl,ls1028a-ftm-alarm", },
+ { .compatible = "fsl,ls1043a-ftm-alarm", },
+ { .compatible = "fsl,ls1046a-ftm-alarm", },
+ { .compatible = "fsl,ls1088a-ftm-alarm", },
+ { .compatible = "fsl,ls208xa-ftm-alarm", },
+ { .compatible = "fsl,lx2160a-ftm-alarm", },
+ { },
+};
+
+static struct platform_driver ftm_rtc_driver = {
+ .probe = ftm_rtc_probe,
+ .driver = {
+ .name = "ftm-alarm",
+ .of_match_table = ftm_rtc_match,
+ },
+};
+
+static int __init ftm_alarm_init(void)
+{
+ return platform_driver_register(&ftm_rtc_driver);
+}
+
+device_initcall(ftm_alarm_init);
+
+MODULE_DESCRIPTION("NXP/Freescale FlexTimer alarm driver");
+MODULE_AUTHOR("Biwen Li <biwen.li@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ftrtc010.c b/drivers/rtc/rtc-ftrtc010.c
index 8f1dd88..0919f7d 100644
--- a/drivers/rtc/rtc-ftrtc010.c
+++ b/drivers/rtc/rtc-ftrtc010.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Faraday Technology FTRTC010 driver
*
* Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
*
- * 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.
- *
* Original code for older kernel 2.6.15 are from Stormlinksemi
* first update from Janos Laube for > 2.6.29 kernels
*
diff --git a/drivers/rtc/rtc-generic.c b/drivers/rtc/rtc-generic.c
index 1bf5d23..89ae78e 100644
--- a/drivers/rtc/rtc-generic.c
+++ b/drivers/rtc/rtc-generic.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* rtc-generic: RTC driver using the generic RTC abstraction
*
* Copyright (C) 2008 Kyle McMartin <kyle@mcmartin.ca>
diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c
index a1c44d0..1a3420e 100644
--- a/drivers/rtc/rtc-goldfish.c
+++ b/drivers/rtc/rtc-goldfish.c
@@ -1,23 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
/* drivers/rtc/rtc-goldfish.c
*
* Copyright (C) 2007 Google, Inc.
* Copyright (C) 2017 Imagination Technologies Ltd.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/io.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
-#include <linux/io.h>
#define TIMER_TIME_LOW 0x00 /* get low bits of current time */
/* and update TIMER_TIME_HIGH */
@@ -56,7 +48,7 @@
do_div(rtc_alarm, NSEC_PER_SEC);
memset(alrm, 0, sizeof(struct rtc_wkalrm));
- rtc_time_to_tm(rtc_alarm, &alrm->time);
+ rtc_time64_to_tm(rtc_alarm, &alrm->time);
if (readl(base + TIMER_ALARM_STATUS))
alrm->enabled = 1;
@@ -70,21 +62,15 @@
struct rtc_wkalrm *alrm)
{
struct goldfish_rtc *rtcdrv;
- unsigned long rtc_alarm;
u64 rtc_alarm64;
u64 rtc_status_reg;
void __iomem *base;
- int ret = 0;
rtcdrv = dev_get_drvdata(dev);
base = rtcdrv->base;
if (alrm->enabled) {
- ret = rtc_tm_to_time(&alrm->time, &rtc_alarm);
- if (ret != 0)
- return ret;
-
- rtc_alarm64 = rtc_alarm * NSEC_PER_SEC;
+ rtc_alarm64 = rtc_tm_to_time64(&alrm->time) * NSEC_PER_SEC;
writel((rtc_alarm64 >> 32), base + TIMER_ALARM_HIGH);
writel(rtc_alarm64, base + TIMER_ALARM_LOW);
} else {
@@ -98,7 +84,7 @@
writel(1, base + TIMER_CLEAR_ALARM);
}
- return ret;
+ return 0;
}
static int goldfish_rtc_alarm_irq_enable(struct device *dev,
@@ -147,7 +133,7 @@
do_div(time, NSEC_PER_SEC);
- rtc_time_to_tm(time, tm);
+ rtc_time64_to_tm(time, tm);
return 0;
}
@@ -156,21 +142,16 @@
{
struct goldfish_rtc *rtcdrv;
void __iomem *base;
- unsigned long now;
u64 now64;
- int ret;
rtcdrv = dev_get_drvdata(dev);
base = rtcdrv->base;
- ret = rtc_tm_to_time(tm, &now);
- if (ret == 0) {
- now64 = now * NSEC_PER_SEC;
- writel((now64 >> 32), base + TIMER_TIME_HIGH);
- writel(now64, base + TIMER_TIME_LOW);
- }
+ now64 = rtc_tm_to_time64(tm) * NSEC_PER_SEC;
+ writel((now64 >> 32), base + TIMER_TIME_HIGH);
+ writel(now64, base + TIMER_TIME_LOW);
- return ret;
+ return 0;
}
static const struct rtc_class_ops goldfish_rtc_ops = {
@@ -205,19 +186,20 @@
if (rtcdrv->irq < 0)
return -ENODEV;
- rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &goldfish_rtc_ops,
- THIS_MODULE);
+ rtcdrv->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtcdrv->rtc))
return PTR_ERR(rtcdrv->rtc);
+ rtcdrv->rtc->ops = &goldfish_rtc_ops;
+ rtcdrv->rtc->range_max = U64_MAX / NSEC_PER_SEC;
+
err = devm_request_irq(&pdev->dev, rtcdrv->irq,
goldfish_rtc_interrupt,
0, pdev->name, rtcdrv);
if (err)
return err;
- return 0;
+ return rtc_register_device(rtcdrv->rtc);
}
static const struct of_device_id goldfish_rtc_of_match[] = {
diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c
index 3e1abb4..1b42ee0 100644
--- a/drivers/rtc/rtc-hid-sensor-time.c
+++ b/drivers/rtc/rtc-hid-sensor-time.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensor Time Driver
* Copyright (c) 2012, Alexander Holler.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
@@ -205,8 +192,7 @@
static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned long flags;
- struct hid_time_state *time_state =
- platform_get_drvdata(to_platform_device(dev));
+ struct hid_time_state *time_state = dev_get_drvdata(dev);
int ret;
reinit_completion(&time_state->comp_last_time);
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
index e5ad527..443f6d0 100644
--- a/drivers/rtc/rtc-hym8563.c
+++ b/drivers/rtc/rtc-hym8563.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Haoyu HYM8563 RTC driver
*
@@ -6,15 +7,6 @@
*
* based on rtc-HYM8563
* Copyright (C) 2010 ROCKCHIP, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/module.h>
@@ -109,6 +101,8 @@
}
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);
diff --git a/drivers/rtc/rtc-imx-sc.c b/drivers/rtc/rtc-imx-sc.c
new file mode 100644
index 0000000..cf2c121
--- /dev/null
+++ b/drivers/rtc/rtc-imx-sc.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP.
+ */
+
+#include <dt-bindings/firmware/imx/rsrc.h>
+#include <linux/arm-smccc.h>
+#include <linux/firmware/imx/sci.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9
+#define IMX_SC_TIMER_FUNC_SET_RTC_ALARM 8
+#define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6
+
+#define IMX_SIP_SRTC 0xC2000002
+#define IMX_SIP_SRTC_SET_TIME 0x0
+
+#define SC_IRQ_GROUP_RTC 2
+#define SC_IRQ_RTC 1
+
+static struct imx_sc_ipc *rtc_ipc_handle;
+static struct rtc_device *imx_sc_rtc;
+
+struct imx_sc_msg_timer_get_rtc_time {
+ struct imx_sc_rpc_msg hdr;
+ u32 time;
+} __packed;
+
+struct imx_sc_msg_timer_rtc_set_alarm {
+ struct imx_sc_rpc_msg hdr;
+ u16 year;
+ u8 mon;
+ u8 day;
+ u8 hour;
+ u8 min;
+ u8 sec;
+} __packed;
+
+static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct imx_sc_msg_timer_get_rtc_time msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_TIMER;
+ hdr->func = IMX_SC_TIMER_FUNC_GET_RTC_SEC1970;
+ hdr->size = 1;
+
+ ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true);
+ if (ret) {
+ dev_err(dev, "read rtc time failed, ret %d\n", ret);
+ return ret;
+ }
+
+ 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 int imx_sc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ return imx_scu_irq_group_enable(SC_IRQ_GROUP_RTC, SC_IRQ_RTC, enable);
+}
+
+static int imx_sc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ /*
+ * SCU firmware does NOT provide read alarm API, but .read_alarm
+ * callback is required by RTC framework to support alarm function,
+ * so just return here.
+ */
+ return 0;
+}
+
+static int imx_sc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct imx_sc_msg_timer_rtc_set_alarm msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+ struct rtc_time *alrm_tm = &alrm->time;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_TIMER;
+ hdr->func = IMX_SC_TIMER_FUNC_SET_RTC_ALARM;
+ hdr->size = 3;
+
+ msg.year = alrm_tm->tm_year + 1900;
+ msg.mon = alrm_tm->tm_mon + 1;
+ msg.day = alrm_tm->tm_mday;
+ msg.hour = alrm_tm->tm_hour;
+ msg.min = alrm_tm->tm_min;
+ msg.sec = alrm_tm->tm_sec;
+
+ ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true);
+ if (ret) {
+ dev_err(dev, "set rtc alarm failed, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = imx_sc_rtc_alarm_irq_enable(dev, alrm->enabled);
+ if (ret) {
+ dev_err(dev, "enable rtc alarm failed, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops imx_sc_rtc_ops = {
+ .read_time = imx_sc_rtc_read_time,
+ .set_time = imx_sc_rtc_set_time,
+ .read_alarm = imx_sc_rtc_read_alarm,
+ .set_alarm = imx_sc_rtc_set_alarm,
+ .alarm_irq_enable = imx_sc_rtc_alarm_irq_enable,
+};
+
+static int imx_sc_rtc_alarm_notify(struct notifier_block *nb,
+ unsigned long event, void *group)
+{
+ /* ignore non-rtc irq */
+ if (!((event & SC_IRQ_RTC) && (*(u8 *)group == SC_IRQ_GROUP_RTC)))
+ return 0;
+
+ rtc_update_irq(imx_sc_rtc, 1, RTC_IRQF | RTC_AF);
+
+ return 0;
+}
+
+static struct notifier_block imx_sc_rtc_alarm_sc_notifier = {
+ .notifier_call = imx_sc_rtc_alarm_notify,
+};
+
+static int imx_sc_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = imx_scu_get_handle(&rtc_ipc_handle);
+ if (ret)
+ return ret;
+
+ device_init_wakeup(&pdev->dev, true);
+
+ imx_sc_rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(imx_sc_rtc))
+ return PTR_ERR(imx_sc_rtc);
+
+ imx_sc_rtc->ops = &imx_sc_rtc_ops;
+ imx_sc_rtc->range_min = 0;
+ imx_sc_rtc->range_max = U32_MAX;
+
+ ret = rtc_register_device(imx_sc_rtc);
+ if (ret)
+ return ret;
+
+ imx_scu_irq_register_notifier(&imx_sc_rtc_alarm_sc_notifier);
+
+ return 0;
+}
+
+static const struct of_device_id imx_sc_dt_ids[] = {
+ { .compatible = "fsl,imx8qxp-sc-rtc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, imx_sc_dt_ids);
+
+static struct platform_driver imx_sc_rtc_driver = {
+ .driver = {
+ .name = "imx-sc-rtc",
+ .of_match_table = imx_sc_dt_ids,
+ },
+ .probe = imx_sc_rtc_probe,
+};
+module_platform_driver(imx_sc_rtc_driver);
+
+MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
+MODULE_DESCRIPTION("NXP i.MX System Controller RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
index 8093111..f21dc6b 100644
--- a/drivers/rtc/rtc-imxdi.c
+++ b/drivers/rtc/rtc-imxdi.c
@@ -1,20 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2010 Orex Computed Radiography
*/
/*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-/* based on rtc-mc13892.c */
-
-/*
* This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
* to implement a Linux RTC. Times and alarms are truncated to seconds.
* Since the RTC framework performs API locking via rtc->ops_lock the
@@ -552,7 +542,7 @@
unsigned long now;
now = readl(imxdi->ioaddr + DTCMR);
- rtc_time_to_tm(now, tm);
+ rtc_time64_to_tm(now, tm);
return 0;
}
@@ -561,7 +551,7 @@
* set the seconds portion of dryice time counter and clear the
* fractional part.
*/
-static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int dryice_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
u32 dcr, dsr;
@@ -588,7 +578,7 @@
if (rc != 0)
return rc;
- rc = di_write_wait(imxdi, secs, DTCMR);
+ rc = di_write_wait(imxdi, rtc_tm_to_time64(tm), DTCMR);
if (rc != 0)
return rc;
@@ -618,7 +608,7 @@
u32 dcamr;
dcamr = readl(imxdi->ioaddr + DCAMR);
- rtc_time_to_tm(dcamr, &alarm->time);
+ rtc_time64_to_tm(dcamr, &alarm->time);
/* alarm is enabled if the interrupt is enabled */
alarm->enabled = (readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
@@ -640,21 +630,10 @@
static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
- unsigned long now;
- unsigned long alarm_time;
int rc;
- rc = rtc_tm_to_time(&alarm->time, &alarm_time);
- if (rc)
- return rc;
-
- /* don't allow setting alarm in the past */
- now = readl(imxdi->ioaddr + DTCMR);
- if (alarm_time < now)
- return -EINVAL;
-
/* write the new alarm time */
- rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+ rc = di_write_wait(imxdi, rtc_tm_to_time64(&alarm->time), DCAMR);
if (rc)
return rc;
@@ -668,7 +647,7 @@
static const struct rtc_class_ops dryice_rtc_ops = {
.read_time = dryice_rtc_read_time,
- .set_mmss = dryice_rtc_set_mmss,
+ .set_time = dryice_rtc_set_time,
.alarm_irq_enable = dryice_rtc_alarm_irq_enable,
.read_alarm = dryice_rtc_read_alarm,
.set_alarm = dryice_rtc_set_alarm,
@@ -761,7 +740,6 @@
*/
static int __init dryice_rtc_probe(struct platform_device *pdev)
{
- struct resource *res;
struct imxdi_dev *imxdi;
int norm_irq, sec_irq;
int rc;
@@ -772,8 +750,7 @@
imxdi->pdev = pdev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- imxdi->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ imxdi->ioaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(imxdi->ioaddr))
return PTR_ERR(imxdi->ioaddr);
@@ -796,6 +773,10 @@
mutex_init(&imxdi->write_mutex);
+ imxdi->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(imxdi->rtc))
+ return PTR_ERR(imxdi->rtc);
+
imxdi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(imxdi->clk))
return PTR_ERR(imxdi->clk);
@@ -829,12 +810,13 @@
}
platform_set_drvdata(pdev, imxdi);
- imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &dryice_rtc_ops, THIS_MODULE);
- if (IS_ERR(imxdi->rtc)) {
- rc = PTR_ERR(imxdi->rtc);
+
+ imxdi->rtc->ops = &dryice_rtc_ops;
+ imxdi->rtc->range_max = U32_MAX;
+
+ rc = rtc_register_device(imxdi->rtc);
+ if (rc)
goto err;
- }
return 0;
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
index 890ccfc..961bd5d 100644
--- a/drivers/rtc/rtc-isl12022.c
+++ b/drivers/rtc/rtc-isl12022.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An I2C driver for the Intersil ISL 12022
*
@@ -5,10 +6,6 @@
*
* Based on the Philips PCF8563 RTC
* by Alessandro Zummo <a.zummo@towertech.it>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
*/
#include <linux/i2c.h>
diff --git a/drivers/rtc/rtc-isl12026.c b/drivers/rtc/rtc-isl12026.c
index 97f594f..5b6b17f 100644
--- a/drivers/rtc/rtc-isl12026.c
+++ b/drivers/rtc/rtc-isl12026.c
@@ -454,9 +454,9 @@
isl12026_force_power_modes(client);
- priv->nvm_client = i2c_new_dummy(client->adapter, ISL12026_EEPROM_ADDR);
- if (!priv->nvm_client)
- return -ENOMEM;
+ priv->nvm_client = i2c_new_dummy_device(client->adapter, ISL12026_EEPROM_ADDR);
+ if (IS_ERR(priv->nvm_client))
+ return PTR_ERR(priv->nvm_client);
priv->rtc = devm_rtc_allocate_device(&client->dev);
ret = PTR_ERR_OR_ZERO(priv->rtc);
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index ea18a8f..ebb691f 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -1,21 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Intersil ISL1208 rtc class driver
*
* Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org>
- *
- * 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.
- *
*/
-#include <linux/module.h>
-#include <linux/i2c.h>
#include <linux/bcd.h>
-#include <linux/rtc.h>
-#include "rtc-core.h"
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
+#include <linux/rtc.h>
/* Register map */
/* rtc section */
@@ -74,10 +69,50 @@
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 */
@@ -85,29 +120,13 @@
isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
unsigned len)
{
- u8 reg_addr[1] = { reg };
- struct i2c_msg msgs[2] = {
- {
- .addr = client->addr,
- .len = sizeof(reg_addr),
- .buf = reg_addr
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = buf
- }
- };
int ret;
WARN_ON(reg > ISL1219_REG_YRT);
WARN_ON(reg + len > ISL1219_REG_YRT + 1);
- ret = i2c_transfer(client->adapter, msgs, 2);
- if (ret > 0)
- ret = 0;
- return ret;
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+ return (ret < 0) ? ret : 0;
}
/* block write */
@@ -115,26 +134,13 @@
isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
unsigned len)
{
- u8 i2c_buf[ISL1208_REG_USR2 + 2];
- struct i2c_msg msgs[1] = {
- {
- .addr = client->addr,
- .len = len + 1,
- .buf = i2c_buf
- }
- };
int ret;
WARN_ON(reg > ISL1219_REG_YRT);
WARN_ON(reg + len > ISL1219_REG_YRT + 1);
- i2c_buf[0] = reg;
- memcpy(&i2c_buf[1], &buf[0], len);
-
- ret = i2c_transfer(client->adapter, msgs, 1);
- if (ret > 0)
- ret = 0;
- return ret;
+ ret = i2c_smbus_write_i2c_block_data(client, reg, len, buf);
+ return (ret < 0) ? ret : 0;
}
/* simple check to see whether we have a isl1208 */
@@ -191,6 +197,7 @@
return atr;
}
+/* returns adjustment value + 100 */
static int
isl1208_i2c_get_dtr(struct i2c_client *client)
{
@@ -201,7 +208,7 @@
/* 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
@@ -278,8 +285,8 @@
(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)
@@ -518,7 +525,7 @@
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
int sr;
sr = isl1208_i2c_get_sr(client);
@@ -540,7 +547,7 @@
static ssize_t timestamp0_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
u8 regs[ISL1219_EVT_SECTION_LEN] = { 0, };
struct rtc_time tm;
int sr;
@@ -586,7 +593,7 @@
{
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;
/*
@@ -609,7 +616,7 @@
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;
@@ -626,11 +633,12 @@
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;
@@ -650,7 +658,7 @@
isl1208_sysfs_show_atrim(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int atr = isl1208_i2c_get_atr(to_i2c_client(dev));
+ int atr = isl1208_i2c_get_atr(to_i2c_client(dev->parent));
if (atr < 0)
return atr;
@@ -663,11 +671,11 @@
isl1208_sysfs_show_dtrim(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev));
+ int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev->parent));
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);
@@ -676,7 +684,7 @@
isl1208_sysfs_show_usr(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int usr = isl1208_i2c_get_usr(to_i2c_client(dev));
+ int usr = isl1208_i2c_get_usr(to_i2c_client(dev->parent));
if (usr < 0)
return usr;
@@ -701,7 +709,10 @@
if (usr < 0 || usr > 0xffff)
return -EINVAL;
- return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count;
+ if (isl1208_i2c_set_usr(to_i2c_client(dev->parent), usr))
+ return -EIO;
+
+ return count;
}
static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,
@@ -727,6 +738,46 @@
.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,
@@ -749,7 +800,7 @@
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))
@@ -758,14 +809,33 @@
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);
- rtc->ops = &isl1208_rtc_ops;
+ /* 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];
+ }
- i2c_set_clientdata(client, rtc);
- dev_set_drvdata(&rtc->dev, client);
+ isl1208->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(isl1208->rtc))
+ return PTR_ERR(isl1208->rtc);
+
+ isl1208->rtc->ops = &isl1208_rtc_ops;
+
+ /* 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) {
@@ -777,7 +847,7 @@
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;
@@ -798,13 +868,15 @@
dev_err(&client->dev, "could not enable tamper detection\n");
return rc;
}
- rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files);
- if (rc)
- return rc;
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;
+ }
- rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
+ rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files);
if (rc)
return rc;
@@ -818,40 +890,19 @@
if (rc)
return rc;
- return rtc_register_device(rtc);
+ rc = rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config);
+ if (rc)
+ return rc;
+
+ return rtc_register_device(isl1208->rtc);
}
-static int
-isl1208_remove(struct i2c_client *client)
-{
- sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
-
- return 0;
-}
-
-static const struct i2c_device_id isl1208_id[] = {
- { "isl1208", TYPE_ISL1208 },
- { "isl1218", TYPE_ISL1218 },
- { "isl1219", TYPE_ISL1219 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, isl1208_id);
-
-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);
-
static struct i2c_driver isl1208_driver = {
.driver = {
.name = "rtc-isl1208",
.of_match_table = of_match_ptr(isl1208_of_match),
},
.probe = isl1208_probe,
- .remove = isl1208_remove,
.id_table = isl1208_id,
};
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index d0a8917..3089645 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
* Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net>
* JZ4740 SoC RTC driver
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <linux/clk.h>
@@ -20,6 +11,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/reboot.h>
#include <linux/rtc.h>
#include <linux/slab.h>
@@ -156,6 +148,9 @@
uint32_t secs, secs2;
int timeout = 5;
+ if (jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD) != 0x12345678)
+ return -EINVAL;
+
/* If the seconds register is read while it is updated, it can contain a
* bogus value. This can be avoided by making sure that two consecutive
* reads have the same value.
@@ -171,16 +166,21 @@
if (timeout == 0)
return -EIO;
- rtc_time_to_tm(secs, time);
+ rtc_time64_to_tm(secs, time);
return 0;
}
-static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int jz4740_rtc_set_time(struct device *dev, struct rtc_time *time)
{
struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
- return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs);
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, rtc_tm_to_time64(time));
+ if (ret)
+ return ret;
+
+ return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
}
static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -196,18 +196,16 @@
alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE);
alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF);
- rtc_time_to_tm(secs, &alrm->time);
+ rtc_time64_to_tm(secs, &alrm->time);
- return rtc_valid_tm(&alrm->time);
+ return 0;
}
static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
int ret;
struct jz4740_rtc *rtc = dev_get_drvdata(dev);
- unsigned long secs;
-
- rtc_tm_to_time(&alrm->time, &secs);
+ uint32_t secs = lower_32_bits(rtc_tm_to_time64(&alrm->time));
ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs);
if (!ret)
@@ -225,7 +223,7 @@
static const struct rtc_class_ops jz4740_rtc_ops = {
.read_time = jz4740_rtc_read_time,
- .set_mmss = jz4740_rtc_set_mmss,
+ .set_time = jz4740_rtc_set_time,
.read_alarm = jz4740_rtc_read_alarm,
.set_alarm = jz4740_rtc_set_alarm,
.alarm_irq_enable = jz4740_rtc_alarm_irq_enable,
@@ -309,7 +307,6 @@
{
int ret;
struct jz4740_rtc *rtc;
- uint32_t scratchpad;
struct resource *mem;
const struct platform_device_id *id = platform_get_device_id(pdev);
const struct of_device_id *of_id = of_match_device(
@@ -326,10 +323,8 @@
rtc->type = id->driver_data;
rtc->irq = platform_get_irq(pdev, 0);
- if (rtc->irq < 0) {
- dev_err(&pdev->dev, "Failed to get platform irq\n");
+ if (rtc->irq < 0)
return -ENOENT;
- }
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rtc->base = devm_ioremap_resource(&pdev->dev, mem);
@@ -348,14 +343,26 @@
device_init_wakeup(&pdev->dev, 1);
- rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &jz4740_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc)) {
- ret = PTR_ERR(rtc->rtc);
- dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret);
+ ret = dev_pm_set_wake_irq(&pdev->dev, rtc->irq);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set wake irq: %d\n", ret);
return ret;
}
+ rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "Failed to allocate rtc device: %d\n", ret);
+ return ret;
+ }
+
+ rtc->rtc->ops = &jz4740_rtc_ops;
+ rtc->rtc->range_max = U32_MAX;
+
+ ret = rtc_register_device(rtc->rtc);
+ if (ret)
+ return ret;
+
ret = devm_request_irq(&pdev->dev, rtc->irq, jz4740_rtc_irq, 0,
pdev->name, rtc);
if (ret) {
@@ -363,16 +370,6 @@
return ret;
}
- scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD);
- if (scratchpad != 0x12345678) {
- ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
- ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
- if (ret) {
- dev_err(&pdev->dev, "Could not write to RTC registers\n");
- return ret;
- }
- }
-
if (np && of_device_is_system_power_controller(np)) {
if (!pm_power_off) {
/* Default: 60ms */
@@ -397,35 +394,6 @@
return 0;
}
-#ifdef CONFIG_PM
-static int jz4740_rtc_suspend(struct device *dev)
-{
- struct jz4740_rtc *rtc = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- enable_irq_wake(rtc->irq);
- return 0;
-}
-
-static int jz4740_rtc_resume(struct device *dev)
-{
- struct jz4740_rtc *rtc = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(rtc->irq);
- return 0;
-}
-
-static const struct dev_pm_ops jz4740_pm_ops = {
- .suspend = jz4740_rtc_suspend,
- .resume = jz4740_rtc_resume,
-};
-#define JZ4740_RTC_PM_OPS (&jz4740_pm_ops)
-
-#else
-#define JZ4740_RTC_PM_OPS NULL
-#endif /* CONFIG_PM */
-
static const struct platform_device_id jz4740_rtc_ids[] = {
{ "jz4740-rtc", ID_JZ4740 },
{ "jz4780-rtc", ID_JZ4780 },
@@ -437,7 +405,6 @@
.probe = jz4740_rtc_probe,
.driver = {
.name = "jz4740-rtc",
- .pm = JZ4740_RTC_PM_OPS,
.of_match_table = of_match_ptr(jz4740_rtc_of_match),
},
.id_table = jz4740_rtc_ids,
diff --git a/drivers/rtc/rtc-lp8788.c b/drivers/rtc/rtc-lp8788.c
index e20e7bd..c0b8fbc 100644
--- a/drivers/rtc/rtc-lp8788.c
+++ b/drivers/rtc/rtc-lp8788.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI LP8788 MFD - rtc driver
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.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.
- *
*/
#include <linux/err.h>
diff --git a/drivers/rtc/rtc-lpc24xx.c b/drivers/rtc/rtc-lpc24xx.c
index 14dc7b0..a8bb156 100644
--- a/drivers/rtc/rtc-lpc24xx.c
+++ b/drivers/rtc/rtc-lpc24xx.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RTC driver for NXP LPC178x/18xx/43xx Real-Time Clock (RTC)
*
* Copyright (C) 2011 NXP Semiconductors
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
- *
- * 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.
- *
*/
#include <linux/clk.h>
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
index 910e600..ac39323 100644
--- a/drivers/rtc/rtc-lpc32xx.c
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2010 NXP Semiconductors
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
@@ -47,8 +39,6 @@
#define LPC32XX_RTC_KEY_ONSW_LOADVAL 0xB5C13F27
-#define RTC_NAME "rtc-lpc32xx"
-
#define rtc_readl(dev, reg) \
__raw_readl((dev)->rtc_base + (reg))
#define rtc_writel(dev, reg, val) \
@@ -68,14 +58,15 @@
struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT);
- rtc_time_to_tm(elapsed_sec, time);
+ rtc_time64_to_tm(elapsed_sec, time);
return 0;
}
-static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int lpc32xx_rtc_set_time(struct device *dev, struct rtc_time *time)
{
struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 secs = rtc_tm_to_time64(time);
u32 tmp;
spin_lock_irq(&rtc->lock);
@@ -97,7 +88,7 @@
{
struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
- rtc_time_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time);
+ rtc_time64_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time);
wkalrm->enabled = rtc->alarm_enabled;
wkalrm->pending = !!(rtc_readl(rtc, LPC32XX_RTC_INTSTAT) &
LPC32XX_RTC_INTSTAT_MATCH0);
@@ -111,13 +102,8 @@
struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
unsigned long alarmsecs;
u32 tmp;
- int ret;
- ret = rtc_tm_to_time(&wkalrm->time, &alarmsecs);
- if (ret < 0) {
- dev_warn(dev, "Failed to convert time: %d\n", ret);
- return ret;
- }
+ alarmsecs = rtc_tm_to_time64(&wkalrm->time);
spin_lock_irq(&rtc->lock);
@@ -191,7 +177,7 @@
static const struct rtc_class_ops lpc32xx_rtc_ops = {
.read_time = lpc32xx_rtc_read_time,
- .set_mmss = lpc32xx_rtc_set_mmss,
+ .set_time = lpc32xx_rtc_set_time,
.read_alarm = lpc32xx_rtc_read_alarm,
.set_alarm = lpc32xx_rtc_set_alarm,
.alarm_irq_enable = lpc32xx_rtc_alarm_irq_enable,
@@ -201,21 +187,13 @@
{
struct resource *res;
struct lpc32xx_rtc *rtc;
- int rtcirq;
+ int err;
u32 tmp;
- rtcirq = platform_get_irq(pdev, 0);
- if (rtcirq < 0) {
- dev_warn(&pdev->dev, "Can't get interrupt resource\n");
- rtcirq = -1;
- }
-
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (unlikely(!rtc))
return -ENOMEM;
- rtc->irq = rtcirq;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rtc->rtc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(rtc->rtc_base))
@@ -256,18 +234,25 @@
platform_set_drvdata(pdev, rtc);
- rtc->rtc = devm_rtc_device_register(&pdev->dev, RTC_NAME,
- &lpc32xx_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc)) {
- dev_err(&pdev->dev, "Can't get RTC\n");
+ rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc))
return PTR_ERR(rtc->rtc);
- }
+
+ rtc->rtc->ops = &lpc32xx_rtc_ops;
+ rtc->rtc->range_max = U32_MAX;
+
+ err = rtc_register_device(rtc->rtc);
+ if (err)
+ return err;
/*
* IRQ is enabled after device registration in case alarm IRQ
* is pending upon suspend exit.
*/
- if (rtc->irq >= 0) {
+ rtc->irq = platform_get_irq(pdev, 0);
+ if (rtc->irq < 0) {
+ dev_warn(&pdev->dev, "Can't get interrupt resource\n");
+ } else {
if (devm_request_irq(&pdev->dev, rtc->irq,
lpc32xx_rtc_alarm_interrupt,
0, pdev->name, rtc) < 0) {
@@ -374,7 +359,7 @@
.probe = lpc32xx_rtc_probe,
.remove = lpc32xx_rtc_remove,
.driver = {
- .name = RTC_NAME,
+ .name = "rtc-lpc32xx",
.pm = LPC32XX_RTC_PM_OPS,
.of_match_table = of_match_ptr(lpc32xx_rtc_match),
},
diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c
index f4c2486..8bd3405 100644
--- a/drivers/rtc/rtc-ls1x.c
+++ b/drivers/rtc/rtc-ls1x.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com>
*
* Derived from driver/rtc/rtc-au1xxx.c
- *
- * 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.
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index ad03e2f..5f46f85 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C client/driver for the ST M41T80 family of i2c rtc chips.
*
@@ -6,11 +7,6 @@
* Based on m41t00.c by Mark A. Greer <mgreer@mvista.com>
*
* 2006 (c) mycable GmbH
- *
- * 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.
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -217,7 +213,7 @@
sizeof(buf), buf);
if (err < 0) {
dev_err(&client->dev, "Unable to read date\n");
- return -EIO;
+ return err;
}
tm->tm_sec = bcd2bin(buf[M41T80_REG_SEC] & 0x7f);
@@ -274,10 +270,11 @@
if (flags < 0)
return flags;
- if (i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS,
- flags & ~M41T80_FLAGS_OF)) {
+ err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS,
+ flags & ~M41T80_FLAGS_OF);
+ if (err < 0) {
dev_err(&client->dev, "Unable to write flags register\n");
- return -EIO;
+ return err;
}
return err;
@@ -287,10 +284,12 @@
{
struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
- u8 reg;
+ int reg;
if (clientdata->features & M41T80_FEATURE_BL) {
reg = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
+ if (reg < 0)
+ return reg;
seq_printf(seq, "battery\t\t: %s\n",
(reg & M41T80_FLAGS_BATT_LOW) ? "exhausted" : "ok");
}
@@ -393,7 +392,7 @@
alrm->time.tm_min = bcd2bin(alarmvals[3] & 0x7f);
alrm->time.tm_hour = bcd2bin(alarmvals[2] & 0x3f);
alrm->time.tm_mday = bcd2bin(alarmvals[1] & 0x3f);
- alrm->time.tm_mon = bcd2bin(alarmvals[0] & 0x3f);
+ alrm->time.tm_mon = bcd2bin(alarmvals[0] & 0x3f) - 1;
alrm->enabled = !!(alarmvals[0] & M41T80_ALMON_AFE);
alrm->pending = (flags & M41T80_FLAGS_AF) && alrm->enabled;
@@ -745,7 +744,7 @@
return -EINVAL;
wdt_margin = new_margin;
wdt_ping();
- /* Fall */
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(wdt_margin, (int __user *)arg);
@@ -799,7 +798,7 @@
*/
wdt_is_open = 1;
mutex_unlock(&m41t80_rtc_mutex);
- return nonseekable_open(inode, file);
+ return stream_open(inode, file);
}
return -ENODEV;
}
@@ -873,7 +872,7 @@
static int m41t80_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct i2c_adapter *adapter = client->adapter;
int rc = 0;
struct rtc_time tm;
struct m41t80_data *m41t80_data = NULL;
@@ -939,11 +938,7 @@
if (m41t80_data->features & M41T80_FEATURE_HT) {
m41t80_rtc_read_time(&client->dev, &tm);
dev_info(&client->dev, "HT bit was set!\n");
- dev_info(&client->dev,
- "Power Down at %04i-%02i-%02i %02i:%02i:%02i\n",
- tm.tm_year + 1900,
- tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
- tm.tm_min, tm.tm_sec);
+ dev_info(&client->dev, "Power Down at %ptR\n", &tm);
}
rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_HOUR,
rc & ~M41T80_ALHOUR_HT);
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index 4a08a9d..9444cb5 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Driver for ST M41T93 SPI RTC
*
* (c) 2010 Nikolaus Voss, Weinmann Medical GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/bcd.h>
diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c
index bab82b4..6803b02 100644
--- a/drivers/rtc/rtc-m41t94.c
+++ b/drivers/rtc/rtc-m41t94.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ST M41T94 SPI RTC
*
* Copyright (C) 2008 Kim B. Heino
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
index 0cf6507..d3a75d4 100644
--- a/drivers/rtc/rtc-m48t35.c
+++ b/drivers/rtc/rtc-m48t35.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the SGS-Thomson M48T35 Timekeeper RAM chip
*
@@ -7,11 +8,6 @@
* Copyright (C) 2008 Thomas Bogendoerfer
*
* Based on code written by Paul Gortmaker.
- *
- * 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.
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index ac9ca10..67e2187 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ST M48T59 RTC driver
*
* Copyright (c) 2007 Wind River Systems, Inc.
*
* Author: Mark Zhan <rongkai.zhan@windriver.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.
*/
#include <linux/kernel.h>
@@ -99,9 +96,7 @@
M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
spin_unlock_irqrestore(&m48t59->lock, flags);
- dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
- tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "RTC read time %ptR\n", tm);
return 0;
}
@@ -188,9 +183,7 @@
M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
spin_unlock_irqrestore(&m48t59->lock, flags);
- dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
- tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "RTC read alarm time %ptR\n", tm);
return rtc_valid_tm(tm);
}
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
index a953353..59b54ed 100644
--- a/drivers/rtc/rtc-m48t86.c
+++ b/drivers/rtc/rtc-m48t86.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ST M48T86 / Dallas DS12887 RTC driver
* Copyright (c) 2006 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
- * 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.
- *
* This drivers only supports the clock running in BCD and 24H mode.
* If it will be ever adapted to binary and 12H mode, care must be taken
* to not introduce bugs.
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c
index 7458274..daaeb6f 100644
--- a/drivers/rtc/rtc-max6902.c
+++ b/drivers/rtc/rtc-max6902.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* drivers/rtc/rtc-max6902.c
*
* Copyright (C) 2006 8D Technologies inc.
* Copyright (C) 2004 Compulab Ltd.
*
- * 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.
- *
* Driver for MAX6902 spi RTC
- *
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-max6916.c b/drivers/rtc/rtc-max6916.c
index 7e908a4..e72e768 100644
--- a/drivers/rtc/rtc-max6916.c
+++ b/drivers/rtc/rtc-max6916.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* rtc-max6916.c
*
* Driver for MAXIM max6916 Low Current, SPI Compatible
* Real Time Clock
*
* Author : Venkat Prashanth B U <venkat.prashanth2498@gmail.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.
- *
*/
#include <linux/init.h>
@@ -86,7 +82,7 @@
if (dt->tm_year < 100 || dt->tm_year > 199) {
dev_err(&spi->dev, "Year must be between 2000 and 2099. It's %d.\n",
dt->tm_year + 1900);
- return -EINVAL;
+ return -EINVAL;
}
buf[0] = MAX6916_CLOCK_BURST & 0x7F;
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index 8a60900..d5a0e27 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -360,7 +360,7 @@
out:
mutex_unlock(&info->lock);
- return 0;
+ return ret;
}
static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -673,11 +673,8 @@
struct platform_device *pdev = to_platform_device(info->dev);
info->rtc_irq = platform_get_irq(pdev, 0);
- if (info->rtc_irq < 0) {
- dev_err(info->dev, "Failed to get rtc interrupts: %d\n",
- info->rtc_irq);
+ if (info->rtc_irq < 0)
return info->rtc_irq;
- }
} else {
info->rtc_irq = parent_i2c->irq;
}
@@ -693,11 +690,11 @@
goto add_rtc_irq;
}
- info->rtc = i2c_new_dummy(parent_i2c->adapter,
- info->drv_data->rtc_i2c_addr);
- if (!info->rtc) {
+ info->rtc = devm_i2c_new_dummy_device(info->dev, parent_i2c->adapter,
+ info->drv_data->rtc_i2c_addr);
+ if (IS_ERR(info->rtc)) {
dev_err(info->dev, "Failed to allocate I2C device for RTC\n");
- return -ENODEV;
+ return PTR_ERR(info->rtc);
}
info->rtc_regmap = devm_regmap_init_i2c(info->rtc,
@@ -705,7 +702,7 @@
if (IS_ERR(info->rtc_regmap)) {
ret = PTR_ERR(info->rtc_regmap);
dev_err(info->dev, "Failed to allocate RTC regmap: %d\n", ret);
- goto err_unregister_i2c;
+ return ret;
}
add_rtc_irq:
@@ -715,15 +712,10 @@
&info->rtc_irq_data);
if (ret < 0) {
dev_err(info->dev, "Failed to add RTC irq chip: %d\n", ret);
- goto err_unregister_i2c;
+ return ret;
}
return 0;
-
-err_unregister_i2c:
- if (info->rtc)
- i2c_unregister_device(info->rtc);
- return ret;
}
static int max77686_rtc_probe(struct platform_device *pdev)
@@ -786,8 +778,6 @@
err_rtc:
regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
- if (info->rtc)
- i2c_unregister_device(info->rtc);
return ret;
}
@@ -798,8 +788,6 @@
free_irq(info->virq, info);
regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
- if (info->rtc)
- i2c_unregister_device(info->rtc);
return 0;
}
diff --git a/drivers/rtc/rtc-max8907.c b/drivers/rtc/rtc-max8907.c
index 19c29b7..db3495d 100644
--- a/drivers/rtc/rtc-max8907.c
+++ b/drivers/rtc/rtc-max8907.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* RTC driver for Maxim MAX8907
*
@@ -5,10 +6,6 @@
*
* Based on drivers/rtc/rtc-max8925.c,
* Copyright (C) 2009-2010 Marvell International Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/bcd.h>
diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c
index 67d6fc2..64bb8ac 100644
--- a/drivers/rtc/rtc-max8925.c
+++ b/drivers/rtc/rtc-max8925.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* RTC driver for Maxim MAX8925
*
* Copyright (C) 2009-2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.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.
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
index 08c661a..20e50d9 100644
--- a/drivers/rtc/rtc-max8997.c
+++ b/drivers/rtc/rtc-max8997.c
@@ -215,7 +215,7 @@
out:
mutex_unlock(&info->lock);
- return 0;
+ return ret;
}
static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
index 0fa3370..afce2c0 100644
--- a/drivers/rtc/rtc-mc13xxx.c
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Real Time Clock driver for Freescale MC13XXX PMIC
*
* (C) 2009 Sascha Hauer, Pengutronix
* (C) 2009 Uwe Kleine-Koenig, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/mfd/mc13xxx.h>
@@ -89,14 +86,14 @@
return 0;
}
-static int mc13xxx_rtc_set_mmss(struct device *dev, time64_t secs)
+static int mc13xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
unsigned int seconds, days;
unsigned int alarmseconds;
int ret;
- days = div_s64_rem(secs, SEC_PER_DAY, &seconds);
+ days = div_s64_rem(rtc_tm_to_time64(tm), SEC_PER_DAY, &seconds);
mc13xxx_lock(priv->mc13xxx);
@@ -158,7 +155,7 @@
static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
- unsigned seconds, days;
+ unsigned int seconds, days;
time64_t s1970;
int enabled, pending;
int ret;
@@ -253,7 +250,7 @@
static const struct rtc_class_ops mc13xxx_rtc_ops = {
.read_time = mc13xxx_rtc_read_time,
- .set_mmss64 = mc13xxx_rtc_set_mmss,
+ .set_time = mc13xxx_rtc_set_time,
.read_alarm = mc13xxx_rtc_read_alarm,
.set_alarm = mc13xxx_rtc_set_alarm,
.alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable,
@@ -285,8 +282,15 @@
priv->mc13xxx = mc13xxx;
priv->valid = 1;
+ priv->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(priv->rtc))
+ return PTR_ERR(priv->rtc);
platform_set_drvdata(pdev, priv);
+ priv->rtc->ops = &mc13xxx_rtc_ops;
+ /* 15bit days + hours, minutes, seconds */
+ priv->rtc->range_max = (timeu64_t)(1 << 15) * SEC_PER_DAY - 1;
+
mc13xxx_lock(mc13xxx);
mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_RTCRST);
@@ -303,8 +307,9 @@
mc13xxx_unlock(mc13xxx);
- priv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &mc13xxx_rtc_ops, THIS_MODULE);
+ ret = rtc_register_device(priv->rtc);
+ if (ret)
+ goto err_irq_request;
return 0;
diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c
index 2f1772a..2ecd875 100644
--- a/drivers/rtc/rtc-mc146818-lib.c
+++ b/drivers/rtc/rtc-mc146818-lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/export.h>
@@ -82,7 +83,7 @@
time->tm_year += real_year - 72;
#endif
- if (century)
+ if (century > 20)
time->tm_year += (century - 19) * 100;
/*
diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c
index 00e11c1..1660d5e 100644
--- a/drivers/rtc/rtc-mcp795.c
+++ b/drivers/rtc/rtc-mcp795.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SPI Driver for Microchip MCP795 RTC
*
@@ -7,11 +8,6 @@
*
* Device datasheet:
* http://ww1.microchip.com/downloads/en/DeviceDoc/22280A.pdf
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
@@ -233,9 +229,7 @@
if (ret)
return ret;
- dev_dbg(dev, "Set mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n",
- tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
- tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec);
+ dev_dbg(dev, "Set mcp795: %ptR\n", tim);
return 0;
}
@@ -258,9 +252,7 @@
tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1;
tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */
- dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n",
- tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
- tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec);
+ dev_dbg(dev, "Read from mcp795: %ptR\n", tim);
return 0;
}
@@ -319,9 +311,8 @@
return ret;
dev_dbg(dev, "Alarm IRQ armed\n");
}
- dev_dbg(dev, "Set alarm: %02d-%02d(%d) %02d:%02d:%02d\n",
- alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday,
- alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
+ dev_dbg(dev, "Set alarm: %ptRdr(%d) %ptRt\n",
+ &alm->time, alm->time.tm_wday, &alm->time);
return 0;
}
@@ -345,9 +336,8 @@
alm->time.tm_isdst = -1;
alm->time.tm_yday = -1;
- dev_dbg(dev, "Read alarm: %02d-%02d(%d) %02d:%02d:%02d\n",
- alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday,
- alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
+ dev_dbg(dev, "Read alarm: %ptRdr(%d) %ptRt\n",
+ &alm->time, alm->time.tm_wday, &alm->time);
return 0;
}
diff --git a/drivers/rtc/rtc-meson-vrtc.c b/drivers/rtc/rtc-meson-vrtc.c
new file mode 100644
index 0000000..89e5ba0
--- /dev/null
+++ b/drivers/rtc/rtc-meson-vrtc.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/time64.h>
+
+struct meson_vrtc_data {
+ void __iomem *io_alarm;
+ struct rtc_device *rtc;
+ unsigned long alarm_time;
+ bool enabled;
+};
+
+static int meson_vrtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct timespec64 time;
+
+ dev_dbg(dev, "%s\n", __func__);
+ ktime_get_raw_ts64(&time);
+ rtc_time64_to_tm(time.tv_sec, tm);
+
+ return 0;
+}
+
+static void meson_vrtc_set_wakeup_time(struct meson_vrtc_data *vrtc,
+ unsigned long time)
+{
+ writel_relaxed(time, vrtc->io_alarm);
+}
+
+static int meson_vrtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct meson_vrtc_data *vrtc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s: alarm->enabled=%d\n", __func__, alarm->enabled);
+ if (alarm->enabled)
+ vrtc->alarm_time = rtc_tm_to_time64(&alarm->time);
+ else
+ vrtc->alarm_time = 0;
+
+ return 0;
+}
+
+static int meson_vrtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct meson_vrtc_data *vrtc = dev_get_drvdata(dev);
+
+ vrtc->enabled = enabled;
+ return 0;
+}
+
+static const struct rtc_class_ops meson_vrtc_ops = {
+ .read_time = meson_vrtc_read_time,
+ .set_alarm = meson_vrtc_set_alarm,
+ .alarm_irq_enable = meson_vrtc_alarm_irq_enable,
+};
+
+static int meson_vrtc_probe(struct platform_device *pdev)
+{
+ struct meson_vrtc_data *vrtc;
+ int ret;
+
+ vrtc = devm_kzalloc(&pdev->dev, sizeof(*vrtc), GFP_KERNEL);
+ if (!vrtc)
+ return -ENOMEM;
+
+ vrtc->io_alarm = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(vrtc->io_alarm))
+ return PTR_ERR(vrtc->io_alarm);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ platform_set_drvdata(pdev, vrtc);
+
+ vrtc->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(vrtc->rtc))
+ return PTR_ERR(vrtc->rtc);
+
+ vrtc->rtc->ops = &meson_vrtc_ops;
+ ret = rtc_register_device(vrtc->rtc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int __maybe_unused meson_vrtc_suspend(struct device *dev)
+{
+ struct meson_vrtc_data *vrtc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (vrtc->alarm_time) {
+ unsigned long local_time;
+ long alarm_secs;
+ struct timespec64 time;
+
+ ktime_get_raw_ts64(&time);
+ local_time = time.tv_sec;
+
+ dev_dbg(dev, "alarm_time = %lus, local_time=%lus\n",
+ vrtc->alarm_time, local_time);
+ alarm_secs = vrtc->alarm_time - local_time;
+ if (alarm_secs > 0) {
+ meson_vrtc_set_wakeup_time(vrtc, alarm_secs);
+ dev_dbg(dev, "system will wakeup in %lds.\n",
+ alarm_secs);
+ } else {
+ dev_err(dev, "alarm time already passed: %lds.\n",
+ alarm_secs);
+ }
+ }
+
+ return 0;
+}
+
+static int __maybe_unused meson_vrtc_resume(struct device *dev)
+{
+ struct meson_vrtc_data *vrtc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ vrtc->alarm_time = 0;
+ meson_vrtc_set_wakeup_time(vrtc, 0);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(meson_vrtc_pm_ops,
+ meson_vrtc_suspend, meson_vrtc_resume);
+
+static const struct of_device_id meson_vrtc_dt_match[] = {
+ { .compatible = "amlogic,meson-vrtc"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, meson_vrtc_dt_match);
+
+static struct platform_driver meson_vrtc_driver = {
+ .probe = meson_vrtc_probe,
+ .driver = {
+ .name = "meson-vrtc",
+ .of_match_table = meson_vrtc_dt_match,
+ .pm = &meson_vrtc_pm_ops,
+ },
+};
+
+module_platform_driver(meson_vrtc_driver);
+
+MODULE_DESCRIPTION("Amlogic Virtual Wakeup RTC Timer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c
new file mode 100644
index 0000000..e08b981
--- /dev/null
+++ b/drivers/rtc/rtc-meson.c
@@ -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");
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index dd03642..15a9d02 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Real-time clock driver for MPC5121
*
* Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
* Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
* Copyright 2011, Dmitry Eremin-Solenikov
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/init.h>
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
index 1925aaf..17bf539 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rtc-mrst.c: Driver for Moorestown virtual RTC
*
@@ -5,11 +6,6 @@
* Author: Jacob Pan (jacob.jun.pan@intel.com)
* Feng Tang (feng.tang@intel.com)
*
- * 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; version 2
- * of the License.
- *
* Note:
* VRTC is emulated by system controller firmware, the real HW
* RTC is located in the PMIC device. SCU FW shadows PMIC RTC
@@ -90,7 +86,7 @@
unsigned long flags;
if (vrtc_is_updating())
- mdelay(20);
+ msleep(20);
spin_lock_irqsave(&rtc_lock, flags);
time->tm_sec = vrtc_cmos_read(RTC_SECONDS);
@@ -261,11 +257,10 @@
static int mrst_procfs(struct device *dev, struct seq_file *seq)
{
- unsigned char rtc_control, valid;
+ unsigned char rtc_control;
spin_lock_irq(&rtc_lock);
rtc_control = vrtc_cmos_read(RTC_CONTROL);
- valid = vrtc_cmos_read(RTC_VALID);
spin_unlock_irq(&rtc_lock);
seq_printf(seq,
diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c
index 0c72a2e..1c2d3c4 100644
--- a/drivers/rtc/rtc-msm6242.c
+++ b/drivers/rtc/rtc-msm6242.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Oki MSM6242 RTC Driver
*
diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c
index 385f830..704229e 100644
--- a/drivers/rtc/rtc-mt6397.c
+++ b/drivers/rtc/rtc-mt6397.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2015 MediaTek Inc.
* Author: Tianping.Fang <tianping.fang@mediatek.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.
-*
-* 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/delay.h>
@@ -332,6 +324,10 @@
platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_allocate_device(rtc->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
ret = request_threaded_irq(rtc->irq, NULL,
mtk_rtc_irq_handler_thread,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
@@ -339,25 +335,21 @@
if (ret) {
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
rtc->irq, ret);
- goto out_dispose_irq;
+ return ret;
}
device_init_wakeup(&pdev->dev, 1);
- rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
- &mtk_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev)) {
- dev_err(&pdev->dev, "register rtc device failed\n");
- ret = PTR_ERR(rtc->rtc_dev);
+ rtc->rtc_dev->ops = &mtk_rtc_ops;
+
+ ret = rtc_register_device(rtc->rtc_dev);
+ if (ret)
goto out_free_irq;
- }
return 0;
out_free_irq:
- free_irq(rtc->irq, rtc->rtc_dev);
-out_dispose_irq:
- irq_dispose_mapping(rtc->irq);
+ free_irq(rtc->irq, rtc);
return ret;
}
@@ -365,9 +357,7 @@
{
struct mt6397_rtc *rtc = platform_get_drvdata(pdev);
- rtc_device_unregister(rtc->rtc_dev);
- free_irq(rtc->irq, rtc->rtc_dev);
- irq_dispose_mapping(rtc->irq);
+ free_irq(rtc->irq, rtc);
return 0;
}
diff --git a/drivers/rtc/rtc-mt7622.c b/drivers/rtc/rtc-mt7622.c
index fd0cea7..16bd26b 100644
--- a/drivers/rtc/rtc-mt7622.c
+++ b/drivers/rtc/rtc-mt7622.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for MediaTek SoC based RTC
*
* Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
- *
- * 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/clk.h>
@@ -338,7 +329,6 @@
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource\n");
ret = hw->irq;
goto err;
}
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index 4b198b3..ab9db57 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the RTC in Marvell SoCs.
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <linux/init.h>
@@ -60,7 +57,7 @@
rtc_reg = (bin2bcd(tm->tm_mday) << RTC_MDAY_OFFS) |
(bin2bcd(tm->tm_mon + 1) << RTC_MONTH_OFFS) |
- (bin2bcd(tm->tm_year % 100) << RTC_YEAR_OFFS);
+ (bin2bcd(tm->tm_year - 100) << RTC_YEAR_OFFS);
writel(rtc_reg, ioaddr + RTC_DATE_REG_OFFS);
return 0;
@@ -125,13 +122,9 @@
/* hw counts from year 2000, but tm_year is relative to 1900 */
alm->time.tm_year = bcd2bin(year) + 100;
- if (rtc_valid_tm(&alm->time) < 0) {
- dev_err(dev, "retrieved alarm date/time is not valid.\n");
- rtc_time_to_tm(0, &alm->time);
- }
-
alm->enabled = !!readl(ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
- return 0;
+
+ return rtc_valid_tm(&alm->time);
}
static int mv_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
@@ -163,7 +156,7 @@
<< RTC_MONTH_OFFS;
if (alm->time.tm_year >= 0)
- rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_year % 100))
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_year - 100))
<< RTC_YEAR_OFFS;
writel(rtc_reg, ioaddr + RTC_ALARM_DATE_REG_OFFS);
@@ -261,15 +254,7 @@
platform_set_drvdata(pdev, pdata);
- if (pdata->irq >= 0) {
- device_init_wakeup(&pdev->dev, 1);
- pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &mv_rtc_alarm_ops,
- THIS_MODULE);
- } else {
- pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &mv_rtc_ops, THIS_MODULE);
- }
+ pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(pdata->rtc)) {
ret = PTR_ERR(pdata->rtc);
goto out;
@@ -285,7 +270,19 @@
}
}
- return 0;
+ if (pdata->irq >= 0) {
+ device_init_wakeup(&pdev->dev, 1);
+ pdata->rtc->ops = &mv_rtc_alarm_ops;
+ } else {
+ pdata->rtc->ops = &mv_rtc_ops;
+ }
+
+ pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+ ret = rtc_register_device(pdata->rtc);
+ if (!ret)
+ return 0;
out:
if (!IS_ERR(pdata->clk))
clk_disable_unprepare(pdata->clk);
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 878c6ee..902d57d 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -8,6 +8,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -183,8 +184,9 @@
struct rtc_plat_data *pdata = dev_get_drvdata(dev);
void __iomem *ioaddr = pdata->ioaddr;
u32 reg;
+ unsigned long flags;
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
reg = readw(ioaddr + RTC_RTCIENR);
if (enabled)
@@ -193,7 +195,7 @@
reg &= ~bit;
writew(reg, ioaddr + RTC_RTCIENR);
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
}
/* This function is the RTC interrupt service routine. */
@@ -253,20 +255,9 @@
/*
* This function sets the internal RTC time based on tm in Gregorian date.
*/
-static int mxc_rtc_set_mmss(struct device *dev, time64_t time)
+static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct rtc_plat_data *pdata = dev_get_drvdata(dev);
-
- /*
- * TTC_DAYR register is 9-bit in MX1 SoC, save time and day of year only
- */
- if (is_imx1_rtc(pdata)) {
- struct rtc_time tm;
-
- rtc_time64_to_tm(time, &tm);
- tm.tm_year = 70;
- time = rtc_tm_to_time64(&tm);
- }
+ time64_t time = rtc_tm_to_time64(tm);
/* Avoid roll-over from reading the different registers */
do {
@@ -310,7 +301,7 @@
/* RTC layer */
static const struct rtc_class_ops mxc_rtc_ops = {
.read_time = mxc_rtc_read_time,
- .set_mmss64 = mxc_rtc_set_mmss,
+ .set_time = mxc_rtc_set_time,
.read_alarm = mxc_rtc_read_alarm,
.set_alarm = mxc_rtc_set_alarm,
.alarm_irq_enable = mxc_rtc_alarm_irq_enable,
@@ -318,7 +309,6 @@
static int mxc_rtc_probe(struct platform_device *pdev)
{
- struct resource *res;
struct rtc_device *rtc;
struct rtc_plat_data *pdata = NULL;
u32 reg;
@@ -336,11 +326,34 @@
else
pdata->devtype = pdev->id_entry->driver_data;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pdata->ioaddr))
return PTR_ERR(pdata->ioaddr);
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ pdata->rtc = rtc;
+ rtc->ops = &mxc_rtc_ops;
+ if (is_imx1_rtc(pdata)) {
+ struct rtc_time tm;
+
+ /* 9bit days + hours minutes seconds */
+ rtc->range_max = (1 << 9) * 86400 - 1;
+
+ /*
+ * Set the start date as beginning of the current year. This can
+ * be overridden using device tree.
+ */
+ rtc_time64_to_tm(ktime_get_real_seconds(), &tm);
+ rtc->start_secs = mktime64(tm.tm_year, 1, 1, 0, 0, 0);
+ rtc->set_start_time = true;
+ } else {
+ /* 16bit days + hours minutes seconds */
+ rtc->range_max = (1 << 16) * 86400ULL - 1;
+ }
+
pdata->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(pdata->clk_ipg)) {
dev_err(&pdev->dev, "unable to get ipg clock!\n");
@@ -396,17 +409,16 @@
pdata->irq = -1;
}
- if (pdata->irq >= 0)
+ if (pdata->irq >= 0) {
device_init_wakeup(&pdev->dev, 1);
-
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &mxc_rtc_ops,
- THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- goto exit_put_clk_ref;
+ ret = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
+ if (ret)
+ dev_err(&pdev->dev, "failed to enable irq wake\n");
}
- pdata->rtc = rtc;
+ ret = rtc_register_device(rtc);
+ if (ret)
+ goto exit_put_clk_ref;
return 0;
@@ -428,35 +440,10 @@
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int mxc_rtc_suspend(struct device *dev)
-{
- struct rtc_plat_data *pdata = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- enable_irq_wake(pdata->irq);
-
- return 0;
-}
-
-static int mxc_rtc_resume(struct device *dev)
-{
- struct rtc_plat_data *pdata = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(pdata->irq);
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(mxc_rtc_pm_ops, mxc_rtc_suspend, mxc_rtc_resume);
-
static struct platform_driver mxc_rtc_driver = {
.driver = {
.name = "mxc_rtc",
.of_match_table = of_match_ptr(imx_rtc_dt_ids),
- .pm = &mxc_rtc_pm_ops,
},
.id_table = imx_rtc_devtype,
.probe = mxc_rtc_probe,
diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c
index 007879a..9153456 100644
--- a/drivers/rtc/rtc-mxc_v2.c
+++ b/drivers/rtc/rtc-mxc_v2.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/rtc.h>
#define SRTC_LPPDR_INIT 0x41736166 /* init for glitch detect */
@@ -278,7 +279,6 @@
static int mxc_rtc_probe(struct platform_device *pdev)
{
struct mxc_rtc_data *pdata;
- struct resource *res;
void __iomem *ioaddr;
int ret = 0;
@@ -286,8 +286,7 @@
if (!pdata)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pdata->ioaddr))
return PTR_ERR(pdata->ioaddr);
@@ -305,6 +304,9 @@
return pdata->irq;
device_init_wakeup(&pdev->dev, 1);
+ ret = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
+ if (ret)
+ dev_err(&pdev->dev, "failed to enable irq wake\n");
ret = clk_prepare_enable(pdata->clk);
if (ret)
@@ -367,30 +369,6 @@
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int mxc_rtc_suspend(struct device *dev)
-{
- struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- enable_irq_wake(pdata->irq);
-
- return 0;
-}
-
-static int mxc_rtc_resume(struct device *dev)
-{
- struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(pdata->irq);
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(mxc_rtc_pm_ops, mxc_rtc_suspend, mxc_rtc_resume);
-
static const struct of_device_id mxc_ids[] = {
{ .compatible = "fsl,imx53-rtc", },
{}
@@ -400,7 +378,6 @@
.driver = {
.name = "mxc_rtc_v2",
.of_match_table = mxc_ids,
- .pm = &mxc_rtc_pm_ops,
},
.probe = mxc_rtc_probe,
.remove = mxc_rtc_remove,
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
deleted file mode 100644
index 7da664a..0000000
--- a/drivers/rtc/rtc-nuc900.c
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (c) 2008-2009 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@gmail.com>
- *
- * 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;version 2 of the License.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/rtc.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/bcd.h>
-
-/* RTC Control Registers */
-#define REG_RTC_INIR 0x00
-#define REG_RTC_AER 0x04
-#define REG_RTC_FCR 0x08
-#define REG_RTC_TLR 0x0C
-#define REG_RTC_CLR 0x10
-#define REG_RTC_TSSR 0x14
-#define REG_RTC_DWR 0x18
-#define REG_RTC_TAR 0x1C
-#define REG_RTC_CAR 0x20
-#define REG_RTC_LIR 0x24
-#define REG_RTC_RIER 0x28
-#define REG_RTC_RIIR 0x2C
-#define REG_RTC_TTR 0x30
-
-#define RTCSET 0x01
-#define AERRWENB 0x10000
-#define INIRRESET 0xa5eb1357
-#define AERPOWERON 0xA965
-#define AERPOWEROFF 0x0000
-#define LEAPYEAR 0x0001
-#define TICKENB 0x80
-#define TICKINTENB 0x0002
-#define ALARMINTENB 0x0001
-#define MODE24 0x0001
-
-struct nuc900_rtc {
- int irq_num;
- void __iomem *rtc_reg;
- struct rtc_device *rtcdev;
-};
-
-struct nuc900_bcd_time {
- int bcd_sec;
- int bcd_min;
- int bcd_hour;
- int bcd_mday;
- int bcd_mon;
- int bcd_year;
-};
-
-static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
-{
- struct nuc900_rtc *rtc = _rtc;
- unsigned long events = 0, rtc_irq;
-
- rtc_irq = __raw_readl(rtc->rtc_reg + REG_RTC_RIIR);
-
- if (rtc_irq & ALARMINTENB) {
- rtc_irq &= ~ALARMINTENB;
- __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
- events |= RTC_AF | RTC_IRQF;
- }
-
- if (rtc_irq & TICKINTENB) {
- rtc_irq &= ~TICKINTENB;
- __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
- events |= RTC_UF | RTC_IRQF;
- }
-
- rtc_update_irq(rtc->rtcdev, 1, events);
-
- return IRQ_HANDLED;
-}
-
-static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
-{
- unsigned int timeout = 0x1000;
- __raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
-
- mdelay(10);
-
- __raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
-
- while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
- && --timeout)
- mdelay(1);
-
- if (!timeout)
- return ERR_PTR(-EPERM);
-
- return NULL;
-}
-
-static void nuc900_rtc_bcd2bin(unsigned int timereg,
- unsigned int calreg, struct rtc_time *tm)
-{
- tm->tm_mday = bcd2bin(calreg >> 0);
- tm->tm_mon = bcd2bin(calreg >> 8);
- tm->tm_year = bcd2bin(calreg >> 16) + 100;
-
- tm->tm_sec = bcd2bin(timereg >> 0);
- tm->tm_min = bcd2bin(timereg >> 8);
- tm->tm_hour = bcd2bin(timereg >> 16);
-}
-
-static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
- struct nuc900_bcd_time *gettm)
-{
- gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
- gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8;
-
- if (settm->tm_year < 100) {
- dev_warn(dev, "The year will be between 1970-1999, right?\n");
- gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
- } else {
- gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
- }
-
- gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
- gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
- gettm->bcd_hour = bin2bcd(settm->tm_hour) << 16;
-}
-
-static int nuc900_alarm_irq_enable(struct device *dev, unsigned int enabled)
-{
- struct nuc900_rtc *rtc = dev_get_drvdata(dev);
-
- if (enabled)
- __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)|
- (ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
- else
- __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
- (~ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
-
- return 0;
-}
-
-static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- struct nuc900_rtc *rtc = dev_get_drvdata(dev);
- unsigned int timeval, clrval;
-
- timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
- clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
-
- nuc900_rtc_bcd2bin(timeval, clrval, tm);
-
- return 0;
-}
-
-static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- struct nuc900_rtc *rtc = dev_get_drvdata(dev);
- struct nuc900_bcd_time gettm;
- unsigned long val;
- int *err;
-
- nuc900_rtc_bin2bcd(dev, tm, &gettm);
-
- err = check_rtc_access_enable(rtc);
- if (IS_ERR(err))
- return PTR_ERR(err);
-
- val = gettm.bcd_mday | gettm.bcd_mon | gettm.bcd_year;
- __raw_writel(val, rtc->rtc_reg + REG_RTC_CLR);
-
- val = gettm.bcd_sec | gettm.bcd_min | gettm.bcd_hour;
- __raw_writel(val, rtc->rtc_reg + REG_RTC_TLR);
-
- return 0;
-}
-
-static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-{
- struct nuc900_rtc *rtc = dev_get_drvdata(dev);
- unsigned int timeval, carval;
-
- timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
- carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
-
- nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
-
- return rtc_valid_tm(&alrm->time);
-}
-
-static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-{
- struct nuc900_rtc *rtc = dev_get_drvdata(dev);
- struct nuc900_bcd_time tm;
- unsigned long val;
- int *err;
-
- nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
-
- err = check_rtc_access_enable(rtc);
- if (IS_ERR(err))
- return PTR_ERR(err);
-
- val = tm.bcd_mday | tm.bcd_mon | tm.bcd_year;
- __raw_writel(val, rtc->rtc_reg + REG_RTC_CAR);
-
- val = tm.bcd_sec | tm.bcd_min | tm.bcd_hour;
- __raw_writel(val, rtc->rtc_reg + REG_RTC_TAR);
-
- return 0;
-}
-
-static const struct rtc_class_ops nuc900_rtc_ops = {
- .read_time = nuc900_rtc_read_time,
- .set_time = nuc900_rtc_set_time,
- .read_alarm = nuc900_rtc_read_alarm,
- .set_alarm = nuc900_rtc_set_alarm,
- .alarm_irq_enable = nuc900_alarm_irq_enable,
-};
-
-static int __init nuc900_rtc_probe(struct platform_device *pdev)
-{
- struct resource *res;
- struct nuc900_rtc *nuc900_rtc;
-
- nuc900_rtc = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_rtc),
- GFP_KERNEL);
- if (!nuc900_rtc)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nuc900_rtc->rtc_reg = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(nuc900_rtc->rtc_reg))
- return PTR_ERR(nuc900_rtc->rtc_reg);
-
- platform_set_drvdata(pdev, nuc900_rtc);
-
- nuc900_rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name,
- &nuc900_rtc_ops, THIS_MODULE);
- if (IS_ERR(nuc900_rtc->rtcdev)) {
- dev_err(&pdev->dev, "rtc device register failed\n");
- return PTR_ERR(nuc900_rtc->rtcdev);
- }
-
- __raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
- nuc900_rtc->rtc_reg + REG_RTC_TSSR);
-
- nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
- if (devm_request_irq(&pdev->dev, nuc900_rtc->irq_num,
- nuc900_rtc_interrupt, 0, "nuc900rtc", nuc900_rtc)) {
- dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
- return -EBUSY;
- }
-
- return 0;
-}
-
-static struct platform_driver nuc900_rtc_driver = {
- .driver = {
- .name = "nuc900-rtc",
- },
-};
-
-module_platform_driver_probe(nuc900_rtc_driver, nuc900_rtc_probe);
-
-MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
-MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:nuc900-rtc");
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 323ff55..a2941c8 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* TI OMAP Real Time Clock interface for Linux
*
@@ -6,11 +7,6 @@
*
* Copyright (C) 2006 David Brownell (new RTC framework)
* Copyright (C) 2014 Johan Hovold <johan@kernel.org>
- *
- * 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.
*/
#include <dt-bindings/gpio/gpio.h>
@@ -271,7 +267,7 @@
}
/* this hardware doesn't support "don't care" alarm fields */
-static int tm2bcd(struct rtc_time *tm)
+static void tm2bcd(struct rtc_time *tm)
{
tm->tm_sec = bin2bcd(tm->tm_sec);
tm->tm_min = bin2bcd(tm->tm_min);
@@ -279,13 +275,7 @@
tm->tm_mday = bin2bcd(tm->tm_mday);
tm->tm_mon = bin2bcd(tm->tm_mon + 1);
-
- /* epoch == 1900 */
- if (tm->tm_year < 100 || tm->tm_year > 199)
- return -EINVAL;
tm->tm_year = bin2bcd(tm->tm_year - 100);
-
- return 0;
}
static void bcd2tm(struct rtc_time *tm)
@@ -328,8 +318,7 @@
{
struct omap_rtc *rtc = dev_get_drvdata(dev);
- if (tm2bcd(tm) < 0)
- return -EINVAL;
+ tm2bcd(tm);
local_irq_disable();
rtc_wait_not_busy(rtc);
@@ -378,8 +367,7 @@
struct omap_rtc *rtc = dev_get_drvdata(dev);
u8 reg, irqwake_reg = 0;
- if (tm2bcd(&alm->time) < 0)
- return -EINVAL;
+ tm2bcd(&alm->time);
local_irq_disable();
rtc_wait_not_busy(rtc);
@@ -415,25 +403,17 @@
static struct omap_rtc *omap_rtc_power_off_rtc;
-/*
- * omap_rtc_poweroff: RTC-controlled power off
- *
- * The RTC can be used to control an external PMIC via the pmic_power_en pin,
- * which can be configured to transition to OFF on ALARM2 events.
- *
- * Notes:
- * The two-second alarm offset is the shortest offset possible as the alarm
- * registers must be set before the next timer update and the offset
- * calculation is too heavy for everything to be done within a single access
- * period (~15 us).
- *
- * Called with local interrupts disabled.
+/**
+ * omap_rtc_power_off_program: Set the pmic power off sequence. The RTC
+ * generates pmic_pwr_enable control, which can be used to control an external
+ * PMIC.
*/
-static void omap_rtc_power_off(void)
+int omap_rtc_power_off_program(struct device *dev)
{
struct omap_rtc *rtc = omap_rtc_power_off_rtc;
struct rtc_time tm;
unsigned long now;
+ int seconds;
u32 val;
rtc->type->unlock(rtc);
@@ -441,17 +421,18 @@
val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN);
- /* set alarm two seconds from now */
- omap_rtc_read_time_raw(rtc, &tm);
- bcd2tm(&tm);
- rtc_tm_to_time(&tm, &now);
- rtc_time_to_tm(now + 2, &tm);
+again:
+ /* Clear any existing ALARM2 event */
+ rtc_writel(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM2);
- if (tm2bcd(&tm) < 0) {
- dev_err(&rtc->rtc->dev, "power off failed\n");
- rtc->type->lock(rtc);
- return;
- }
+ /* set alarm one second from now */
+ omap_rtc_read_time_raw(rtc, &tm);
+ seconds = tm.tm_sec;
+ bcd2tm(&tm);
+ now = rtc_tm_to_time64(&tm);
+ rtc_time64_to_tm(now + 1, &tm);
+
+ tm2bcd(&tm);
rtc_wait_not_busy(rtc);
@@ -470,14 +451,55 @@
val = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG,
val | OMAP_RTC_INTERRUPTS_IT_ALARM2);
+
+ /* Retry in case roll over happened before alarm was armed. */
+ if (rtc_read(rtc, OMAP_RTC_SECONDS_REG) != seconds) {
+ val = rtc_read(rtc, OMAP_RTC_STATUS_REG);
+ if (!(val & OMAP_RTC_STATUS_ALARM2))
+ goto again;
+ }
+
rtc->type->lock(rtc);
+ return 0;
+}
+EXPORT_SYMBOL(omap_rtc_power_off_program);
+
+/*
+ * omap_rtc_poweroff: RTC-controlled power off
+ *
+ * The RTC can be used to control an external PMIC via the pmic_power_en pin,
+ * which can be configured to transition to OFF on ALARM2 events.
+ *
+ * Notes:
+ * The one-second alarm offset is the shortest offset possible as the alarm
+ * registers must be set before the next timer update and the offset
+ * calculation is too heavy for everything to be done within a single access
+ * period (~15 us).
+ *
+ * Called with local interrupts disabled.
+ */
+static void omap_rtc_power_off(void)
+{
+ struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc;
+ u32 val;
+
+ omap_rtc_power_off_program(rtc->dev.parent);
+
+ /* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */
+ omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc);
+ val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG);
+ val |= OMAP_RTC_PMIC_POWER_EN_EN | OMAP_RTC_PMIC_EXT_WKUP_POL(0) |
+ OMAP_RTC_PMIC_EXT_WKUP_EN(0);
+ rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG, val);
+ omap_rtc_power_off_rtc->type->lock(omap_rtc_power_off_rtc);
+
/*
- * Wait for alarm to trigger (within two seconds) and external PMIC to
+ * Wait for alarm to trigger (within one second) and external PMIC to
* power off the system. Add a 500 ms margin for external latencies
* (e.g. debounce circuits).
*/
- mdelay(2500);
+ mdelay(1500);
}
static const struct rtc_class_ops omap_rtc_ops = {
@@ -561,9 +583,7 @@
.dt_free_map = pinconf_generic_dt_free_map,
};
-enum rtc_pin_config_param {
- PIN_CONFIG_ACTIVE_HIGH = PIN_CONFIG_END + 1,
-};
+#define PIN_CONFIG_ACTIVE_HIGH (PIN_CONFIG_END + 1)
static const struct pinconf_generic_params rtc_params[] = {
{"ti,active-high", PIN_CONFIG_ACTIVE_HIGH, 0},
@@ -721,8 +741,7 @@
if (of_id) {
rtc->type = of_id->data;
rtc->is_pmic_controller = rtc->type->has_pmic_mode &&
- of_property_read_bool(pdev->dev.of_node,
- "system-power-controller");
+ of_device_is_system_power_controller(pdev->dev.of_node);
} else {
id_entry = platform_get_device_id(pdev);
rtc->type = (void *)id_entry->driver_data;
@@ -843,6 +862,8 @@
}
rtc->rtc->ops = &omap_rtc_ops;
+ rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->rtc->range_max = RTC_TIMESTAMP_END_2099;
omap_rtc_nvmem_config.priv = rtc;
/* handle periodic and alarm irqs */
diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c
index 60f2250..7b9f8bc 100644
--- a/drivers/rtc/rtc-opal.c
+++ b/drivers/rtc/rtc-opal.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* IBM OPAL RTC driver
* Copyright (C) 2014 IBM
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -224,7 +212,7 @@
return rc;
}
-int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled)
+static int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct rtc_wkalrm alarm = { .enabled = 0 };
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c
index f176cb9..178bfb1 100644
--- a/drivers/rtc/rtc-pcap.c
+++ b/drivers/rtc/rtc-pcap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* pcap rtc code for Motorola EZX phones
*
@@ -5,11 +6,6 @@
* Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com>
*
* Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/kernel.h>
@@ -55,7 +51,7 @@
ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, &days);
secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY;
- rtc_time_to_tm(secs, tm);
+ rtc_time64_to_tm(secs, tm);
return 0;
}
@@ -63,12 +59,9 @@
static int pcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev);
- struct rtc_time *tm = &alrm->time;
- unsigned long secs;
+ unsigned long secs = rtc_tm_to_time64(&alrm->time);
u32 tod, days;
- rtc_tm_to_time(tm, &secs);
-
tod = secs % SEC_PER_DAY;
ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TODA, tod);
@@ -90,14 +83,15 @@
ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAY, &days);
secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY;
- rtc_time_to_tm(secs, tm);
+ rtc_time64_to_tm(secs, tm);
return 0;
}
-static int pcap_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int pcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev);
+ unsigned long secs = rtc_tm_to_time64(tm);
u32 tod, days;
tod = secs % SEC_PER_DAY;
@@ -128,9 +122,9 @@
static const struct rtc_class_ops pcap_rtc_ops = {
.read_time = pcap_rtc_read_time,
+ .set_time = pcap_rtc_set_time,
.read_alarm = pcap_rtc_read_alarm,
.set_alarm = pcap_rtc_set_alarm,
- .set_mmss = pcap_rtc_set_mmss,
.alarm_irq_enable = pcap_rtc_alarm_irq_enable,
};
@@ -149,11 +143,13 @@
platform_set_drvdata(pdev, pcap_rtc);
- pcap_rtc->rtc = devm_rtc_device_register(&pdev->dev, "pcap",
- &pcap_rtc_ops, THIS_MODULE);
+ pcap_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(pcap_rtc->rtc))
return PTR_ERR(pcap_rtc->rtc);
+ pcap_rtc->rtc->ops = &pcap_rtc_ops;
+ pcap_rtc->rtc->range_max = (1 << 14) * 86400ULL - 1;
+
timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ);
alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA);
@@ -167,7 +163,7 @@
if (err)
return err;
- return 0;
+ return rtc_register_device(pcap_rtc->rtc);
}
static int __exit pcap_rtc_remove(struct platform_device *pdev)
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index e5222c5..c3691fa 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An SPI driver for the Philips PCF2123 RTC
* Copyright 2009 Cyber Switching, Inc.
@@ -10,10 +11,6 @@
* Thanks to Christian Pellegrin <chripell@fsfe.org> for
* the sysfs contributions to this driver.
*
- * 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.
- *
* Please note that the CS is active high, so platform data
* should look something like:
*
@@ -29,7 +26,6 @@
* },
* ...
*};
- *
*/
#include <linux/bcd.h>
@@ -44,7 +40,7 @@
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
-#include <linux/sysfs.h>
+#include <linux/regmap.h>
/* REGISTERS */
#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */
@@ -86,7 +82,7 @@
#define OSC_HAS_STOPPED BIT(7) /* Clock has been stopped */
/* PCF2123_REG_ALRM_XX BITS */
-#define ALRM_ENABLE BIT(7) /* MN, HR, DM, or DW alarm enable */
+#define ALRM_DISABLE BIT(7) /* MN, HR, DM, or DW alarm matching */
/* PCF2123_REG_TMR_CLKOUT BITS */
#define CD_TMR_4096KHZ (0) /* 4096 KHz countdown timer */
@@ -99,6 +95,7 @@
#define OFFSET_SIGN_BIT 6 /* 2's complement sign bit */
#define OFFSET_COARSE BIT(7) /* Coarse mode offset */
#define OFFSET_STEP (2170) /* Offset step in parts per billion */
+#define OFFSET_MASK GENMASK(6, 0) /* Offset value */
/* READ/WRITE ADDRESS BITS */
#define PCF2123_WRITE BIT(4)
@@ -107,120 +104,35 @@
static struct spi_driver pcf2123_driver;
-struct pcf2123_sysfs_reg {
- struct device_attribute attr;
- char name[2];
-};
-
-struct pcf2123_plat_data {
+struct pcf2123_data {
struct rtc_device *rtc;
- struct pcf2123_sysfs_reg regs[16];
+ struct regmap *map;
};
-/*
- * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select
- * is released properly after an SPI write. This function should be
- * called after EVERY read/write call over SPI.
- */
-static inline void pcf2123_delay_trec(void)
-{
- ndelay(30);
-}
-
-static int pcf2123_read(struct device *dev, u8 reg, u8 *rxbuf, size_t size)
-{
- struct spi_device *spi = to_spi_device(dev);
- int ret;
-
- reg |= PCF2123_READ;
- ret = spi_write_then_read(spi, ®, 1, rxbuf, size);
- pcf2123_delay_trec();
-
- return ret;
-}
-
-static int pcf2123_write(struct device *dev, u8 *txbuf, size_t size)
-{
- struct spi_device *spi = to_spi_device(dev);
- int ret;
-
- txbuf[0] |= PCF2123_WRITE;
- ret = spi_write(spi, txbuf, size);
- pcf2123_delay_trec();
-
- return ret;
-}
-
-static int pcf2123_write_reg(struct device *dev, u8 reg, u8 val)
-{
- u8 txbuf[2];
-
- txbuf[0] = reg;
- txbuf[1] = val;
- return pcf2123_write(dev, txbuf, sizeof(txbuf));
-}
-
-static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
- char *buffer)
-{
- struct pcf2123_sysfs_reg *r;
- u8 rxbuf[1];
- unsigned long reg;
- int ret;
-
- r = container_of(attr, struct pcf2123_sysfs_reg, attr);
-
- ret = kstrtoul(r->name, 16, ®);
- if (ret)
- return ret;
-
- ret = pcf2123_read(dev, reg, rxbuf, 1);
- if (ret < 0)
- return -EIO;
-
- return sprintf(buffer, "0x%x\n", rxbuf[0]);
-}
-
-static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
- const char *buffer, size_t count)
-{
- struct pcf2123_sysfs_reg *r;
- unsigned long reg;
- unsigned long val;
-
- int ret;
-
- r = container_of(attr, struct pcf2123_sysfs_reg, attr);
-
- ret = kstrtoul(r->name, 16, ®);
- if (ret)
- return ret;
-
- ret = kstrtoul(buffer, 10, &val);
- if (ret)
- return ret;
-
- ret = pcf2123_write_reg(dev, reg, val);
- if (ret < 0)
- return -EIO;
- return count;
-}
+static const struct regmap_config pcf2123_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = PCF2123_READ,
+ .write_flag_mask = PCF2123_WRITE,
+ .max_register = PCF2123_REG_CTDWN_TMR,
+};
static int pcf2123_read_offset(struct device *dev, long *offset)
{
- int ret;
- s8 reg;
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
+ int ret, val;
+ unsigned int reg;
- ret = pcf2123_read(dev, PCF2123_REG_OFFSET, ®, 1);
- if (ret < 0)
+ ret = regmap_read(pcf2123->map, PCF2123_REG_OFFSET, ®);
+ if (ret)
return ret;
- if (reg & OFFSET_COARSE)
- reg <<= 1; /* multiply by 2 and sign extend */
- else
- reg = sign_extend32(reg, OFFSET_SIGN_BIT);
+ val = sign_extend32((reg & OFFSET_MASK), OFFSET_SIGN_BIT);
- *offset = ((long)reg) * OFFSET_STEP;
+ if (reg & OFFSET_COARSE)
+ val *= 2;
+
+ *offset = ((long)val) * OFFSET_STEP;
return 0;
}
@@ -237,6 +149,7 @@
*/
static int pcf2123_set_offset(struct device *dev, long offset)
{
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
s8 reg;
if (offset > OFFSET_STEP * 127)
@@ -244,7 +157,7 @@
else if (offset < OFFSET_STEP * -128)
reg = -128;
else
- reg = (s8)((offset + (OFFSET_STEP >> 1)) / OFFSET_STEP);
+ reg = DIV_ROUND_CLOSEST(offset, OFFSET_STEP);
/* choose fine offset only for odd values in the normal range */
if (reg & 1 && reg <= 63 && reg >= -64) {
@@ -256,16 +169,18 @@
reg |= OFFSET_COARSE;
}
- return pcf2123_write_reg(dev, PCF2123_REG_OFFSET, reg);
+ return regmap_write(pcf2123->map, PCF2123_REG_OFFSET, (unsigned int)reg);
}
static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
u8 rxbuf[7];
int ret;
- ret = pcf2123_read(dev, PCF2123_REG_SC, rxbuf, sizeof(rxbuf));
- if (ret < 0)
+ ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_SC, rxbuf,
+ sizeof(rxbuf));
+ if (ret)
return ret;
if (rxbuf[0] & OSC_HAS_STOPPED) {
@@ -279,86 +194,170 @@
tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F);
tm->tm_wday = rxbuf[4] & 0x07;
tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */
- tm->tm_year = bcd2bin(rxbuf[6]);
- if (tm->tm_year < 70)
- tm->tm_year += 100; /* assume we are in 1970...2069 */
+ tm->tm_year = bcd2bin(rxbuf[6]) + 100;
- dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
- "mday=%d, mon=%d, year=%d, wday=%d\n",
- __func__,
- tm->tm_sec, tm->tm_min, tm->tm_hour,
- tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+ dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm);
return 0;
}
static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- u8 txbuf[8];
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
+ u8 txbuf[7];
int ret;
- dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
- "mday=%d, mon=%d, year=%d, wday=%d\n",
- __func__,
- tm->tm_sec, tm->tm_min, tm->tm_hour,
- tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+ dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm);
/* Stop the counter first */
- ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_STOP);
- if (ret < 0)
+ ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP);
+ if (ret)
return ret;
/* Set the new time */
- txbuf[0] = PCF2123_REG_SC;
- txbuf[1] = bin2bcd(tm->tm_sec & 0x7F);
- txbuf[2] = bin2bcd(tm->tm_min & 0x7F);
- txbuf[3] = bin2bcd(tm->tm_hour & 0x3F);
- txbuf[4] = bin2bcd(tm->tm_mday & 0x3F);
- txbuf[5] = tm->tm_wday & 0x07;
- txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */
- txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100);
+ txbuf[0] = bin2bcd(tm->tm_sec & 0x7F);
+ txbuf[1] = bin2bcd(tm->tm_min & 0x7F);
+ txbuf[2] = bin2bcd(tm->tm_hour & 0x3F);
+ txbuf[3] = bin2bcd(tm->tm_mday & 0x3F);
+ txbuf[4] = tm->tm_wday & 0x07;
+ txbuf[5] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */
+ txbuf[6] = bin2bcd(tm->tm_year - 100);
- ret = pcf2123_write(dev, txbuf, sizeof(txbuf));
- if (ret < 0)
+ ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_SC, txbuf,
+ sizeof(txbuf));
+ if (ret)
return ret;
/* Start the counter */
- ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_CLEAR);
- if (ret < 0)
+ ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR);
+ if (ret)
return ret;
return 0;
}
+static int pcf2123_rtc_alarm_irq_enable(struct device *dev, unsigned int en)
+{
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
+
+ return regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE,
+ en ? CTRL2_AIE : 0);
+}
+
+static int pcf2123_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
+ u8 rxbuf[4];
+ int ret;
+ unsigned int val = 0;
+
+ ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_ALRM_MN, rxbuf,
+ sizeof(rxbuf));
+ if (ret)
+ return ret;
+
+ alm->time.tm_min = bcd2bin(rxbuf[0] & 0x7F);
+ alm->time.tm_hour = bcd2bin(rxbuf[1] & 0x3F);
+ alm->time.tm_mday = bcd2bin(rxbuf[2] & 0x3F);
+ alm->time.tm_wday = bcd2bin(rxbuf[3] & 0x07);
+
+ dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time);
+
+ ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val);
+ if (ret)
+ return ret;
+
+ alm->enabled = !!(val & CTRL2_AIE);
+
+ return 0;
+}
+
+static int pcf2123_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
+ u8 txbuf[4];
+ int ret;
+
+ dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time);
+
+ /* Disable alarm interrupt */
+ ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, 0);
+ if (ret)
+ return ret;
+
+ /* Ensure alarm flag is clear */
+ ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0);
+ if (ret)
+ return ret;
+
+ /* Set new alarm */
+ txbuf[0] = bin2bcd(alm->time.tm_min & 0x7F);
+ txbuf[1] = bin2bcd(alm->time.tm_hour & 0x3F);
+ txbuf[2] = bin2bcd(alm->time.tm_mday & 0x3F);
+ txbuf[3] = ALRM_DISABLE;
+
+ ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_ALRM_MN, txbuf,
+ sizeof(txbuf));
+ if (ret)
+ return ret;
+
+ return pcf2123_rtc_alarm_irq_enable(dev, alm->enabled);
+}
+
+static irqreturn_t pcf2123_rtc_irq(int irq, void *dev)
+{
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
+ struct mutex *lock = &pcf2123->rtc->ops_lock;
+ unsigned int val = 0;
+ int ret = IRQ_NONE;
+
+ mutex_lock(lock);
+ regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val);
+
+ /* Alarm? */
+ if (val & CTRL2_AF) {
+ ret = IRQ_HANDLED;
+
+ /* Clear alarm flag */
+ regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0);
+
+ rtc_update_irq(pcf2123->rtc, 1, RTC_IRQF | RTC_AF);
+ }
+
+ mutex_unlock(lock);
+
+ return ret;
+}
+
static int pcf2123_reset(struct device *dev)
{
+ struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
int ret;
- u8 rxbuf[2];
+ unsigned int val = 0;
- ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_SW_RESET);
- if (ret < 0)
+ ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_SW_RESET);
+ if (ret)
return ret;
/* Stop the counter */
dev_dbg(dev, "stopping RTC\n");
- ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_STOP);
- if (ret < 0)
+ ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP);
+ if (ret)
return ret;
/* See if the counter was actually stopped */
dev_dbg(dev, "checking for presence of RTC\n");
- ret = pcf2123_read(dev, PCF2123_REG_CTRL1, rxbuf, sizeof(rxbuf));
- if (ret < 0)
+ ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL1, &val);
+ if (ret)
return ret;
- dev_dbg(dev, "received data from RTC (0x%02X 0x%02X)\n",
- rxbuf[0], rxbuf[1]);
- if (!(rxbuf[0] & CTRL1_STOP))
+ dev_dbg(dev, "received data from RTC (0x%08X)\n", val);
+ if (!(val & CTRL1_STOP))
return -ENODEV;
/* Start the counter */
- ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_CLEAR);
- if (ret < 0)
+ ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR);
+ if (ret)
return ret;
return 0;
@@ -369,28 +368,37 @@
.set_time = pcf2123_rtc_set_time,
.read_offset = pcf2123_read_offset,
.set_offset = pcf2123_set_offset,
-
+ .read_alarm = pcf2123_rtc_read_alarm,
+ .set_alarm = pcf2123_rtc_set_alarm,
+ .alarm_irq_enable = pcf2123_rtc_alarm_irq_enable,
};
static int pcf2123_probe(struct spi_device *spi)
{
struct rtc_device *rtc;
struct rtc_time tm;
- struct pcf2123_plat_data *pdata;
- int ret, i;
+ struct pcf2123_data *pcf2123;
+ int ret = 0;
- pdata = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_plat_data),
+ pcf2123 = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_data),
GFP_KERNEL);
- if (!pdata)
+ if (!pcf2123)
return -ENOMEM;
- spi->dev.platform_data = pdata;
+
+ dev_set_drvdata(&spi->dev, pcf2123);
+
+ pcf2123->map = devm_regmap_init_spi(spi, &pcf2123_regmap_config);
+ if (IS_ERR(pcf2123->map)) {
+ dev_err(&spi->dev, "regmap init failed.\n");
+ return PTR_ERR(pcf2123->map);
+ }
ret = pcf2123_rtc_read_time(&spi->dev, &tm);
if (ret < 0) {
ret = pcf2123_reset(&spi->dev);
if (ret < 0) {
dev_err(&spi->dev, "chip not found\n");
- goto kfree_exit;
+ return ret;
}
}
@@ -398,60 +406,46 @@
(spi->max_speed_hz + 500) / 1000);
/* Finalize the initialization */
- rtc = devm_rtc_device_register(&spi->dev, pcf2123_driver.driver.name,
- &pcf2123_rtc_ops, THIS_MODULE);
+ rtc = devm_rtc_allocate_device(&spi->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
- if (IS_ERR(rtc)) {
- dev_err(&spi->dev, "failed to register.\n");
- ret = PTR_ERR(rtc);
- goto kfree_exit;
+ pcf2123->rtc = rtc;
+
+ /* Register alarm irq */
+ if (spi->irq > 0) {
+ ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
+ pcf2123_rtc_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ pcf2123_driver.driver.name, &spi->dev);
+ if (!ret)
+ device_init_wakeup(&spi->dev, true);
+ else
+ dev_err(&spi->dev, "could not request irq.\n");
}
- pdata->rtc = rtc;
+ /* The PCF2123's alarm only has minute accuracy. Must add timer
+ * support to this driver to generate interrupts more than once
+ * per minute.
+ */
+ rtc->uie_unsupported = 1;
+ rtc->ops = &pcf2123_rtc_ops;
+ rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->range_max = RTC_TIMESTAMP_END_2099;
+ rtc->set_start_time = true;
- for (i = 0; i < 16; i++) {
- sysfs_attr_init(&pdata->regs[i].attr.attr);
- sprintf(pdata->regs[i].name, "%1x", i);
- pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
- pdata->regs[i].attr.attr.name = pdata->regs[i].name;
- pdata->regs[i].attr.show = pcf2123_show;
- pdata->regs[i].attr.store = pcf2123_store;
- ret = device_create_file(&spi->dev, &pdata->regs[i].attr);
- if (ret) {
- dev_err(&spi->dev, "Unable to create sysfs %s\n",
- pdata->regs[i].name);
- goto sysfs_exit;
- }
- }
-
- return 0;
-
-sysfs_exit:
- for (i--; i >= 0; i--)
- device_remove_file(&spi->dev, &pdata->regs[i].attr);
-
-kfree_exit:
- spi->dev.platform_data = NULL;
- return ret;
-}
-
-static int pcf2123_remove(struct spi_device *spi)
-{
- struct pcf2123_plat_data *pdata = dev_get_platdata(&spi->dev);
- int i;
-
- if (pdata) {
- for (i = 0; i < 16; i++)
- if (pdata->regs[i].name[0])
- device_remove_file(&spi->dev,
- &pdata->regs[i].attr);
- }
+ ret = rtc_register_device(rtc);
+ if (ret)
+ return ret;
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id pcf2123_dt_ids[] = {
+ { .compatible = "nxp,pcf2123", },
+ { .compatible = "microcrystal,rv2123", },
+ /* Deprecated, do not use */
{ .compatible = "nxp,rtc-pcf2123", },
{ /* sentinel */ }
};
@@ -464,7 +458,6 @@
.of_match_table = of_match_ptr(pcf2123_dt_ids),
},
.probe = pcf2123_probe,
- .remove = pcf2123_remove,
};
module_spi_driver(pcf2123_driver);
diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 7cb786d..02b069c 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -1,16 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An I2C and SPI driver for the NXP PCF2127/29 RTC
* Copyright 2013 Til-Technologies
*
* Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
*
+ * Watchdog and tamper functions
+ * Author: Bruno Thomsen <bruno.thomsen@gmail.com>
+ *
* based on the other drivers in this same directory.
*
* Datasheet: http://cache.nxp.com/documents/data_sheet/PCF2127.pdf
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/i2c.h>
@@ -21,30 +21,67 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
+#include <linux/watchdog.h>
-#define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */
-#define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */
+/* Control register 1 */
+#define PCF2127_REG_CTRL1 0x00
+#define PCF2127_BIT_CTRL1_TSF1 BIT(4)
+/* Control register 2 */
+#define PCF2127_REG_CTRL2 0x01
+#define PCF2127_BIT_CTRL2_TSIE BIT(2)
+#define PCF2127_BIT_CTRL2_TSF2 BIT(5)
+/* Control register 3 */
+#define PCF2127_REG_CTRL3 0x02
+#define PCF2127_BIT_CTRL3_BLIE BIT(0)
+#define PCF2127_BIT_CTRL3_BIE BIT(1)
+#define PCF2127_BIT_CTRL3_BLF BIT(2)
+#define PCF2127_BIT_CTRL3_BF BIT(3)
+#define PCF2127_BIT_CTRL3_BTSE BIT(4)
+/* Time and date registers */
+#define PCF2127_REG_SC 0x03
+#define PCF2127_BIT_SC_OSF BIT(7)
+#define PCF2127_REG_MN 0x04
+#define PCF2127_REG_HR 0x05
+#define PCF2127_REG_DM 0x06
+#define PCF2127_REG_DW 0x07
+#define PCF2127_REG_MO 0x08
+#define PCF2127_REG_YR 0x09
+/* Watchdog registers */
+#define PCF2127_REG_WD_CTL 0x10
+#define PCF2127_BIT_WD_CTL_TF0 BIT(0)
+#define PCF2127_BIT_WD_CTL_TF1 BIT(1)
+#define PCF2127_BIT_WD_CTL_CD0 BIT(6)
+#define PCF2127_BIT_WD_CTL_CD1 BIT(7)
+#define PCF2127_REG_WD_VAL 0x11
+/* Tamper timestamp registers */
+#define PCF2127_REG_TS_CTRL 0x12
+#define PCF2127_BIT_TS_CTRL_TSOFF BIT(6)
+#define PCF2127_BIT_TS_CTRL_TSM BIT(7)
+#define PCF2127_REG_TS_SC 0x13
+#define PCF2127_REG_TS_MN 0x14
+#define PCF2127_REG_TS_HR 0x15
+#define PCF2127_REG_TS_DM 0x16
+#define PCF2127_REG_TS_MO 0x17
+#define PCF2127_REG_TS_YR 0x18
+/*
+ * RAM registers
+ * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
+ * battery backed and can survive a power outage.
+ * PCF2129 doesn't have this feature.
+ */
+#define PCF2127_REG_RAM_ADDR_MSB 0x1A
+#define PCF2127_REG_RAM_WRT_CMD 0x1C
+#define PCF2127_REG_RAM_RD_CMD 0x1D
-#define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */
-#define PCF2127_REG_CTRL3_BLF BIT(2)
-
-#define PCF2127_REG_SC (0x03) /* datetime */
-#define PCF2127_REG_MN (0x04)
-#define PCF2127_REG_HR (0x05)
-#define PCF2127_REG_DM (0x06)
-#define PCF2127_REG_DW (0x07)
-#define PCF2127_REG_MO (0x08)
-#define PCF2127_REG_YR (0x09)
-
-/* the pcf2127 has 512 bytes nvmem, pcf2129 doesn't */
-#define PCF2127_REG_RAM_addr_MSB 0x1a
-#define PCF2127_REG_RAM_wrt_cmd 0x1c
-#define PCF2127_REG_RAM_rd_cmd 0x1d
-
-#define PCF2127_OSF BIT(7) /* Oscillator Fail flag */
+/* Watchdog timer value constants */
+#define PCF2127_WD_VAL_STOP 0
+#define PCF2127_WD_VAL_MIN 2
+#define PCF2127_WD_VAL_MAX 255
+#define PCF2127_WD_VAL_DEFAULT 60
struct pcf2127 {
struct rtc_device *rtc;
+ struct watchdog_device wdd;
struct regmap *regmap;
};
@@ -57,30 +94,25 @@
struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
unsigned char buf[10];
int ret;
- int i;
- for (i = 0; i <= PCF2127_REG_CTRL3; i++) {
- ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1 + i,
- (unsigned int *)(buf + i));
- if (ret) {
- dev_err(dev, "%s: read error\n", __func__);
- return ret;
- }
- }
-
- ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_SC,
- (buf + PCF2127_REG_SC),
- ARRAY_SIZE(buf) - PCF2127_REG_SC);
+ /*
+ * Avoid reading CTRL2 register as it causes WD_VAL register
+ * value to reset to 0 which means watchdog is stopped.
+ */
+ ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
+ (buf + PCF2127_REG_CTRL3),
+ ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
if (ret) {
dev_err(dev, "%s: read error\n", __func__);
return ret;
}
- if (buf[PCF2127_REG_CTRL3] & PCF2127_REG_CTRL3_BLF)
+ if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
dev_info(dev,
"low voltage detected, check/replace RTC battery.\n");
- if (buf[PCF2127_REG_SC] & PCF2127_OSF) {
+ /* Clock integrity is not guaranteed when OSF flag is set. */
+ if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
/*
* no need clear the flag here,
* it will be cleared once the new date is saved
@@ -91,14 +123,12 @@
}
dev_dbg(dev,
- "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, "
- "sec=%02x, min=%02x, hr=%02x, "
+ "%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
- __func__,
- buf[0], buf[1], buf[2],
- buf[3], buf[4], buf[5],
- buf[6], buf[7], buf[8], buf[9]);
-
+ __func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
+ buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
+ buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
+ buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
@@ -169,7 +199,7 @@
if (ret)
return ret;
- touser = touser & PCF2127_REG_CTRL3_BLF ? 1 : 0;
+ touser = touser & PCF2127_BIT_CTRL3_BLF ? 1 : 0;
if (copy_to_user((void __user *)arg, &touser, sizeof(int)))
return -EFAULT;
@@ -195,12 +225,12 @@
int ret;
unsigned char offsetbuf[] = { offset >> 8, offset };
- ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_addr_MSB,
+ ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_ADDR_MSB,
offsetbuf, 2);
if (ret)
return ret;
- ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_RAM_rd_cmd,
+ ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_RAM_RD_CMD,
val, bytes);
return ret ?: bytes;
@@ -213,17 +243,176 @@
int ret;
unsigned char offsetbuf[] = { offset >> 8, offset };
- ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_addr_MSB,
+ ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_ADDR_MSB,
offsetbuf, 2);
if (ret)
return ret;
- ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_wrt_cmd,
+ ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_WRT_CMD,
val, bytes);
return ret ?: bytes;
}
+/* watchdog driver */
+
+static int pcf2127_wdt_ping(struct watchdog_device *wdd)
+{
+ struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
+
+ return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL, wdd->timeout);
+}
+
+/*
+ * Restart watchdog timer if feature is active.
+ *
+ * Note: Reading CTRL2 register causes watchdog to stop which is unfortunate,
+ * since register also contain control/status flags for other features.
+ * Always call this function after reading CTRL2 register.
+ */
+static int pcf2127_wdt_active_ping(struct watchdog_device *wdd)
+{
+ int ret = 0;
+
+ if (watchdog_active(wdd)) {
+ ret = pcf2127_wdt_ping(wdd);
+ if (ret)
+ dev_err(wdd->parent,
+ "%s: watchdog restart failed, ret=%d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int pcf2127_wdt_start(struct watchdog_device *wdd)
+{
+ return pcf2127_wdt_ping(wdd);
+}
+
+static int pcf2127_wdt_stop(struct watchdog_device *wdd)
+{
+ struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
+
+ return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL,
+ PCF2127_WD_VAL_STOP);
+}
+
+static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_timeout)
+{
+ dev_dbg(wdd->parent, "new watchdog timeout: %is (old: %is)\n",
+ new_timeout, wdd->timeout);
+
+ wdd->timeout = new_timeout;
+
+ return pcf2127_wdt_active_ping(wdd);
+}
+
+static const struct watchdog_info pcf2127_wdt_info = {
+ .identity = "NXP PCF2127/PCF2129 Watchdog",
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+};
+
+static const struct watchdog_ops pcf2127_watchdog_ops = {
+ .owner = THIS_MODULE,
+ .start = pcf2127_wdt_start,
+ .stop = pcf2127_wdt_stop,
+ .ping = pcf2127_wdt_ping,
+ .set_timeout = pcf2127_wdt_set_timeout,
+};
+
+/* sysfs interface */
+
+static ssize_t timestamp0_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
+ int ret;
+
+ ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
+ PCF2127_BIT_CTRL1_TSF1, 0);
+ if (ret) {
+ dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
+ PCF2127_BIT_CTRL2_TSF2, 0);
+ if (ret) {
+ dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
+ if (ret)
+ return ret;
+
+ return count;
+};
+
+static ssize_t timestamp0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
+ struct rtc_time tm;
+ int ret;
+ unsigned char data[25];
+
+ ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data,
+ sizeof(data));
+ if (ret) {
+ dev_err(dev, "%s: read error ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ dev_dbg(dev,
+ "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, "
+ "ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
+ __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2],
+ data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC],
+ data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR],
+ data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO],
+ data[PCF2127_REG_TS_YR]);
+
+ ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
+ if (ret)
+ return ret;
+
+ if (!(data[PCF2127_REG_CTRL1] & PCF2127_BIT_CTRL1_TSF1) &&
+ !(data[PCF2127_REG_CTRL2] & PCF2127_BIT_CTRL2_TSF2))
+ return 0;
+
+ tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F);
+ tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F);
+ tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F);
+ tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F);
+ /* TS_MO register (month) value range: 1-12 */
+ tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1;
+ tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]);
+ if (tm.tm_year < 70)
+ tm.tm_year += 100; /* assume we are in 1970...2069 */
+
+ 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 struct attribute *pcf2127_attrs[] = {
+ &dev_attr_timestamp0.attr,
+ NULL
+};
+
+static const struct attribute_group pcf2127_attr_group = {
+ .attrs = pcf2127_attrs,
+};
+
static int pcf2127_probe(struct device *dev, struct regmap *regmap,
const char *name, bool has_nvmem)
{
@@ -240,11 +429,22 @@
dev_set_drvdata(dev, pcf2127);
- pcf2127->rtc = devm_rtc_device_register(dev, name, &pcf2127_rtc_ops,
- THIS_MODULE);
+ pcf2127->rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(pcf2127->rtc))
return PTR_ERR(pcf2127->rtc);
+ pcf2127->rtc->ops = &pcf2127_rtc_ops;
+
+ pcf2127->wdd.parent = dev;
+ pcf2127->wdd.info = &pcf2127_wdt_info;
+ pcf2127->wdd.ops = &pcf2127_watchdog_ops;
+ pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN;
+ pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX;
+ pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
+ pcf2127->wdd.min_hw_heartbeat_ms = 500;
+
+ watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
+
if (has_nvmem) {
struct nvmem_config nvmem_cfg = {
.priv = pcf2127,
@@ -256,7 +456,84 @@
ret = rtc_nvmem_register(pcf2127->rtc, &nvmem_cfg);
}
- return ret;
+ /*
+ * Watchdog timer enabled and reset pin /RST activated when timed out.
+ * Select 1Hz clock source for watchdog timer.
+ * Timer is not started until WD_VAL is loaded with a valid value.
+ * Note: Countdown timer disabled and not available.
+ */
+ ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_WD_CTL,
+ PCF2127_BIT_WD_CTL_CD1 |
+ PCF2127_BIT_WD_CTL_CD0 |
+ PCF2127_BIT_WD_CTL_TF1 |
+ PCF2127_BIT_WD_CTL_TF0,
+ PCF2127_BIT_WD_CTL_CD1 |
+ PCF2127_BIT_WD_CTL_CD0 |
+ PCF2127_BIT_WD_CTL_TF1);
+ if (ret) {
+ dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
+ return ret;
+ }
+
+#ifdef CONFIG_WATCHDOG
+ ret = devm_watchdog_register_device(dev, &pcf2127->wdd);
+ if (ret)
+ return ret;
+#endif /* CONFIG_WATCHDOG */
+
+ /*
+ * Disable battery low/switch-over timestamp and interrupts.
+ * Clear battery interrupt flags which can block new trigger events.
+ * Note: This is the default chip behaviour but added to ensure
+ * correct tamper timestamp and interrupt function.
+ */
+ ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL3,
+ PCF2127_BIT_CTRL3_BTSE |
+ PCF2127_BIT_CTRL3_BF |
+ PCF2127_BIT_CTRL3_BIE |
+ PCF2127_BIT_CTRL3_BLIE, 0);
+ if (ret) {
+ dev_err(dev, "%s: interrupt config (ctrl3) failed\n",
+ __func__);
+ return ret;
+ }
+
+ /*
+ * Enable timestamp function and store timestamp of first trigger
+ * event until TSF1 and TFS2 interrupt flags are cleared.
+ */
+ ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL,
+ PCF2127_BIT_TS_CTRL_TSOFF |
+ PCF2127_BIT_TS_CTRL_TSM,
+ PCF2127_BIT_TS_CTRL_TSM);
+ if (ret) {
+ dev_err(dev, "%s: tamper detection config (ts_ctrl) failed\n",
+ __func__);
+ return ret;
+ }
+
+ /*
+ * Enable interrupt generation when TSF1 or TSF2 timestamp flags
+ * are set. Interrupt signal is an open-drain output and can be
+ * left floating if unused.
+ */
+ ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
+ PCF2127_BIT_CTRL2_TSIE,
+ PCF2127_BIT_CTRL2_TSIE);
+ if (ret) {
+ dev_err(dev, "%s: tamper detection config (ctrl2) failed\n",
+ __func__);
+ return ret;
+ }
+
+ ret = rtc_add_group(pcf2127->rtc, &pcf2127_attr_group);
+ if (ret) {
+ dev_err(dev, "%s: tamper sysfs registering failed\n",
+ __func__);
+ return ret;
+ }
+
+ return rtc_register_device(pcf2127->rtc);
}
#ifdef CONFIG_OF
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
index ef72b0c..48951a1 100644
--- a/drivers/rtc/rtc-pcf50633.c
+++ b/drivers/rtc/rtc-pcf50633.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* NXP PCF50633 RTC Driver
*
* (C) 2006-2008 by Openmoko, Inc.
@@ -6,12 +7,6 @@
*
* Broken down from monstrous PCF50633 driver mainly by
* Harald Welte, Andy Green and Werner Almesberger
- *
- * 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.
- *
*/
#include <linux/kernel.h>
@@ -131,9 +126,7 @@
pcf2rtc_time(tm, &pcf_tm);
- dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
- tm->tm_mday, tm->tm_mon, tm->tm_year,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);
return 0;
}
@@ -146,9 +139,7 @@
rtc = dev_get_drvdata(dev);
- dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
- tm->tm_mday, tm->tm_mon, tm->tm_year,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);
rtc2pcf_time(&pcf_tm, tm);
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 283c233..1afa6d9 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* An I2C driver for the PCF85063 RTC
* Copyright 2014 Rose Technology
@@ -5,16 +6,16 @@
* Author: Søren Andersen <san@rosetechnology.dk>
* Maintainers: http://www.nslu2-linux.org/
*
- * based on the other drivers in this same directory.
- *
- * 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.
+ * Copyright (C) 2019 Micro Crystal AG
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
*/
#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
/*
* Information for this driver was pulled from the following datasheets.
@@ -24,67 +25,47 @@
*
* PCF85063A -- Rev. 6 — 18 November 2015
* PCF85063TP -- Rev. 4 — 6 May 2015
-*/
+ *
+ * https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8263-C7_App-Manual.pdf
+ * RV8263 -- Rev. 1.0 — January 2019
+ */
#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_CTRL2_AF BIT(6)
+#define PCF85063_CTRL2_AIE BIT(7)
+
+#define PCF85063_REG_OFFSET 0x02
+#define PCF85063_OFFSET_SIGN_BIT 6 /* 2's complement sign bit */
+#define PCF85063_OFFSET_MODE BIT(7)
+#define PCF85063_OFFSET_STEP0 4340
+#define PCF85063_OFFSET_STEP1 4069
+
+#define PCF85063_REG_RAM 0x03
#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;
+#define PCF85063_REG_ALM_S 0x0b
+#define PCF85063_AEN BIT(7)
-static int pcf85063_stop_clock(struct i2c_client *client, u8 *ctrl1)
-{
- int rc;
- u8 reg;
+struct pcf85063_config {
+ struct regmap_config regmap;
+ unsigned has_alarms:1;
+ unsigned force_cap_7000:1;
+};
- rc = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
- if (rc < 0) {
- dev_err(&client->dev, "Failing to stop the clock\n");
- return -EIO;
- }
-
- /* stop the clock */
- reg = rc | PCF85063_REG_CTRL1_STOP;
-
- rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, reg);
- if (rc < 0) {
- dev_err(&client->dev, "Failing to stop the clock\n");
- return -EIO;
- }
-
- *ctrl1 = reg;
-
- return 0;
-}
-
-static int pcf85063_start_clock(struct i2c_client *client, u8 ctrl1)
-{
- int rc;
-
- /* start the clock */
- ctrl1 &= ~PCF85063_REG_CTRL1_STOP;
-
- rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ctrl1);
- if (rc < 0) {
- dev_err(&client->dev, "Failing to start the clock\n");
- return -EIO;
- }
-
- return 0;
-}
+struct pcf85063 {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+};
static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
int rc;
u8 regs[7];
@@ -94,16 +75,14 @@
* event, the access must be finished within one second. So, read all
* time/date registers in one turn.
*/
- rc = i2c_smbus_read_i2c_block_data(client, PCF85063_REG_SC,
- sizeof(regs), regs);
- if (rc != sizeof(regs)) {
- dev_err(&client->dev, "date/time register read error\n");
- return -EIO;
- }
+ rc = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_SC, regs,
+ sizeof(regs));
+ if (rc)
+ return rc;
/* if the clock has lost its power it makes no sense to use its time */
if (regs[0] & PCF85063_REG_SC_OS) {
- dev_warn(&client->dev, "Power loss detected, invalid time\n");
+ dev_warn(&pcf85063->rtc->dev, "Power loss detected, invalid time\n");
return -EINVAL;
}
@@ -121,20 +100,18 @@
static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
int rc;
u8 regs[7];
- u8 ctrl1;
-
- if ((tm->tm_year < 100) || (tm->tm_year > 199))
- return -EINVAL;
/*
* to accurately set the time, reset the divider chain and keep it in
* reset state until all time/date registers are written
*/
- rc = pcf85063_stop_clock(client, &ctrl1);
- if (rc != 0)
+ rc = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
+ PCF85063_REG_CTRL1_STOP,
+ PCF85063_REG_CTRL1_STOP);
+ if (rc)
return rc;
/* hours, minutes and seconds */
@@ -156,63 +133,351 @@
regs[6] = bin2bcd(tm->tm_year - 100);
/* write all registers at once */
- rc = i2c_smbus_write_i2c_block_data(client, PCF85063_REG_SC,
- sizeof(regs), regs);
- if (rc < 0) {
- dev_err(&client->dev, "date/time register write error\n");
+ rc = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_SC,
+ regs, sizeof(regs));
+ if (rc)
return rc;
- }
/*
* Write the control register as a separate action since the size of
* the register space is different between the PCF85063TP and
* PCF85063A devices. The rollover point can not be used.
*/
- rc = pcf85063_start_clock(client, ctrl1);
- if (rc != 0)
- return rc;
+ return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
+ PCF85063_REG_CTRL1_STOP, 0);
+}
+
+static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
+ u8 buf[4];
+ unsigned int val;
+ int ret;
+
+ ret = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_ALM_S,
+ buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ alrm->time.tm_sec = bcd2bin(buf[0]);
+ alrm->time.tm_min = bcd2bin(buf[1]);
+ alrm->time.tm_hour = bcd2bin(buf[2]);
+ alrm->time.tm_mday = bcd2bin(buf[3]);
+
+ ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
+ if (ret)
+ return ret;
+
+ alrm->enabled = !!(val & PCF85063_CTRL2_AIE);
return 0;
}
+static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
+ u8 buf[5];
+ int ret;
+
+ buf[0] = bin2bcd(alrm->time.tm_sec);
+ buf[1] = bin2bcd(alrm->time.tm_min);
+ buf[2] = bin2bcd(alrm->time.tm_hour);
+ buf[3] = bin2bcd(alrm->time.tm_mday);
+ buf[4] = PCF85063_AEN; /* Do not match on week day */
+
+ ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
+ PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_ALM_S,
+ buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
+ PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
+ alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF);
+}
+
+static int pcf85063_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
+
+ return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
+ PCF85063_CTRL2_AIE,
+ enabled ? PCF85063_CTRL2_AIE : 0);
+}
+
+static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id)
+{
+ struct pcf85063 *pcf85063 = dev_id;
+ unsigned int val;
+ int err;
+
+ err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
+ if (err)
+ return IRQ_NONE;
+
+ if (val & PCF85063_CTRL2_AF) {
+ rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF);
+ regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
+ PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
+ 0);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int pcf85063_read_offset(struct device *dev, long *offset)
+{
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
+ long val;
+ u32 reg;
+ int ret;
+
+ ret = regmap_read(pcf85063->regmap, PCF85063_REG_OFFSET, ®);
+ if (ret < 0)
+ return ret;
+
+ val = sign_extend32(reg & ~PCF85063_OFFSET_MODE,
+ PCF85063_OFFSET_SIGN_BIT);
+
+ if (reg & PCF85063_OFFSET_MODE)
+ *offset = val * PCF85063_OFFSET_STEP1;
+ else
+ *offset = val * PCF85063_OFFSET_STEP0;
+
+ return 0;
+}
+
+static int pcf85063_set_offset(struct device *dev, long offset)
+{
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
+ s8 mode0, mode1, reg;
+ unsigned int error0, error1;
+
+ if (offset > PCF85063_OFFSET_STEP0 * 63)
+ return -ERANGE;
+ if (offset < PCF85063_OFFSET_STEP0 * -64)
+ return -ERANGE;
+
+ mode0 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP0);
+ mode1 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP1);
+
+ error0 = abs(offset - (mode0 * PCF85063_OFFSET_STEP0));
+ error1 = abs(offset - (mode1 * PCF85063_OFFSET_STEP1));
+ if (mode1 > 63 || mode1 < -64 || error0 < error1)
+ reg = mode0 & ~PCF85063_OFFSET_MODE;
+ else
+ reg = mode1 | PCF85063_OFFSET_MODE;
+
+ return regmap_write(pcf85063->regmap, PCF85063_REG_OFFSET, reg);
+}
+
+static int pcf85063_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
+ int status, ret = 0;
+
+ switch (cmd) {
+ case RTC_VL_READ:
+ ret = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status & PCF85063_REG_SC_OS)
+ dev_warn(&pcf85063->rtc->dev, "Voltage low, data loss detected.\n");
+
+ status &= PCF85063_REG_SC_OS;
+
+ if (copy_to_user((void __user *)arg, &status, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+
+ case RTC_VL_CLR:
+ ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_SC,
+ PCF85063_REG_SC_OS, 0);
+
+ return ret;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
static const struct rtc_class_ops pcf85063_rtc_ops = {
.read_time = pcf85063_rtc_read_time,
- .set_time = pcf85063_rtc_set_time
+ .set_time = pcf85063_rtc_set_time,
+ .read_offset = pcf85063_read_offset,
+ .set_offset = pcf85063_set_offset,
+ .ioctl = pcf85063_ioctl,
};
-static int pcf85063_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct rtc_class_ops pcf85063_rtc_ops_alarm = {
+ .read_time = pcf85063_rtc_read_time,
+ .set_time = pcf85063_rtc_set_time,
+ .read_offset = pcf85063_read_offset,
+ .set_offset = pcf85063_set_offset,
+ .read_alarm = pcf85063_rtc_read_alarm,
+ .set_alarm = pcf85063_rtc_set_alarm,
+ .alarm_irq_enable = pcf85063_rtc_alarm_irq_enable,
+ .ioctl = pcf85063_ioctl,
+};
+
+static int pcf85063_nvmem_read(void *priv, unsigned int offset,
+ void *val, size_t bytes)
{
- struct rtc_device *rtc;
+ return regmap_read(priv, PCF85063_REG_RAM, val);
+}
+
+static int pcf85063_nvmem_write(void *priv, unsigned int offset,
+ void *val, size_t bytes)
+{
+ return regmap_write(priv, PCF85063_REG_RAM, *(u8 *)val);
+}
+
+static int pcf85063_load_capacitance(struct pcf85063 *pcf85063,
+ const struct device_node *np,
+ unsigned int force_cap)
+{
+ u32 load = 7000;
+ u8 reg = 0;
+
+ if (force_cap)
+ load = force_cap;
+ else
+ of_property_read_u32(np, "quartz-load-femtofarads", &load);
+
+ switch (load) {
+ default:
+ dev_warn(&pcf85063->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000",
+ load);
+ /* fall through */
+ case 7000:
+ break;
+ case 12500:
+ reg = PCF85063_REG_CTRL1_CAP_SEL;
+ break;
+ }
+
+ return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
+ PCF85063_REG_CTRL1_CAP_SEL, reg);
+}
+
+static const struct pcf85063_config pcf85063a_config = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x11,
+ },
+ .has_alarms = 1,
+};
+
+static const struct pcf85063_config pcf85063tp_config = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x0a,
+ },
+};
+
+static const struct pcf85063_config rv8263_config = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x11,
+ },
+ .has_alarms = 1,
+ .force_cap_7000 = 1,
+};
+
+static int pcf85063_probe(struct i2c_client *client)
+{
+ struct pcf85063 *pcf85063;
+ unsigned int tmp;
int err;
+ const struct pcf85063_config *config = &pcf85063tp_config;
+ const void *data = of_device_get_match_data(&client->dev);
+ struct nvmem_config nvmem_cfg = {
+ .name = "pcf85063_nvram",
+ .reg_read = pcf85063_nvmem_read,
+ .reg_write = pcf85063_nvmem_write,
+ .type = NVMEM_TYPE_BATTERY_BACKED,
+ .size = 1,
+ };
dev_dbg(&client->dev, "%s\n", __func__);
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
- return -ENODEV;
+ pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
+ GFP_KERNEL);
+ if (!pcf85063)
+ return -ENOMEM;
- err = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
- if (err < 0) {
+ if (data)
+ config = data;
+
+ pcf85063->regmap = devm_regmap_init_i2c(client, &config->regmap);
+ if (IS_ERR(pcf85063->regmap))
+ return PTR_ERR(pcf85063->regmap);
+
+ i2c_set_clientdata(client, pcf85063);
+
+ err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL1, &tmp);
+ if (err) {
dev_err(&client->dev, "RTC chip is not present\n");
return err;
}
- rtc = devm_rtc_device_register(&client->dev,
- pcf85063_driver.driver.name,
- &pcf85063_rtc_ops, THIS_MODULE);
+ pcf85063->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(pcf85063->rtc))
+ return PTR_ERR(pcf85063->rtc);
- return PTR_ERR_OR_ZERO(rtc);
+ err = pcf85063_load_capacitance(pcf85063, client->dev.of_node,
+ config->force_cap_7000 ? 7000 : 0);
+ if (err < 0)
+ dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
+ err);
+
+ pcf85063->rtc->ops = &pcf85063_rtc_ops;
+ pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
+ pcf85063->rtc->uie_unsupported = 1;
+
+ if (config->has_alarms && client->irq > 0) {
+ err = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, pcf85063_rtc_handle_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "pcf85063", pcf85063);
+ if (err) {
+ dev_warn(&pcf85063->rtc->dev,
+ "unable to request IRQ, alarms disabled\n");
+ } else {
+ pcf85063->rtc->ops = &pcf85063_rtc_ops_alarm;
+ device_init_wakeup(&client->dev, true);
+ err = dev_pm_set_wake_irq(&client->dev, client->irq);
+ if (err)
+ dev_err(&pcf85063->rtc->dev,
+ "failed to enable irq wake\n");
+ }
+ }
+
+ nvmem_cfg.priv = pcf85063->regmap;
+ rtc_nvmem_register(pcf85063->rtc, &nvmem_cfg);
+
+ return rtc_register_device(pcf85063->rtc);
}
-static const struct i2c_device_id pcf85063_id[] = {
- { "pcf85063", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, pcf85063_id);
-
#ifdef CONFIG_OF
static const struct of_device_id pcf85063_of_match[] = {
- { .compatible = "nxp,pcf85063" },
+ { .compatible = "nxp,pcf85063", .data = &pcf85063tp_config },
+ { .compatible = "nxp,pcf85063tp", .data = &pcf85063tp_config },
+ { .compatible = "nxp,pcf85063a", .data = &pcf85063a_config },
+ { .compatible = "microcrystal,rv8263", .data = &rv8263_config },
{}
};
MODULE_DEVICE_TABLE(of, pcf85063_of_match);
@@ -223,8 +488,7 @@
.name = "rtc-pcf85063",
.of_match_table = of_match_ptr(pcf85063_of_match),
},
- .probe = pcf85063_probe,
- .id_table = pcf85063_id,
+ .probe_new = pcf85063_probe,
};
module_i2c_driver(pcf85063_driver);
diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c
index 453615f..2f435e5 100644
--- a/drivers/rtc/rtc-pcf8523.c
+++ b/drivers/rtc/rtc-pcf8523.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Avionic Design GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/bcd.h>
@@ -85,23 +82,46 @@
return 0;
}
-static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
+static int pcf8523_voltage_low(struct i2c_client *client)
{
u8 value;
int err;
+ err = pcf8523_read(client, REG_CONTROL3, &value);
+ if (err < 0)
+ return err;
+
+ return !!(value & REG_CONTROL3_BLF);
+}
+
+static int pcf8523_load_capacitance(struct i2c_client *client)
+{
+ u32 load;
+ u8 value;
+ int err;
+
err = pcf8523_read(client, REG_CONTROL1, &value);
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;
}
@@ -167,6 +187,14 @@
struct i2c_msg msgs[2];
int err;
+ err = pcf8523_voltage_low(client);
+ if (err < 0) {
+ return err;
+ } else if (err > 0) {
+ dev_err(dev, "low voltage detected, time is unreliable\n");
+ return -EINVAL;
+ }
+
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 1;
@@ -251,17 +279,13 @@
unsigned long arg)
{
struct i2c_client *client = to_i2c_client(dev);
- u8 value;
- int ret = 0, err;
+ int ret;
switch (cmd) {
case RTC_VL_READ:
- err = pcf8523_read(client, REG_CONTROL3, &value);
- if (err < 0)
- return err;
-
- if (value & REG_CONTROL3_BLF)
- ret = 1;
+ ret = pcf8523_voltage_low(client);
+ if (ret < 0)
+ return ret;
if (copy_to_user((void __user *)arg, &ret, sizeof(int)))
return -EFAULT;
@@ -331,9 +355,10 @@
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)
@@ -358,6 +383,7 @@
#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);
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index c04a1ed..3450d61 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* drivers/rtc/rtc-pcf85363.c
*
* Driver for NXP PCF85363 real-time clock.
*
* Copyright (C) 2017 Eric Nelson
- *
- * 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.
- *
- * Based loosely on rtc-8583 by Russell King, Wolfram Sang and Juergen Beisert
*/
#include <linux/module.h>
#include <linux/i2c.h>
@@ -112,14 +107,16 @@
#define NVRAM_SIZE 0x40
-static struct i2c_driver pcf85363_driver;
-
struct pcf85363 {
- struct device *dev;
struct rtc_device *rtc;
struct regmap *regmap;
};
+struct pcf85x63_config {
+ struct regmap_config regmap;
+ unsigned int num_nvram;
+};
+
static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
@@ -169,7 +166,12 @@
buf[DT_YEARS] = bin2bcd(tm->tm_year % 100);
ret = regmap_bulk_write(pcf85363->regmap, CTRL_STOP_EN,
- tmp, sizeof(tmp));
+ tmp, 2);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_write(pcf85363->regmap, DT_100THS,
+ buf, sizeof(tmp) - 2);
if (ret)
return ret;
@@ -311,54 +313,102 @@
val, bytes);
}
-static const struct regmap_config regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 0x7f,
+static int pcf85x63_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct pcf85363 *pcf85363 = priv;
+ unsigned int tmp_val;
+ int ret;
+
+ ret = regmap_read(pcf85363->regmap, CTRL_RAMBYTE, &tmp_val);
+ (*(unsigned char *) val) = (unsigned char) tmp_val;
+
+ return ret;
+}
+
+static int pcf85x63_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct pcf85363 *pcf85363 = priv;
+ unsigned char tmp_val;
+
+ tmp_val = *((unsigned char *)val);
+ return regmap_write(pcf85363->regmap, CTRL_RAMBYTE,
+ (unsigned int)tmp_val);
+}
+
+static const struct pcf85x63_config pcf_85263_config = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x2f,
+ },
+ .num_nvram = 1
+};
+
+static const struct pcf85x63_config pcf_85363_config = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x7f,
+ },
+ .num_nvram = 2
};
static int pcf85363_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf85363 *pcf85363;
- struct nvmem_config nvmem_cfg = {
- .name = "pcf85363-",
- .word_size = 1,
- .stride = 1,
- .size = NVRAM_SIZE,
- .reg_read = pcf85363_nvram_read,
- .reg_write = pcf85363_nvram_write,
+ const struct pcf85x63_config *config = &pcf_85363_config;
+ const void *data = of_device_get_match_data(&client->dev);
+ static struct nvmem_config nvmem_cfg[] = {
+ {
+ .name = "pcf85x63-",
+ .word_size = 1,
+ .stride = 1,
+ .size = 1,
+ .reg_read = pcf85x63_nvram_read,
+ .reg_write = pcf85x63_nvram_write,
+ }, {
+ .name = "pcf85363-",
+ .word_size = 1,
+ .stride = 1,
+ .size = NVRAM_SIZE,
+ .reg_read = pcf85363_nvram_read,
+ .reg_write = pcf85363_nvram_write,
+ },
};
- int ret;
+ int ret, i;
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
- return -ENODEV;
+ if (data)
+ config = data;
pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363),
GFP_KERNEL);
if (!pcf85363)
return -ENOMEM;
- pcf85363->regmap = devm_regmap_init_i2c(client, ®map_config);
+ pcf85363->regmap = devm_regmap_init_i2c(client, &config->regmap);
if (IS_ERR(pcf85363->regmap)) {
dev_err(&client->dev, "regmap allocation failed\n");
return PTR_ERR(pcf85363->regmap);
}
- pcf85363->dev = &client->dev;
i2c_set_clientdata(client, pcf85363);
- pcf85363->rtc = devm_rtc_allocate_device(pcf85363->dev);
+ pcf85363->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(pcf85363->rtc))
return PTR_ERR(pcf85363->rtc);
pcf85363->rtc->ops = &rtc_ops;
+ pcf85363->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ pcf85363->rtc->range_max = RTC_TIMESTAMP_END_2099;
if (client->irq > 0) {
regmap_write(pcf85363->regmap, CTRL_FLAGS, 0);
regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
PIN_IO_INTA_OUT, PIN_IO_INTAPM);
- ret = devm_request_threaded_irq(pcf85363->dev, client->irq,
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf85363_rtc_handle_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"pcf85363", client);
@@ -370,15 +420,18 @@
ret = rtc_register_device(pcf85363->rtc);
- nvmem_cfg.priv = pcf85363;
- rtc_nvmem_register(pcf85363->rtc, &nvmem_cfg);
+ for (i = 0; i < config->num_nvram; i++) {
+ nvmem_cfg[i].priv = pcf85363;
+ rtc_nvmem_register(pcf85363->rtc, &nvmem_cfg[i]);
+ }
return ret;
}
static const struct of_device_id dev_ids[] = {
- { .compatible = "nxp,pcf85363" },
- {}
+ { .compatible = "nxp,pcf85263", .data = &pcf_85263_config },
+ { .compatible = "nxp,pcf85363", .data = &pcf_85363_config },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dev_ids);
@@ -393,5 +446,5 @@
module_i2c_driver(pcf85363_driver);
MODULE_AUTHOR("Eric Nelson");
-MODULE_DESCRIPTION("pcf85363 I2C RTC driver");
+MODULE_DESCRIPTION("pcf85263/pcf85363 I2C RTC driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 3efc86c..24baa47 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An I2C driver for the Philips PCF8563 RTC
* Copyright 2005-06 Tower Technologies
@@ -8,10 +9,6 @@
* based on the other drivers in this same directory.
*
* http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk-provider.h>
@@ -199,8 +196,9 @@
* In the routines that deal directly with the pcf8563 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
*/
-static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
unsigned char buf[9];
int err;
@@ -231,9 +229,7 @@
tm->tm_mday = bcd2bin(buf[PCF8563_REG_DM] & 0x3F);
tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
tm->tm_mon = bcd2bin(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
- tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]);
- if (tm->tm_year < 70)
- tm->tm_year += 100; /* assume we are in 1970...2069 */
+ tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]) + 100;
/* detect the polarity heuristically. see note above. */
pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
(tm->tm_year >= 100) : (tm->tm_year < 100);
@@ -247,8 +243,9 @@
return 0;
}
-static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
unsigned char buf[9];
@@ -269,7 +266,7 @@
buf[PCF8563_REG_MO] = bin2bcd(tm->tm_mon + 1);
/* year and century */
- buf[PCF8563_REG_YR] = bin2bcd(tm->tm_year % 100);
+ buf[PCF8563_REG_YR] = bin2bcd(tm->tm_year - 100);
if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
buf[PCF8563_REG_MO] |= PCF8563_MO_C;
@@ -302,8 +299,8 @@
* because of the cached voltage_low value but do it
* anyway for consistency.
*/
- if (pcf8563_get_datetime(to_i2c_client(dev), &tm))
- pcf8563_set_datetime(to_i2c_client(dev), &tm);
+ if (pcf8563_rtc_read_time(dev, &tm))
+ pcf8563_rtc_set_time(dev, &tm);
/* Clear the cached value. */
pcf8563->voltage_low = 0;
@@ -317,16 +314,6 @@
#define pcf8563_rtc_ioctl NULL
#endif
-static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return pcf8563_get_datetime(to_i2c_client(dev), tm);
-}
-
-static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return pcf8563_set_datetime(to_i2c_client(dev), tm);
-}
-
static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -563,7 +550,6 @@
struct pcf8563 *pcf8563;
int err;
unsigned char buf;
- unsigned char alm_pending;
dev_dbg(&client->dev, "%s\n", __func__);
@@ -587,42 +573,46 @@
return err;
}
- err = pcf8563_get_alarm_mode(client, NULL, &alm_pending);
- if (err) {
- dev_err(&client->dev, "%s: read error\n", __func__);
+ /* Clear flags and disable interrupts */
+ buf = 0;
+ err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf);
+ if (err < 0) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
return err;
}
- if (alm_pending)
- pcf8563_set_alarm_mode(client, 0);
- pcf8563->rtc = devm_rtc_device_register(&client->dev,
- pcf8563_driver.driver.name,
- &pcf8563_rtc_ops, THIS_MODULE);
-
+ pcf8563->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(pcf8563->rtc))
return PTR_ERR(pcf8563->rtc);
+ pcf8563->rtc->ops = &pcf8563_rtc_ops;
+ /* the pcf8563 alarm only supports a minute accuracy */
+ pcf8563->rtc->uie_unsupported = 1;
+ pcf8563->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ pcf8563->rtc->range_max = RTC_TIMESTAMP_END_2099;
+ pcf8563->rtc->set_start_time = true;
+
if (client->irq > 0) {
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf8563_irq,
- IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
+ IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW,
pcf8563_driver.driver.name, client);
if (err) {
dev_err(&client->dev, "unable to request IRQ %d\n",
client->irq);
return err;
}
-
}
+ err = rtc_register_device(pcf8563->rtc);
+ if (err)
+ return err;
+
#ifdef CONFIG_COMMON_CLK
/* register clk in common clk framework */
pcf8563_clkout_register_clk(pcf8563);
#endif
- /* the pcf8563 alarm only supports a minute accuracy */
- pcf8563->rtc->uie_unsupported = 1;
-
return 0;
}
@@ -636,6 +626,8 @@
#ifdef CONFIG_OF
static const struct of_device_id pcf8563_of_match[] = {
{ .compatible = "nxp,pcf8563" },
+ { .compatible = "epson,rtc8564" },
+ { .compatible = "microcrystal,rv8564" },
{}
};
MODULE_DEVICE_TABLE(of, pcf8563_of_match);
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
index 7ca9e88..c80ca20 100644
--- a/drivers/rtc/rtc-pcf8583.c
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/rtc/rtc-pcf8583.c
*
* Copyright (C) 2000 Russell King
* Copyright (C) 2008 Wolfram Sang & Juergen Beisert, Pengutronix
*
- * 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.
- *
* Driver for PCF8583 RTC & RAM chip
*
* Converted to the generic RTC susbsystem by G. Liakhovetski (2006)
diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c
index 3c08eab..17653ed 100644
--- a/drivers/rtc/rtc-pic32.c
+++ b/drivers/rtc/rtc-pic32.c
@@ -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>
@@ -170,9 +162,7 @@
rtc_tm->tm_year += 100;
- dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
- 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
- rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+ dev_dbg(dev, "read time %ptR\n", rtc_tm);
clk_disable(pdata->clk);
return 0;
@@ -182,16 +172,8 @@
{
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 %04d.%02d.%02d %02d:%02d:%02d\n",
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
-
- if (year < 0 || year >= 100) {
- dev_err(dev, "rtc only supports 100 years\n");
- return -EINVAL;
- }
+ dev_dbg(dev, "set time %ptR\n", tm);
clk_enable(pdata->clk);
writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC);
@@ -199,7 +181,7 @@
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;
@@ -224,10 +206,7 @@
alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0;
- dev_dbg(dev, "getalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
- alm_en,
- 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
- alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+ dev_dbg(dev, "getalarm: %d, %ptR\n", alm_en, alm_tm);
alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
@@ -247,10 +226,7 @@
void __iomem *base = pdata->reg_base;
clk_enable(pdata->clk);
- dev_dbg(dev, "setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
- alrm->enabled,
- 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "setalarm: %d, %ptR\n", alrm->enabled, tm);
writel(0x00, base + PIC32_ALRMTIME);
writel(0x00, base + PIC32_ALRMDATE);
@@ -332,10 +308,8 @@
platform_set_drvdata(pdev, pdata);
pdata->alarm_irq = platform_get_irq(pdev, 0);
- if (pdata->alarm_irq < 0) {
- dev_err(&pdev->dev, "no irq for alarm\n");
+ if (pdata->alarm_irq < 0)
return pdata->alarm_irq;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pdata->reg_base = devm_ioremap_resource(&pdev->dev, res);
@@ -358,13 +332,17 @@
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;
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index f85a1a9..d4a5f8a 100644
--- a/drivers/rtc/rtc-pl030.c
+++ b/drivers/rtc/rtc-pl030.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/rtc/rtc-pl030.c
*
* Copyright (C) 2000-2001 Deep Blue Solutions Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/rtc.h>
@@ -112,6 +109,13 @@
goto err_rtc;
}
+ rtc->rtc = devm_rtc_allocate_device(&dev->dev);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ goto err_rtc;
+ }
+
+ rtc->rtc->ops = &pl030_ops;
rtc->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!rtc->base) {
ret = -ENOMEM;
@@ -128,12 +132,9 @@
if (ret)
goto err_irq;
- rtc->rtc = rtc_device_register("pl030", &dev->dev, &pl030_ops,
- THIS_MODULE);
- if (IS_ERR(rtc->rtc)) {
- ret = PTR_ERR(rtc->rtc);
+ ret = rtc_register_device(rtc->rtc);
+ if (ret)
goto err_reg;
- }
return 0;
@@ -154,7 +155,6 @@
writel(0, rtc->base + RTC_CR);
free_irq(dev->irq[0], rtc);
- rtc_device_unregister(rtc->rtc);
iounmap(rtc->base);
amba_release_regions(dev);
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 82eb7da..180caeb 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* drivers/rtc/rtc-pl031.c
*
@@ -9,11 +10,6 @@
*
* Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
* Copyright 2010 (c) ST-Ericsson AB
- *
- * 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.
*/
#include <linux/module.h>
#include <linux/rtc.h>
@@ -310,7 +306,6 @@
device_init_wakeup(&adev->dev, false);
if (adev->irq[0])
free_irq(adev->irq[0], ldata);
- rtc_device_unregister(ldata->rtc);
amba_release_regions(adev);
return 0;
@@ -383,24 +378,25 @@
}
device_init_wakeup(&adev->dev, true);
- ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
- THIS_MODULE);
- if (IS_ERR(ldata->rtc)) {
- ret = PTR_ERR(ldata->rtc);
+ ldata->rtc = devm_rtc_allocate_device(&adev->dev);
+ if (IS_ERR(ldata->rtc))
+ return PTR_ERR(ldata->rtc);
+
+ ldata->rtc->ops = ops;
+
+ ret = rtc_register_device(ldata->rtc);
+ if (ret)
goto out;
- }
if (adev->irq[0]) {
ret = request_irq(adev->irq[0], pl031_interrupt,
vendor->irqflags, "rtc-pl031", ldata);
if (ret)
- goto out_no_irq;
+ goto out;
dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);
}
return 0;
-out_no_irq:
- rtc_device_unregister(ldata->rtc);
out:
amba_release_regions(adev);
err_req:
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index 29358a0..f5a30e0 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -1,13 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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/of.h>
#include <linux/module.h>
@@ -213,13 +205,12 @@
}
}
- 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);
- dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
- secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
- tm->tm_mday, tm->tm_mon, tm->tm_year);
+ dev_dbg(dev, "secs = %lu, h:m:s == %ptRt, y-m-d = %ptRdr\n", secs, tm, tm);
return 0;
}
@@ -264,10 +255,8 @@
goto rtc_rw_fail;
}
- dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
- alarm->time.tm_hour, alarm->time.tm_min,
- alarm->time.tm_sec, alarm->time.tm_mday,
- alarm->time.tm_mon, alarm->time.tm_year);
+ dev_dbg(dev, "Alarm Set for h:m:s=%ptRt, y-m-d=%ptRdr\n",
+ &alarm->time, &alarm->time);
rtc_rw_fail:
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
return rc;
@@ -288,7 +277,8 @@
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);
@@ -298,10 +288,8 @@
return rc;
}
- dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
- alarm->time.tm_hour, alarm->time.tm_min,
- alarm->time.tm_sec, alarm->time.tm_mday,
- alarm->time.tm_mon, alarm->time.tm_year);
+ dev_dbg(dev, "Alarm set for - h:m:s=%ptRt, y-m-d=%ptRdr\n",
+ &alarm->time, &alarm->time);
return 0;
}
@@ -480,10 +468,8 @@
}
rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0);
- if (rtc_dd->rtc_alarm_irq < 0) {
- dev_err(&pdev->dev, "Alarm IRQ resource absent!\n");
+ if (rtc_dd->rtc_alarm_irq < 0)
return -ENXIO;
- }
rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
"allow-set-time");
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c
deleted file mode 100644
index a9dd921..0000000
--- a/drivers/rtc/rtc-proc.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * RTC subsystem, proc interface
- *
- * Copyright (C) 2005-06 Tower Technologies
- * Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * based on arch/arm/common/rtctime.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/rtc.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-
-#include "rtc-core.h"
-
-#define NAME_SIZE 10
-
-#if defined(CONFIG_RTC_HCTOSYS_DEVICE)
-static bool is_rtc_hctosys(struct rtc_device *rtc)
-{
- int size;
- char name[NAME_SIZE];
-
- size = scnprintf(name, NAME_SIZE, "rtc%d", rtc->id);
- if (size > NAME_SIZE)
- return false;
-
- return !strncmp(name, CONFIG_RTC_HCTOSYS_DEVICE, NAME_SIZE);
-}
-#else
-static bool is_rtc_hctosys(struct rtc_device *rtc)
-{
- return (rtc->id == 0);
-}
-#endif
-
-static int rtc_proc_show(struct seq_file *seq, void *offset)
-{
- int err;
- struct rtc_device *rtc = seq->private;
- const struct rtc_class_ops *ops = rtc->ops;
- struct rtc_wkalrm alrm;
- struct rtc_time tm;
-
- err = rtc_read_time(rtc, &tm);
- if (err == 0) {
- seq_printf(seq,
- "rtc_time\t: %02d:%02d:%02d\n"
- "rtc_date\t: %04d-%02d-%02d\n",
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
- }
-
- err = rtc_read_alarm(rtc, &alrm);
- if (err == 0) {
- seq_printf(seq, "alrm_time\t: ");
- if ((unsigned int)alrm.time.tm_hour <= 24)
- seq_printf(seq, "%02d:", alrm.time.tm_hour);
- else
- seq_printf(seq, "**:");
- if ((unsigned int)alrm.time.tm_min <= 59)
- seq_printf(seq, "%02d:", alrm.time.tm_min);
- else
- seq_printf(seq, "**:");
- if ((unsigned int)alrm.time.tm_sec <= 59)
- seq_printf(seq, "%02d\n", alrm.time.tm_sec);
- else
- seq_printf(seq, "**\n");
-
- seq_printf(seq, "alrm_date\t: ");
- if ((unsigned int)alrm.time.tm_year <= 200)
- seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);
- else
- seq_printf(seq, "****-");
- if ((unsigned int)alrm.time.tm_mon <= 11)
- seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);
- else
- seq_printf(seq, "**-");
- if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)
- seq_printf(seq, "%02d\n", alrm.time.tm_mday);
- else
- seq_printf(seq, "**\n");
- seq_printf(seq, "alarm_IRQ\t: %s\n",
- alrm.enabled ? "yes" : "no");
- seq_printf(seq, "alrm_pending\t: %s\n",
- alrm.pending ? "yes" : "no");
- seq_printf(seq, "update IRQ enabled\t: %s\n",
- (rtc->uie_rtctimer.enabled) ? "yes" : "no");
- seq_printf(seq, "periodic IRQ enabled\t: %s\n",
- (rtc->pie_enabled) ? "yes" : "no");
- seq_printf(seq, "periodic IRQ frequency\t: %d\n",
- rtc->irq_freq);
- seq_printf(seq, "max user IRQ frequency\t: %d\n",
- rtc->max_user_freq);
- }
-
- seq_printf(seq, "24hr\t\t: yes\n");
-
- if (ops->proc)
- ops->proc(rtc->dev.parent, seq);
-
- return 0;
-}
-
-void rtc_proc_add_device(struct rtc_device *rtc)
-{
- if (is_rtc_hctosys(rtc))
- proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show,
- rtc);
-}
-
-void rtc_proc_del_device(struct rtc_device *rtc)
-{
- if (is_rtc_hctosys(rtc))
- remove_proc_entry("driver/rtc", NULL);
-}
diff --git a/drivers/rtc/rtc-ps3.c b/drivers/rtc/rtc-ps3.c
index 347288b..f0336d6 100644
--- a/drivers/rtc/rtc-ps3.c
+++ b/drivers/rtc/rtc-ps3.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PS3 RTC Driver
*
* Copyright 2009 Sony Corporation
- *
- * 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; version 2 of the License.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.
- * If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
@@ -40,16 +28,13 @@
static int ps3_get_time(struct device *dev, struct rtc_time *tm)
{
- rtc_time_to_tm(read_rtc() + ps3_os_area_get_rtc_diff(), tm);
+ rtc_time64_to_tm(read_rtc() + ps3_os_area_get_rtc_diff(), tm);
return 0;
}
static int ps3_set_time(struct device *dev, struct rtc_time *tm)
{
- unsigned long now;
-
- rtc_tm_to_time(tm, &now);
- ps3_os_area_set_rtc_diff(now - read_rtc());
+ ps3_os_area_set_rtc_diff(rtc_tm_to_time64(tm) - read_rtc());
return 0;
}
@@ -62,13 +47,16 @@
{
struct rtc_device *rtc;
- rtc = devm_rtc_device_register(&dev->dev, "rtc-ps3", &ps3_rtc_ops,
- THIS_MODULE);
+ rtc = devm_rtc_allocate_device(&dev->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
+ rtc->ops = &ps3_rtc_ops;
+ rtc->range_max = U64_MAX;
+
platform_set_drvdata(dev, rtc);
- return 0;
+
+ return rtc_register_device(rtc);
}
static struct platform_driver ps3_rtc_driver = {
diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c
index 9e83be3..89ff713 100644
--- a/drivers/rtc/rtc-puv3.c
+++ b/drivers/rtc/rtc-puv3.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* RTC driver code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -90,9 +87,7 @@
{
rtc_time_to_tm(readl(RTC_RCNR), rtc_tm);
- dev_dbg(dev, "read time %02x.%02x.%02x %02x/%02x/%02x\n",
- rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
- rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+ dev_dbg(dev, "read time %ptRr\n", rtc_tm);
return 0;
}
@@ -101,9 +96,7 @@
{
unsigned long rtc_count = 0;
- dev_dbg(dev, "set time %02d.%02d.%02d %02d/%02d/%02d\n",
- tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "set time %ptRr\n", tm);
rtc_tm_to_time(tm, &rtc_count);
writel(rtc_count, RTC_RCNR);
@@ -119,10 +112,7 @@
alrm->enabled = readl(RTC_RTSR) & RTC_RTSR_ALE;
- dev_dbg(dev, "read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
- alrm->enabled,
- alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
- alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+ dev_dbg(dev, "read alarm: %d, %ptRr\n", alrm->enabled, alm_tm);
return 0;
}
@@ -132,10 +122,7 @@
struct rtc_time *tm = &alrm->time;
unsigned long rtcalarm_count = 0;
- dev_dbg(dev, "puv3_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
- alrm->enabled,
- tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
- tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
+ dev_dbg(dev, "set alarm: %d, %ptRr\n", alrm->enabled, tm);
rtc_tm_to_time(tm, &rtcalarm_count);
writel(rtcalarm_count, RTC_RTAR);
@@ -199,16 +186,12 @@
/* find the IRQs */
puv3_rtc_tickno = platform_get_irq(pdev, 1);
- if (puv3_rtc_tickno < 0) {
- dev_err(&pdev->dev, "no irq for rtc tick\n");
+ if (puv3_rtc_tickno < 0)
return -ENOENT;
- }
puv3_rtc_alarmno = platform_get_irq(pdev, 0);
- if (puv3_rtc_alarmno < 0) {
- dev_err(&pdev->dev, "no irq for alarm\n");
+ if (puv3_rtc_alarmno < 0)
return -ENOENT;
- }
dev_dbg(&pdev->dev, "PKUnity_rtc: tick irq %d, alarm irq %d\n",
puv3_rtc_tickno, puv3_rtc_alarmno);
@@ -252,10 +235,8 @@
/* register RTC and exit */
rtc->ops = &puv3_rtcops;
ret = rtc_register_device(rtc);
- if (ret) {
- dev_err(&pdev->dev, "cannot attach rtc\n");
+ if (ret)
goto err_nortc;
- }
/* platform setup code should have handled this; sigh */
if (!device_can_wakeup(&pdev->dev))
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index e1887b8..d2f1d8f 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Real Time Clock interface for XScale PXA27x and PXA3xx
*
* Copyright (C) 2008 Robert Jarzmik
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -145,8 +131,7 @@
static irqreturn_t pxa_rtc_irq(int irq, void *dev_id)
{
- struct platform_device *pdev = to_platform_device(dev_id);
- struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev);
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev_id);
u32 rtsr;
unsigned long events = 0;
@@ -339,15 +324,11 @@
}
sa1100_rtc->irq_1hz = platform_get_irq(pdev, 0);
- if (sa1100_rtc->irq_1hz < 0) {
- dev_err(dev, "No 1Hz IRQ resource defined\n");
+ if (sa1100_rtc->irq_1hz < 0)
return -ENXIO;
- }
sa1100_rtc->irq_alarm = platform_get_irq(pdev, 1);
- if (sa1100_rtc->irq_alarm < 0) {
- dev_err(dev, "No alarm IRQ resource defined\n");
+ if (sa1100_rtc->irq_alarm < 0)
return -ENXIO;
- }
pxa_rtc->base = devm_ioremap(dev, pxa_rtc->ress->start,
resource_size(pxa_rtc->ress));
diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c
index 1943c81..2498278 100644
--- a/drivers/rtc/rtc-r7301.c
+++ b/drivers/rtc/rtc-r7301.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* EPSON TOYOCOM RTC-7301SF/DG Driver
*
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c
index a39ccd1..84f0d25 100644
--- a/drivers/rtc/rtc-r9701.c
+++ b/drivers/rtc/rtc-r9701.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Epson RTC-9701JE
*
@@ -7,10 +8,6 @@
*
* Copyright (C) 2006 8D Technologies inc.
* Copyright (C) 2004 Compulab Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-rc5t583.c b/drivers/rtc/rtc-rc5t583.c
index 68ce774..18684a7 100644
--- a/drivers/rtc/rtc-rc5t583.c
+++ b/drivers/rtc/rtc-rc5t583.c
@@ -1,20 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock
*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
* Author: Venu Byravarasu <vbyravarasu@nvidia.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+ */
#include <linux/kernel.h>
#include <linux/errno.h>
diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c
index 739c0d4..c0334c6 100644
--- a/drivers/rtc/rtc-rk808.c
+++ b/drivers/rtc/rtc-rk808.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* RTC driver for Rockchip RK808
*
@@ -5,15 +6,6 @@
*
* Author: Chris Zhong <zyw@rock-chips.com>
* Author: Zhang Qing <zhangqing@rock-chips.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/module.h>
@@ -50,9 +42,18 @@
#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)
+struct rk_rtc_compat_reg {
+ unsigned int ctrl_reg;
+ unsigned int status_reg;
+ unsigned int alarm_seconds_reg;
+ unsigned int int_reg;
+ unsigned int seconds_reg;
+};
+
struct rk808_rtc {
struct rk808 *rk808;
struct rtc_device *rtc;
+ struct rk_rtc_compat_reg *creg;
int irq;
};
@@ -101,7 +102,7 @@
int ret;
/* Force an update of the shadowed registers right now */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_RTC_GET_TIME,
BIT_RTC_CTRL_REG_RTC_GET_TIME);
if (ret) {
@@ -115,7 +116,7 @@
* 32khz. If we clear the GET_TIME bit here, the time of i2c transfer
* certainly more than 31.25us: 16 * 2.5us at 400kHz bus frequency.
*/
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_RTC_GET_TIME,
0);
if (ret) {
@@ -123,7 +124,7 @@
return ret;
}
- ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG,
+ ret = regmap_bulk_read(rk808->regmap, rk808_rtc->creg->seconds_reg,
rtc_data, NUM_TIME_REGS);
if (ret) {
dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
@@ -138,9 +139,7 @@
tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100;
tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK);
rockchip_to_gregorian(tm);
- dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
- 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "RTC date/time %ptRd(%d) %ptRt\n", tm, tm->tm_wday, tm);
return ret;
}
@@ -153,9 +152,7 @@
u8 rtc_data[NUM_TIME_REGS];
int ret;
- dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
- 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "set RTC date/time %ptRd(%d) %ptRt\n", tm, tm->tm_wday, tm);
gregorian_to_rockchip(tm);
rtc_data[0] = bin2bcd(tm->tm_sec);
rtc_data[1] = bin2bcd(tm->tm_min);
@@ -166,7 +163,7 @@
rtc_data[6] = bin2bcd(tm->tm_wday);
/* Stop RTC while updating the RTC registers */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_STOP_RTC_M,
BIT_RTC_CTRL_REG_STOP_RTC_M);
if (ret) {
@@ -174,14 +171,14 @@
return ret;
}
- ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG,
+ ret = regmap_bulk_write(rk808->regmap, rk808_rtc->creg->seconds_reg,
rtc_data, NUM_TIME_REGS);
if (ret) {
dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);
return ret;
}
/* Start RTC again */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_STOP_RTC_M, 0);
if (ret) {
dev_err(dev, "Failed to update RTC control: %d\n", ret);
@@ -199,8 +196,13 @@
uint32_t int_reg;
int ret;
- ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ ret = regmap_bulk_read(rk808->regmap,
+ rk808_rtc->creg->alarm_seconds_reg,
alrm_data, NUM_ALARM_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to read RTC alarm date REG: %d\n", ret);
+ return ret;
+ }
alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);
alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK);
@@ -210,16 +212,14 @@
alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
rockchip_to_gregorian(&alrm->time);
- ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
+ ret = regmap_read(rk808->regmap, rk808_rtc->creg->int_reg, &int_reg);
if (ret) {
dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);
return ret;
}
- dev_dbg(dev, "alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
- 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
- alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
- alrm->time.tm_min, alrm->time.tm_sec);
+ dev_dbg(dev, "alrm read RTC date/time %ptRd(%d) %ptRt\n",
+ &alrm->time, alrm->time.tm_wday, &alrm->time);
alrm->enabled = (int_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) ? 1 : 0;
@@ -231,7 +231,7 @@
struct rk808 *rk808 = rk808_rtc->rk808;
int ret;
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->int_reg,
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);
return ret;
@@ -242,7 +242,7 @@
struct rk808 *rk808 = rk808_rtc->rk808;
int ret;
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->int_reg,
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
@@ -261,10 +261,8 @@
dev_err(dev, "Failed to stop alarm: %d\n", ret);
return ret;
}
- dev_dbg(dev, "alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
- 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
- alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
- alrm->time.tm_min, alrm->time.tm_sec);
+ dev_dbg(dev, "alrm set RTC date/time %ptRd(%d) %ptRt\n",
+ &alrm->time, alrm->time.tm_wday, &alrm->time);
gregorian_to_rockchip(&alrm->time);
alrm_data[0] = bin2bcd(alrm->time.tm_sec);
@@ -274,7 +272,8 @@
alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1);
alrm_data[5] = bin2bcd(alrm->time.tm_year - 100);
- ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ ret = regmap_bulk_write(rk808->regmap,
+ rk808_rtc->creg->alarm_seconds_reg,
alrm_data, NUM_ALARM_REGS);
if (ret) {
dev_err(dev, "Failed to bulk write: %d\n", ret);
@@ -318,7 +317,7 @@
struct i2c_client *client = rk808->i2c;
int ret;
- ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,
RTC_STATUS_MASK);
if (ret) {
dev_err(&client->dev,
@@ -344,8 +343,7 @@
/* Turn off the alarm if it should not be a wake source. */
static int rk808_rtc_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(rk808_rtc->irq);
@@ -358,8 +356,7 @@
*/
static int rk808_rtc_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(rk808_rtc->irq);
@@ -371,6 +368,22 @@
static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops,
rk808_rtc_suspend, rk808_rtc_resume);
+static struct rk_rtc_compat_reg rk808_creg = {
+ .ctrl_reg = RK808_RTC_CTRL_REG,
+ .status_reg = RK808_RTC_STATUS_REG,
+ .alarm_seconds_reg = RK808_ALARM_SECONDS_REG,
+ .int_reg = RK808_RTC_INT_REG,
+ .seconds_reg = RK808_SECONDS_REG,
+};
+
+static struct rk_rtc_compat_reg rk817_creg = {
+ .ctrl_reg = RK817_RTC_CTRL_REG,
+ .status_reg = RK817_RTC_STATUS_REG,
+ .alarm_seconds_reg = RK817_ALARM_SECONDS_REG,
+ .int_reg = RK817_RTC_INT_REG,
+ .seconds_reg = RK817_SECONDS_REG,
+};
+
static int rk808_rtc_probe(struct platform_device *pdev)
{
struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
@@ -381,11 +394,20 @@
if (rk808_rtc == NULL)
return -ENOMEM;
+ switch (rk808->variant) {
+ case RK809_ID:
+ case RK817_ID:
+ rk808_rtc->creg = &rk817_creg;
+ break;
+ default:
+ rk808_rtc->creg = &rk808_creg;
+ break;
+ }
platform_set_drvdata(pdev, rk808_rtc);
rk808_rtc->rk808 = rk808;
/* start rtc running by default, and use shadowed timer. */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_STOP_RTC_M |
BIT_RTC_CTRL_REG_RTC_READSEL_M,
BIT_RTC_CTRL_REG_RTC_READSEL_M);
@@ -395,12 +417,12 @@
return ret;
}
- ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,
RTC_STATUS_MASK);
if (ret) {
dev_err(&pdev->dev,
"Failed to write RTC status: %d\n", ret);
- return ret;
+ return ret;
}
device_init_wakeup(&pdev->dev, 1);
@@ -412,12 +434,8 @@
rk808_rtc->rtc->ops = &rk808_rtc_ops;
rk808_rtc->irq = platform_get_irq(pdev, 0);
- if (rk808_rtc->irq < 0) {
- if (rk808_rtc->irq != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Wake up is not possible as irq = %d\n",
- rk808_rtc->irq);
+ if (rk808_rtc->irq < 0)
return rk808_rtc->irq;
- }
/* request alarm irq of rk808 */
ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL,
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index f1c160f..8776ead 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Ricoh RP5C01 RTC Driver
*
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index f2de8b1..47c1367 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* A SPI driver for the Ricoh RS5C348 RTC
*
* Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
*
- * 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.
- *
* The board specific init code should provide characteristics of this
* device:
* Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS
@@ -66,6 +63,17 @@
u8 txbuf[5+7], *txp;
int ret;
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
+ if (ret < 0)
+ return ret;
+ if (ret & RS5C348_BIT_XSTP) {
+ txbuf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
+ txbuf[1] = 0;
+ ret = spi_write_then_read(spi, txbuf, 2, NULL, 0);
+ if (ret < 0)
+ return ret;
+ }
+
/* Transfer 5 bytes before writing SEC. This gives 31us for carry. */
txp = txbuf;
txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
@@ -102,6 +110,16 @@
u8 txbuf[5], rxbuf[7];
int ret;
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
+ if (ret < 0)
+ return ret;
+ if (ret & RS5C348_BIT_VDET)
+ dev_warn(&spi->dev, "voltage-low detected.\n");
+ if (ret & RS5C348_BIT_XSTP) {
+ dev_warn(&spi->dev, "oscillator-stop detected.\n");
+ return -EINVAL;
+ }
+
/* Transfer 5 byte befores reading SEC. This gives 31us for carry. */
txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
txbuf[1] = 0; /* dummy */
@@ -143,8 +161,6 @@
.set_time = rs5c348_rtc_set_time,
};
-static struct spi_driver rs5c348_driver;
-
static int rs5c348_probe(struct spi_device *spi)
{
int ret;
@@ -161,53 +177,27 @@
ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS));
if (ret < 0 || (ret & 0x80)) {
dev_err(&spi->dev, "not found.\n");
- goto kfree_exit;
+ return ret;
}
dev_info(&spi->dev, "spiclk %u KHz.\n",
(spi->max_speed_hz + 500) / 1000);
- /* turn RTC on if it was not on */
- ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
- if (ret < 0)
- goto kfree_exit;
- if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) {
- u8 buf[2];
- struct rtc_time tm;
- if (ret & RS5C348_BIT_VDET)
- dev_warn(&spi->dev, "voltage-low detected.\n");
- if (ret & RS5C348_BIT_XSTP)
- dev_warn(&spi->dev, "oscillator-stop detected.\n");
- rtc_time_to_tm(0, &tm); /* 1970/1/1 */
- ret = rs5c348_rtc_set_time(&spi->dev, &tm);
- if (ret < 0)
- goto kfree_exit;
- buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
- buf[1] = 0;
- ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
- if (ret < 0)
- goto kfree_exit;
- }
-
ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1));
if (ret < 0)
- goto kfree_exit;
+ return ret;
if (ret & RS5C348_BIT_24H)
pdata->rtc_24h = 1;
- rtc = devm_rtc_device_register(&spi->dev, rs5c348_driver.driver.name,
- &rs5c348_rtc_ops, THIS_MODULE);
-
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- goto kfree_exit;
- }
+ rtc = devm_rtc_allocate_device(&spi->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
pdata->rtc = rtc;
- return 0;
- kfree_exit:
- return ret;
+ rtc->ops = &rs5c348_rtc_ops;
+
+ return rtc_register_device(rtc);
}
static struct spi_driver rs5c348_driver = {
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index c503832..3bd6eaa 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An I2C driver for Ricoh RS5C372, R2025S/D and RV5C38[67] RTCs
*
* Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net>
* Copyright (C) 2006 Tower Technologies
* Copyright (C) 2008 Paul Mundt
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/i2c.h>
@@ -52,8 +49,10 @@
# 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 +211,27 @@
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 +259,7 @@
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 +278,32 @@
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 +561,25 @@
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
index 0000000..2b31666
--- /dev/null
+++ b/drivers/rtc/rtc-rv3028.c
@@ -0,0 +1,731 @@
+// 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, ®map_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");
diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c
index 3d6174e..4cdf658 100644
--- a/drivers/rtc/rtc-rv3029c2.c
+++ b/drivers/rtc/rtc-rv3029c2.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Micro Crystal RV-3029 / RV-3049 rtc class driver
*
@@ -5,11 +6,6 @@
* Michael Buesch <m@bues.ch>
*
* based on previously existing rtc class drivers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
@@ -282,13 +278,13 @@
static int rv3029_eeprom_write(struct device *dev, u8 reg,
u8 const buf[], size_t len)
{
- int ret;
+ int ret, err;
size_t i;
u8 tmp;
- ret = rv3029_eeprom_enter(dev);
- if (ret < 0)
- return ret;
+ err = rv3029_eeprom_enter(dev);
+ if (err < 0)
+ return err;
for (i = 0; i < len; i++, reg++) {
ret = rv3029_read_regs(dev, reg, &tmp, 1);
@@ -304,11 +300,11 @@
break;
}
- ret = rv3029_eeprom_exit(dev);
- if (ret < 0)
- return ret;
+ err = rv3029_eeprom_exit(dev);
+ if (err < 0)
+ return err;
- return 0;
+ return ret;
}
static int rv3029_eeprom_update_bits(struct device *dev,
diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c
index 29fc3d2..4960f0a 100644
--- a/drivers/rtc/rtc-rv8803.c
+++ b/drivers/rtc/rtc-rv8803.c
@@ -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 @@
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;
@@ -524,7 +517,7 @@
static int rv8803_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct i2c_adapter *adapter = client->adapter;
struct rv8803_data *rv8803;
int err, flags;
struct nvmem_config nvmem_cfg = {
@@ -571,9 +564,8 @@
dev_warn(&client->dev, "An alarm maybe have been missed.\n");
rv8803->rtc = devm_rtc_allocate_device(&client->dev);
- if (IS_ERR(rv8803->rtc)) {
+ if (IS_ERR(rv8803->rtc))
return PTR_ERR(rv8803->rtc);
- }
if (client->irq > 0) {
err = devm_request_threaded_irq(&client->dev, client->irq,
@@ -602,6 +594,8 @@
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;
@@ -615,6 +609,7 @@
static const struct i2c_device_id rv8803_id[] = {
{ "rv8803", rv_8803 },
+ { "rx8803", rv_8803 },
{ "rx8900", rx_8900 },
{ }
};
@@ -623,7 +618,11 @@
static const struct of_device_id rv8803_of_match[] = {
{
.compatible = "microcrystal,rv8803",
- .data = (void *)rx_8900
+ .data = (void *)rv_8803
+ },
+ {
+ .compatible = "epson,rx8803",
+ .data = (void *)rv_8803
},
{
.compatible = "epson,rx8900",
@@ -643,6 +642,6 @@
};
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");
diff --git a/drivers/rtc/rtc-rx4581.c b/drivers/rtc/rtc-rx4581.c
index c59a218..c092e04 100644
--- a/drivers/rtc/rtc-rx4581.c
+++ b/drivers/rtc/rtc-rx4581.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* drivers/rtc/rtc-rx4581.c
*
* written by Torben Hohn <torbenh@linutronix.de>
@@ -8,10 +9,6 @@
* Copyright (C) 2006 8D Technologies inc.
* Copyright (C) 2004 Compulab Ltd.
*
- * 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.
- *
* Driver for MAX6902 spi RTC
*
* and based on:
@@ -22,13 +19,8 @@
* Author: Martyn Welch <martyn.welch@ge.com>
* Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
*
- * 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.
- *
* Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC)
* Copyright 2005-06 Tower Technologies
- *
*/
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c
index 8e322d8..71e20a6 100644
--- a/drivers/rtc/rtc-rx6110.c
+++ b/drivers/rtc/rtc-rx6110.c
@@ -21,6 +21,8 @@
#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/spi/spi.h>
/* RX-6110 Register definitions */
@@ -114,9 +116,7 @@
*/
static int rx6110_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
{
- pr_debug("%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
- tm->tm_sec, tm->tm_min, tm->tm_hour,
- tm->tm_mday, tm->tm_mon, tm->tm_year);
+ pr_debug("%s: date %ptRr\n", __func__, tm);
/*
* The year in the RTC is a value between 0 and 99.
@@ -154,9 +154,7 @@
tm->tm_mon = bcd2bin(data[RTC_MONTH] & 0x1f) - 1;
tm->tm_year = bcd2bin(data[RTC_YEAR]) + 100;
- pr_debug("%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
- tm->tm_sec, tm->tm_min, tm->tm_hour,
- tm->tm_mday, tm->tm_mon, tm->tm_year);
+ pr_debug("%s: date %ptRr\n", __func__, tm);
/*
* The year in the RTC is a value between 0 and 99.
@@ -248,9 +246,7 @@
if (ret)
return ret;
- dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
- tm->tm_sec, tm->tm_min, tm->tm_hour,
- tm->tm_mday, tm->tm_mon, tm->tm_year);
+ dev_dbg(dev, "%s: date %ptRr\n", __func__, tm);
return 0;
}
@@ -385,9 +381,16 @@
};
MODULE_DEVICE_TABLE(spi, rx6110_id);
+static const struct of_device_id rx6110_spi_of_match[] = {
+ { .compatible = "epson,rx6110" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rx6110_spi_of_match);
+
static struct spi_driver rx6110_driver = {
.driver = {
.name = RX6110_DRIVER_NAME,
+ .of_match_table = of_match_ptr(rx6110_spi_of_match),
},
.probe = rx6110_probe,
.remove = rx6110_remove,
diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
index 7ddc22e..8102469 100644
--- a/drivers/rtc/rtc-rx8010.c
+++ b/drivers/rtc/rtc-rx8010.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Epson RTC module RX-8010 SJ
*
* Copyright(C) Timesys Corporation 2015
* Copyright(C) General Electric Company 2015
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/bcd.h>
@@ -437,7 +433,7 @@
static int rx8010_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct i2c_adapter *adapter = client->adapter;
struct rx8010_data *rx8010;
int err = 0;
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
index 41127ad..b9bda10 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Epson's RTC module RX-8025 SA/NB
*
@@ -13,10 +14,6 @@
* Code cleanup by Sergei Poselenov, <sposelenov@emcraft.com>
* Converted to new style by Wolfgang Grandegger <wg@grandegger.com>
* Alarm and periodic interrupt added by Dmitry Rakhchev <rda@emcraft.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.
*/
#include <linux/bcd.h>
#include <linux/bitops.h>
@@ -193,10 +190,7 @@
if (err)
return err;
- dev_dbg(dev, "%s: read 0x%02x 0x%02x "
- "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__,
- date[0], date[1], date[2], date[3], date[4],
- date[5], date[6]);
+ dev_dbg(dev, "%s: read %7ph\n", __func__, date);
dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f);
dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f);
@@ -210,9 +204,7 @@
dt->tm_mon = bcd2bin(date[RX8025_REG_MONTH] & 0x1f) - 1;
dt->tm_year = bcd2bin(date[RX8025_REG_YEAR]) + 100;
- dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
- dt->tm_sec, dt->tm_min, dt->tm_hour,
- dt->tm_mday, dt->tm_mon, dt->tm_year);
+ dev_dbg(dev, "%s: date %ptRr\n", __func__, dt);
return 0;
}
@@ -243,10 +235,7 @@
date[RX8025_REG_MONTH] = bin2bcd(dt->tm_mon + 1);
date[RX8025_REG_YEAR] = bin2bcd(dt->tm_year - 100);
- dev_dbg(dev,
- "%s: write 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
- __func__,
- date[0], date[1], date[2], date[3], date[4], date[5], date[6]);
+ dev_dbg(dev, "%s: write %7ph\n", __func__, date);
ret = rx8025_write_regs(rx8025->client, RX8025_REG_SEC, 7, date);
if (ret < 0)
@@ -319,10 +308,7 @@
t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12
+ (ald[1] & 0x20 ? 12 : 0);
- dev_dbg(dev, "%s: date: %ds %dm %dh %dmd %dm %dy\n",
- __func__,
- t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
- t->time.tm_mday, t->time.tm_mon, t->time.tm_year);
+ dev_dbg(dev, "%s: date: %ptRr\n", __func__, &t->time);
t->enabled = !!(rx8025->ctrl1 & RX8025_BIT_CTRL1_DALE);
t->pending = (ctrl2 & RX8025_BIT_CTRL2_DAFG) && t->enabled;
@@ -515,7 +501,7 @@
static int rx8025_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct i2c_adapter *adapter = client->adapter;
struct rx8025_data *rx8025;
int err = 0;
diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c
index eac8821..490f70f 100644
--- a/drivers/rtc/rtc-rx8581.c
+++ b/drivers/rtc/rtc-rx8581.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An I2C driver for the Epson RX8581 RTC
*
* Author: Martyn Welch <martyn.welch@ge.com>
* Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
*
- * 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.
- *
* Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC)
* Copyright 2005-06 Tower Technologies
*/
@@ -15,6 +12,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>
@@ -51,11 +50,19 @@
#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 +188,103 @@
.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 +298,14 @@
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 +315,9 @@
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 +333,5 @@
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");
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c
index 77feb60..da34cfd 100644
--- a/drivers/rtc/rtc-s35390a.c
+++ b/drivers/rtc/rtc-s35390a.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Seiko Instruments S-35390A RTC Driver
*
* Copyright (c) 2007 Byron Bradley
- *
- * 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.
*/
#include <linux/module.h>
@@ -36,21 +32,22 @@
#define S35390A_ALRM_BYTE_MINS 2
/* flags for STATUS1 */
-#define S35390A_FLAG_POC 0x01
-#define S35390A_FLAG_BLD 0x02
-#define S35390A_FLAG_INT2 0x04
-#define S35390A_FLAG_24H 0x40
-#define S35390A_FLAG_RESET 0x80
+#define S35390A_FLAG_POC BIT(0)
+#define S35390A_FLAG_BLD BIT(1)
+#define S35390A_FLAG_INT2 BIT(2)
+#define S35390A_FLAG_24H BIT(6)
+#define S35390A_FLAG_RESET BIT(7)
/* flag for STATUS2 */
-#define S35390A_FLAG_TEST 0x01
+#define S35390A_FLAG_TEST BIT(0)
-#define S35390A_INT2_MODE_MASK 0xF0
-
+/* INT2 pin output mode */
+#define S35390A_INT2_MODE_MASK 0x0E
#define S35390A_INT2_MODE_NOINTR 0x00
-#define S35390A_INT2_MODE_FREQ 0x10
-#define S35390A_INT2_MODE_ALARM 0x40
-#define S35390A_INT2_MODE_PMIN_EDG 0x20
+#define S35390A_INT2_MODE_ALARM BIT(1) /* INT2AE */
+#define S35390A_INT2_MODE_PMIN_EDG BIT(2) /* INT2ME */
+#define S35390A_INT2_MODE_FREQ BIT(3) /* INT2FE */
+#define S35390A_INT2_MODE_PMIN (BIT(3) | BIT(2)) /* INT2FE | INT2ME */
static const struct i2c_device_id s35390a_id[] = {
{ "s35390a", 0 },
@@ -108,7 +105,7 @@
static int s35390a_init(struct s35390a *s35390a)
{
- char buf;
+ u8 buf;
int ret;
unsigned initcount = 0;
@@ -288,6 +285,9 @@
alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday,
alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday);
+ if (alm->time.tm_sec != 0)
+ dev_warn(&client->dev, "Alarms are only supported on a per minute basis!\n");
+
/* disable interrupt (which deasserts the irq line) */
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
if (err < 0)
@@ -303,9 +303,6 @@
else
sts = S35390A_INT2_MODE_NOINTR;
- /* This chip expects the bits of each byte to be in reverse order */
- sts = bitrev8(sts);
-
/* set interupt mode*/
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
if (err < 0)
@@ -343,7 +340,7 @@
if (err < 0)
return err;
- if ((bitrev8(sts) & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) {
+ if ((sts & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) {
/*
* When the alarm isn't enabled, the register to configure
* the alarm time isn't accessible.
@@ -435,39 +432,34 @@
unsigned int i;
struct s35390a *s35390a;
char buf, status1;
+ struct device *dev = &client->dev;
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- err = -ENODEV;
- goto exit;
- }
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
- s35390a = devm_kzalloc(&client->dev, sizeof(struct s35390a),
- GFP_KERNEL);
- if (!s35390a) {
- err = -ENOMEM;
- goto exit;
- }
+ s35390a = devm_kzalloc(dev, sizeof(struct s35390a), GFP_KERNEL);
+ if (!s35390a)
+ return -ENOMEM;
s35390a->client[0] = client;
i2c_set_clientdata(client, s35390a);
/* This chip uses multiple addresses, use dummy devices for them */
for (i = 1; i < 8; ++i) {
- s35390a->client[i] = i2c_new_dummy(client->adapter,
- client->addr + i);
- if (!s35390a->client[i]) {
- dev_err(&client->dev, "Address %02x unavailable\n",
- client->addr + i);
- err = -EBUSY;
- goto exit_dummy;
+ s35390a->client[i] = devm_i2c_new_dummy_device(dev,
+ client->adapter,
+ client->addr + i);
+ if (IS_ERR(s35390a->client[i])) {
+ dev_err(dev, "Address %02x unavailable\n",
+ client->addr + i);
+ return PTR_ERR(s35390a->client[i]);
}
}
err_read = s35390a_read_status(s35390a, &status1);
if (err_read < 0) {
- err = err_read;
- dev_err(&client->dev, "error resetting chip\n");
- goto exit_dummy;
+ dev_err(dev, "error resetting chip\n");
+ return err_read;
}
if (status1 & S35390A_FLAG_24H)
@@ -480,52 +472,32 @@
buf = 0;
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1);
if (err < 0) {
- dev_err(&client->dev, "error disabling alarm");
- goto exit_dummy;
+ dev_err(dev, "error disabling alarm");
+ return err;
}
} else {
err = s35390a_disable_test_mode(s35390a);
if (err < 0) {
- dev_err(&client->dev, "error disabling test mode\n");
- goto exit_dummy;
+ dev_err(dev, "error disabling test mode\n");
+ return err;
}
}
- device_set_wakeup_capable(&client->dev, 1);
+ device_set_wakeup_capable(dev, 1);
- s35390a->rtc = devm_rtc_device_register(&client->dev,
- s35390a_driver.driver.name,
- &s35390a_rtc_ops, THIS_MODULE);
+ s35390a->rtc = devm_rtc_device_register(dev, s35390a_driver.driver.name,
+ &s35390a_rtc_ops, THIS_MODULE);
- if (IS_ERR(s35390a->rtc)) {
- err = PTR_ERR(s35390a->rtc);
- goto exit_dummy;
- }
+ if (IS_ERR(s35390a->rtc))
+ return PTR_ERR(s35390a->rtc);
+
+ /* supports per-minute alarms only, therefore set uie_unsupported */
+ s35390a->rtc->uie_unsupported = 1;
if (status1 & S35390A_FLAG_INT2)
rtc_update_irq(s35390a->rtc, 1, RTC_AF);
return 0;
-
-exit_dummy:
- for (i = 1; i < 8; ++i)
- if (s35390a->client[i])
- i2c_unregister_device(s35390a->client[i]);
-
-exit:
- return err;
-}
-
-static int s35390a_remove(struct i2c_client *client)
-{
- unsigned int i;
- struct s35390a *s35390a = i2c_get_clientdata(client);
-
- for (i = 1; i < 8; ++i)
- if (s35390a->client[i])
- i2c_unregister_device(s35390a->client[i]);
-
- return 0;
}
static struct i2c_driver s35390a_driver = {
@@ -534,7 +506,6 @@
.of_match_table = of_match_ptr(s35390a_of_match),
},
.probe = s35390a_probe,
- .remove = s35390a_remove,
.id_table = s35390a_id,
};
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 75c8c50..7801249 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* drivers/rtc/rtc-s3c.c
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
@@ -7,10 +8,6 @@
* Ben Dooks, <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
- * 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.
- *
* S3C2410/S3C2440/S3C24XX Internal RTC Driver
*/
@@ -26,6 +23,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 +37,7 @@
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 +45,7 @@
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 +68,27 @@
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 +116,7 @@
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 +133,19 @@
writeb(tmp, info->base + S3C2410_RTCALM);
+ spin_lock_irqsave(&info->alarm_lock, flags);
+
+ if (info->alarm_enabled && !enabled)
+ s3c_rtc_disable_clk(info);
+ else if (!info->alarm_enabled && enabled)
+ ret = s3c_rtc_enable_clk(info);
+
+ info->alarm_enabled = enabled;
+ spin_unlock_irqrestore(&info->alarm_lock, flags);
+
s3c_rtc_disable_clk(info);
- if (enabled) {
- ret = s3c_rtc_enable_clk(info);
- if (ret)
- return ret;
- } else {
- s3c_rtc_disable_clk(info);
- }
-
- return 0;
+ return ret;
}
/* Set RTC frequency */
@@ -225,13 +209,9 @@
s3c_rtc_disable_clk(info);
rtc_tm->tm_year += 100;
-
- dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
- 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
- rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
-
rtc_tm->tm_mon -= 1;
+ dev_dbg(dev, "read time %ptR\n", rtc_tm);
return 0;
}
@@ -241,9 +221,7 @@
int year = tm->tm_year - 100;
int ret;
- dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "set time %ptR\n", tm);
/* we get around y2k by simply not supporting it */
@@ -292,10 +270,7 @@
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
- dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
- alm_en,
- 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
- alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+ dev_dbg(dev, "read alarm %d, %ptR\n", alm_en, alm_tm);
/* decode the alarm enable field */
if (alm_en & S3C2410_RTCALM_SECEN)
@@ -327,12 +302,8 @@
struct rtc_time *tm = &alrm->time;
unsigned int alrm_en;
int ret;
- int year = tm->tm_year - 100;
- dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
- alrm->enabled,
- 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n", alrm->enabled, tm);
ret = s3c_rtc_enable_clk(info);
if (ret)
@@ -356,11 +327,6 @@
writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
}
- if (year < 100 && year >= 0) {
- alrm_en |= S3C2410_RTCALM_YEAREN;
- writeb(bin2bcd(year), info->base + S3C2410_ALMYEAR);
- }
-
if (tm->tm_mon < 12 && tm->tm_mon >= 0) {
alrm_en |= S3C2410_RTCALM_MONEN;
writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON);
@@ -375,10 +341,10 @@
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;
}
@@ -474,16 +440,6 @@
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;
@@ -497,27 +453,23 @@
/* find the IRQs */
info->irq_tick = platform_get_irq(pdev, 1);
- if (info->irq_tick < 0) {
- dev_err(&pdev->dev, "no irq for rtc tick\n");
+ if (info->irq_tick < 0)
return info->irq_tick;
- }
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);
info->irq_alarm = platform_get_irq(pdev, 0);
- if (info->irq_alarm < 0) {
- dev_err(&pdev->dev, "no irq for alarm\n");
+ if (info->irq_alarm < 0)
return info->irq_alarm;
- }
dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
info->irq_tick, info->irq_alarm);
@@ -609,6 +561,8 @@
s3c_rtc_setfreq(info, 1);
+ s3c_rtc_disable_clk(info);
+
return 0;
err_nortc:
diff --git a/drivers/rtc/rtc-s3c.h b/drivers/rtc/rtc-s3c.h
index 004b61a..3552914 100644
--- a/drivers/rtc/rtc-s3c.h
+++ b/drivers/rtc/rtc-s3c.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
* http://www.simtec.co.uk/products/SWLINUX/
*
- * 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.
- *
* S3C2410 Internal RTC register definition
*/
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
index 6495f84..eb9dde4 100644
--- a/drivers/rtc/rtc-s5m.c
+++ b/drivers/rtc/rtc-s5m.c
@@ -406,9 +406,7 @@
return -EINVAL;
}
- dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
- 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+ dev_dbg(dev, "%s: %ptR(%d)\n", __func__, tm, tm->tm_wday);
return 0;
}
@@ -436,9 +434,7 @@
if (ret < 0)
return ret;
- dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
- 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+ dev_dbg(dev, "%s: %ptR(%d)\n", __func__, tm, tm->tm_wday);
ret = regmap_raw_write(info->regmap, info->regs->time, data,
info->regs->regs_count);
@@ -490,11 +486,7 @@
return -EINVAL;
}
- dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
- 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
- alrm->time.tm_mday, alrm->time.tm_hour,
- alrm->time.tm_min, alrm->time.tm_sec,
- alrm->time.tm_wday);
+ dev_dbg(dev, "%s: %ptR(%d)\n", __func__, &alrm->time, alrm->time.tm_wday);
ret = s5m_check_peding_alarm_interrupt(info, alrm);
@@ -513,9 +505,7 @@
return ret;
s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
- dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
- 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
+ dev_dbg(info->dev, "%s: %ptR(%d)\n", __func__, &tm, tm.tm_wday);
switch (info->device_type) {
case S5M8763X:
@@ -558,9 +548,7 @@
return ret;
s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
- dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
- 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
+ dev_dbg(info->dev, "%s: %ptR(%d)\n", __func__, &tm, tm.tm_wday);
switch (info->device_type) {
case S5M8763X:
@@ -620,10 +608,7 @@
return -EINVAL;
}
- dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
- 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
- alrm->time.tm_mday, alrm->time.tm_hour, alrm->time.tm_min,
- alrm->time.tm_sec, alrm->time.tm_wday);
+ dev_dbg(dev, "%s: %ptR(%d)\n", __func__, &alrm->time, alrm->time.tm_wday);
ret = s5m_rtc_stop_alarm(info);
if (ret < 0)
@@ -775,10 +760,10 @@
return -ENODEV;
}
- info->i2c = i2c_new_dummy(s5m87xx->i2c->adapter, RTC_I2C_ADDR);
- if (!info->i2c) {
+ info->i2c = i2c_new_dummy_device(s5m87xx->i2c->adapter, RTC_I2C_ADDR);
+ if (IS_ERR(info->i2c)) {
dev_err(&pdev->dev, "Failed to allocate I2C for RTC\n");
- return -ENODEV;
+ return PTR_ERR(info->i2c);
}
info->regmap = devm_regmap_init_i2c(info->i2c, regmap_cfg);
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index 304d905..86fa723 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx
*
@@ -14,11 +15,6 @@
*
* Converted to the RTC subsystem and Driver Model
* by Richard Purdie <rpurdie@rpsys.net>
- *
- * 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.
*/
#include <linux/platform_device.h>
diff --git a/drivers/rtc/rtc-sc27xx.c b/drivers/rtc/rtc-sc27xx.c
index deea5c3..b956768 100644
--- a/drivers/rtc/rtc-sc27xx.c
+++ b/drivers/rtc/rtc-sc27xx.c
@@ -129,19 +129,6 @@
SPRD_RTC_ALM_INT_MASK);
}
-static int sprd_rtc_disable_ints(struct sprd_rtc *rtc)
-{
- int ret;
-
- ret = regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
- SPRD_RTC_INT_MASK, 0);
- if (ret)
- return ret;
-
- return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
- SPRD_RTC_INT_MASK);
-}
-
static int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock)
{
int ret;
@@ -151,7 +138,7 @@
if (ret)
return ret;
- val &= ~(SPRD_RTC_ALMLOCK_MASK | SPRD_RTC_POWEROFF_ALM_FLAG);
+ val &= ~SPRD_RTC_ALMLOCK_MASK;
if (lock)
val |= SPRD_RTC_ALM_LOCK;
else
@@ -172,7 +159,8 @@
return ret;
}
- return 0;
+ return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+ SPRD_RTC_SPG_UPD_EN);
}
static int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type,
@@ -427,10 +415,14 @@
u32 val;
/*
- * If aie_timer is enabled, we should get the normal alarm time.
+ * Before RTC device is registered, it will check to see if there is an
+ * alarm already set in RTC hardware, and we always read the normal
+ * alarm at this time.
+ *
+ * Or if aie_timer is enabled, we should get the normal alarm time.
* Otherwise we should get auxiliary alarm time.
*/
- if (rtc->rtc && rtc->rtc->aie_timer.enabled == 0)
+ if (rtc->rtc && rtc->rtc->registered && rtc->rtc->aie_timer.enabled == 0)
return sprd_rtc_read_aux_alarm(dev, alrm);
ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs);
@@ -575,6 +567,32 @@
return 0;
}
+static int sprd_rtc_check_alarm_int(struct sprd_rtc *rtc)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val);
+ if (ret)
+ return ret;
+
+ /*
+ * The SPRD_RTC_INT_EN register is not put in always-power-on region
+ * supplied by VDDRTC, so we should check if we need enable the alarm
+ * interrupt when system booting.
+ *
+ * If we have set SPRD_RTC_POWEROFF_ALM_FLAG which is saved in
+ * always-power-on region, that means we have set one alarm last time,
+ * so we should enable the alarm interrupt to help RTC core to see if
+ * there is an alarm already set in RTC hardware.
+ */
+ if (!(val & SPRD_RTC_POWEROFF_ALM_FLAG))
+ return 0;
+
+ return regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
+ SPRD_RTC_ALARM_EN, SPRD_RTC_ALARM_EN);
+}
+
static int sprd_rtc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -596,10 +614,8 @@
}
rtc->irq = platform_get_irq(pdev, 0);
- if (rtc->irq < 0) {
- dev_err(&pdev->dev, "failed to get RTC irq number\n");
+ if (rtc->irq < 0)
return rtc->irq;
- }
rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc->rtc))
@@ -608,10 +624,10 @@
rtc->dev = &pdev->dev;
platform_set_drvdata(pdev, rtc);
- /* clear all RTC interrupts and disable all RTC interrupts */
- ret = sprd_rtc_disable_ints(rtc);
+ /* check if we need set the alarm interrupt */
+ ret = sprd_rtc_check_alarm_int(rtc);
if (ret) {
- dev_err(&pdev->dev, "failed to disable RTC interrupts\n");
+ dev_err(&pdev->dev, "failed to check RTC alarm interrupt\n");
return ret;
}
@@ -631,16 +647,17 @@
return ret;
}
+ device_init_wakeup(&pdev->dev, 1);
+
rtc->rtc->ops = &sprd_rtc_ops;
rtc->rtc->range_min = 0;
rtc->rtc->range_max = 5662310399LL;
ret = rtc_register_device(rtc->rtc);
if (ret) {
- dev_err(&pdev->dev, "failed to register rtc device\n");
+ device_init_wakeup(&pdev->dev, 0);
return ret;
}
- device_init_wakeup(&pdev->dev, 1);
return 0;
}
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
new file mode 100644
index 0000000..a7aa943
--- /dev/null
+++ b/drivers/rtc/rtc-sd3078.c
@@ -0,0 +1,229 @@
+// 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, ®map_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)
+ 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");
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 51ba414..579b3ff 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* SuperH On-Chip RTC Support
*
@@ -9,10 +10,6 @@
*
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
* Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
- *
- * 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.
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@@ -279,6 +276,9 @@
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int sec128, sec2, yr, yr100, cf_bit;
+ if (!(readb(rtc->regbase + RCR2) & RCR2_RTCEN))
+ return -EINVAL;
+
do {
unsigned int tmp;
@@ -377,7 +377,7 @@
static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off)
{
unsigned int byte;
- int value = 0xff; /* return 0xff for ignored values */
+ int value = -1; /* return -1 for ignored values */
byte = readb(rtc->regbase + reg_off);
if (byte & AR_ENB) {
@@ -469,7 +469,6 @@
{
struct sh_rtc *rtc;
struct resource *res;
- struct rtc_time r;
char clk_name[6];
int clk_id, ret;
@@ -531,6 +530,10 @@
rtc->clk = NULL;
}
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
clk_enable(rtc->clk);
rtc->capabilities = RTC_DEF_CAPABILITIES;
@@ -594,21 +597,21 @@
sh_rtc_setaie(&pdev->dev, 0);
sh_rtc_setcie(&pdev->dev, 0);
- rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "sh",
- &sh_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev)) {
- ret = PTR_ERR(rtc->rtc_dev);
- goto err_unmap;
- }
-
+ rtc->rtc_dev->ops = &sh_rtc_ops;
rtc->rtc_dev->max_user_freq = 256;
- /* reset rtc to epoch 0 if time is invalid */
- if (rtc_read_time(rtc->rtc_dev, &r) < 0) {
- rtc_time_to_tm(0, &r);
- rtc_set_time(rtc->rtc_dev, &r);
+ if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) {
+ rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_1900;
+ rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_9999;
+ } else {
+ rtc->rtc_dev->range_min = mktime64(1999, 1, 1, 0, 0, 0);
+ rtc->rtc_dev->range_max = mktime64(2098, 12, 31, 23, 59, 59);
}
+ ret = rtc_register_device(rtc->rtc_dev);
+ if (ret)
+ goto err_unmap;
+
device_init_wakeup(&pdev->dev, 1);
return 0;
@@ -681,5 +684,5 @@
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, "
"Jamie Lenehan <lenehan@twibble.org>, "
"Angelo Castello <angelo.castello@st.com>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/rtc/rtc-sirfsoc.c b/drivers/rtc/rtc-sirfsoc.c
index 2a9e151..c759c55 100644
--- a/drivers/rtc/rtc-sirfsoc.c
+++ b/drivers/rtc/rtc-sirfsoc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SiRFSoC Real Time Clock interface for Linux
*
* Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
*/
#include <linux/module.h>
@@ -279,7 +278,7 @@
{},
};
-const struct regmap_config sysrtc_regmap_config = {
+static const struct regmap_config sysrtc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.fast_io = true,
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
index b2483a7..757f4da 100644
--- a/drivers/rtc/rtc-snvs.c
+++ b/drivers/rtc/rtc-snvs.c
@@ -9,6 +9,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/rtc.h>
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
@@ -150,7 +151,7 @@
struct snvs_rtc_data *data = dev_get_drvdata(dev);
unsigned long time = rtc_read_lp_counter(data);
- rtc_time_to_tm(time, tm);
+ rtc_time64_to_tm(time, tm);
return 0;
}
@@ -158,11 +159,9 @@
static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
- unsigned long time;
+ unsigned long time = rtc_tm_to_time64(tm);
int ret;
- rtc_tm_to_time(tm, &time);
-
/* Disable RTC first */
ret = snvs_rtc_enable(data, false);
if (ret)
@@ -184,7 +183,7 @@
u32 lptar, lpsr;
regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar);
- rtc_time_to_tm(lptar, &alrm->time);
+ rtc_time64_to_tm(lptar, &alrm->time);
regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
@@ -206,12 +205,9 @@
static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
- struct rtc_time *alrm_tm = &alrm->time;
- unsigned long time;
+ unsigned long time = rtc_tm_to_time64(&alrm->time);
int ret;
- rtc_tm_to_time(alrm_tm, &time);
-
regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
ret = rtc_write_sync_lp(data);
if (ret)
@@ -239,6 +235,9 @@
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 +252,9 @@
/* 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;
}
@@ -265,7 +267,6 @@
static int snvs_rtc_probe(struct platform_device *pdev)
{
struct snvs_rtc_data *data;
- struct resource *res;
int ret;
void __iomem *mmio;
@@ -273,13 +274,16 @@
if (!data)
return -ENOMEM;
+ data->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(data->rtc))
+ return PTR_ERR(data->rtc);
+
data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
if (IS_ERR(data->regmap)) {
dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mmio = devm_ioremap_resource(&pdev->dev, res);
+ mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
@@ -326,6 +330,9 @@
}
device_init_wakeup(&pdev->dev, true);
+ ret = dev_pm_set_wake_irq(&pdev->dev, data->irq);
+ if (ret)
+ dev_err(&pdev->dev, "failed to enable irq wake\n");
ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
IRQF_SHARED, "rtc alarm", &pdev->dev);
@@ -335,10 +342,10 @@
goto error_rtc_device_register;
}
- data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &snvs_rtc_ops, THIS_MODULE);
- if (IS_ERR(data->rtc)) {
- ret = PTR_ERR(data->rtc);
+ data->rtc->ops = &snvs_rtc_ops;
+ data->rtc->range_max = U32_MAX;
+ ret = rtc_register_device(data->rtc);
+ if (ret) {
dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
goto error_rtc_device_register;
}
@@ -352,18 +359,7 @@
return ret;
}
-#ifdef CONFIG_PM_SLEEP
-static int snvs_rtc_suspend(struct device *dev)
-{
- struct snvs_rtc_data *data = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- return enable_irq_wake(data->irq);
-
- return 0;
-}
-
-static int snvs_rtc_suspend_noirq(struct device *dev)
+static int __maybe_unused snvs_rtc_suspend_noirq(struct device *dev)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
@@ -373,17 +369,7 @@
return 0;
}
-static int snvs_rtc_resume(struct device *dev)
-{
- struct snvs_rtc_data *data = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- return disable_irq_wake(data->irq);
-
- return 0;
-}
-
-static int snvs_rtc_resume_noirq(struct device *dev)
+static int __maybe_unused snvs_rtc_resume_noirq(struct device *dev)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
@@ -394,20 +380,9 @@
}
static const struct dev_pm_ops snvs_rtc_pm_ops = {
- .suspend = snvs_rtc_suspend,
- .suspend_noirq = snvs_rtc_suspend_noirq,
- .resume = snvs_rtc_resume,
- .resume_noirq = snvs_rtc_resume_noirq,
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(snvs_rtc_suspend_noirq, snvs_rtc_resume_noirq)
};
-#define SNVS_RTC_PM_OPS (&snvs_rtc_pm_ops)
-
-#else
-
-#define SNVS_RTC_PM_OPS NULL
-
-#endif
-
static const struct of_device_id snvs_dt_ids[] = {
{ .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
{ /* sentinel */ }
@@ -417,7 +392,7 @@
static struct platform_driver snvs_rtc_driver = {
.driver = {
.name = "snvs_rtc",
- .pm = SNVS_RTC_PM_OPS,
+ .pm = &snvs_rtc_pm_ops,
.of_match_table = snvs_dt_ids,
},
.probe = snvs_rtc_probe,
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
index 0567944..9f23b24 100644
--- a/drivers/rtc/rtc-spear.c
+++ b/drivers/rtc/rtc-spear.c
@@ -358,10 +358,8 @@
/* alarm irqs */
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no update irq?\n");
+ if (irq < 0)
return irq;
- }
status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name,
config);
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
index bee75ca..49474a3 100644
--- a/drivers/rtc/rtc-st-lpc.c
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
*
@@ -7,11 +8,6 @@
* Lee Jones <lee.jones@linaro.org> for STMicroelectronics
*
* Based on the original driver written by Stuart Menefy.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
*/
#include <linux/clk.h>
@@ -166,10 +162,6 @@
now_secs = rtc_tm_to_time64(&now);
alarm_secs = rtc_tm_to_time64(&t->time);
- /* Invalid alarm time */
- if (now_secs > alarm_secs)
- return -EINVAL;
-
memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
/* Now many secs to fire */
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index fccbecb..a833ebc 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* A RTC driver for the Simtek STK17TA8
*
@@ -5,10 +6,6 @@
*
* Based on the DS1553 driver from
* Atsushi Nemoto <anemo@mba.ocn.ne.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/bcd.h>
diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
index c5908cf..2999e33 100644
--- a/drivers/rtc/rtc-stm32.c
+++ b/drivers/rtc/rtc-stm32.c
@@ -519,11 +519,7 @@
/* Write to Alarm register */
writel_relaxed(alrmar, rtc->base + regs->alrmar);
- if (alrm->enabled)
- stm32_rtc_alarm_irq_enable(dev, 1);
- else
- stm32_rtc_alarm_irq_enable(dev, 0);
-
+ stm32_rtc_alarm_irq_enable(dev, alrm->enabled);
end:
stm32_rtc_wpr_lock(rtc);
@@ -780,7 +776,6 @@
rtc->irq_alarm = platform_get_irq(pdev, 0);
if (rtc->irq_alarm <= 0) {
- dev_err(&pdev->dev, "no alarm irq\n");
ret = rtc->irq_alarm;
goto err;
}
@@ -788,11 +783,14 @@
ret = device_init_wakeup(&pdev->dev, true);
if (rtc->data->has_wakeirq) {
rtc->wakeirq_alarm = platform_get_irq(pdev, 1);
- if (rtc->wakeirq_alarm <= 0)
- ret = rtc->wakeirq_alarm;
- else
+ if (rtc->wakeirq_alarm > 0) {
ret = dev_pm_set_dedicated_wake_irq(&pdev->dev,
rtc->wakeirq_alarm);
+ } else {
+ ret = rtc->wakeirq_alarm;
+ if (rtc->wakeirq_alarm == -EPROBE_DEFER)
+ goto err;
+ }
}
if (ret)
dev_warn(&pdev->dev, "alarm can't wake up the system: %d", ret);
diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c
index b76318f..ff6488b 100644
--- a/drivers/rtc/rtc-stmp3xxx.c
+++ b/drivers/rtc/rtc-stmp3xxx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale STMP37XX/STMP378X Real Time Clock driver
*
@@ -8,15 +9,6 @@
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
* Copyright 2011 Wolfram Sang, Pengutronix e.K.
*/
-
-/*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
@@ -160,15 +152,15 @@
if (ret)
return ret;
- rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm);
+ rtc_time64_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm);
return 0;
}
-static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t)
+static int stmp3xxx_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
{
struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
- writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS);
+ writel(rtc_tm_to_time64(rtc_tm), rtc_data->io + STMP3XXX_RTC_SECONDS);
return stmp3xxx_wait_time(rtc_data);
}
@@ -214,17 +206,15 @@
{
struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
- rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time);
+ rtc_time64_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time);
return 0;
}
static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
- unsigned long t;
struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
- rtc_tm_to_time(&alm->time, &t);
- writel(t, rtc_data->io + STMP3XXX_RTC_ALARM);
+ writel(rtc_tm_to_time64(&alm->time), rtc_data->io + STMP3XXX_RTC_ALARM);
stmp3xxx_alarm_irq_enable(dev, alm->enabled);
@@ -235,7 +225,7 @@
.alarm_irq_enable =
stmp3xxx_alarm_irq_enable,
.read_time = stmp3xxx_rtc_gettime,
- .set_mmss = stmp3xxx_rtc_set_mmss,
+ .set_time = stmp3xxx_rtc_settime,
.read_alarm = stmp3xxx_rtc_read_alarm,
.set_alarm = stmp3xxx_rtc_set_alarm,
};
@@ -361,8 +351,7 @@
STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR);
- rtc_data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &stmp3xxx_rtc_ops, THIS_MODULE);
+ rtc_data->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc_data->rtc))
return PTR_ERR(rtc_data->rtc);
@@ -374,6 +363,13 @@
return err;
}
+ rtc_data->rtc->ops = &stmp3xxx_rtc_ops;
+ rtc_data->rtc->range_max = U32_MAX;
+
+ err = rtc_register_device(rtc_data->rtc);
+ if (err)
+ return err;
+
stmp3xxx_wdt_register(pdev);
return 0;
}
diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c
index 11bc562..036463d 100644
--- a/drivers/rtc/rtc-sun4v.c
+++ b/drivers/rtc/rtc-sun4v.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
*
* Author: David S. Miller
- * License: GPL
*
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
@@ -39,7 +39,7 @@
static int sun4v_read_time(struct device *dev, struct rtc_time *tm)
{
- rtc_time_to_tm(hypervisor_get_time(), tm);
+ rtc_time64_to_tm(hypervisor_get_time(), tm);
return 0;
}
@@ -66,14 +66,7 @@
static int sun4v_set_time(struct device *dev, struct rtc_time *tm)
{
- unsigned long secs;
- int err;
-
- err = rtc_tm_to_time(tm, &secs);
- if (err)
- return err;
-
- return hypervisor_set_time(secs);
+ return hypervisor_set_time(rtc_tm_to_time64(tm));
}
static const struct rtc_class_ops sun4v_rtc_ops = {
@@ -85,13 +78,15 @@
{
struct rtc_device *rtc;
- rtc = devm_rtc_device_register(&pdev->dev, "sun4v",
- &sun4v_rtc_ops, THIS_MODULE);
+ rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
+ rtc->ops = &sun4v_rtc_ops;
+ rtc->range_max = U64_MAX;
platform_set_drvdata(pdev, rtc);
- return 0;
+
+ return rtc_register_device(rtc);
}
static struct platform_driver sun4v_rtc_driver = {
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index 2cd5a7b..5e2bd9f 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* An RTC driver for Allwinner A31/A23
*
@@ -8,16 +9,6 @@
* An RTC driver for Allwinner A10/A20
*
* Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com>
- *
- * 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/clk.h>
@@ -41,9 +32,11 @@
/* Control register */
#define SUN6I_LOSC_CTRL 0x0000
#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16)
+#define SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS BIT(15)
#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
+#define SUN6I_LOSC_CTRL_EXT_LOSC_EN BIT(4)
#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0)
#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
@@ -118,9 +111,33 @@
#define SUN6I_YEAR_MAX 2033
#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900)
+/*
+ * There are other differences between models, including:
+ *
+ * - number of GPIO pins that can be configured to hold a certain level
+ * - crypto-key related registers (H5, H6)
+ * - boot process related (super standby, secondary processor entry address)
+ * registers (R40, H6)
+ * - SYS power domain controls (R40)
+ * - DCXO controls (H6)
+ * - RC oscillator calibration (H6)
+ *
+ * These functions are not covered by this driver.
+ */
+struct sun6i_rtc_clk_data {
+ unsigned long rc_osc_rate;
+ unsigned int fixed_prescaler : 16;
+ unsigned int has_prescaler : 1;
+ unsigned int has_out_clk : 1;
+ unsigned int export_iosc : 1;
+ unsigned int has_losc_en : 1;
+ unsigned int has_auto_swt : 1;
+};
+
struct sun6i_rtc_dev {
struct rtc_device *rtc;
struct device *dev;
+ const struct sun6i_rtc_clk_data *data;
void __iomem *base;
int irq;
unsigned long alarm;
@@ -139,14 +156,19 @@
unsigned long parent_rate)
{
struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw);
- u32 val;
+ u32 val = 0;
val = readl(rtc->base + SUN6I_LOSC_CTRL);
if (val & SUN6I_LOSC_CTRL_EXT_OSC)
return parent_rate;
- val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL);
- val &= GENMASK(4, 0);
+ if (rtc->data->fixed_prescaler)
+ parent_rate /= rtc->data->fixed_prescaler;
+
+ if (rtc->data->has_prescaler) {
+ val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL);
+ val &= GENMASK(4, 0);
+ }
return parent_rate / (val + 1);
}
@@ -172,6 +194,10 @@
val &= ~SUN6I_LOSC_CTRL_EXT_OSC;
val |= SUN6I_LOSC_CTRL_KEY;
val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0;
+ if (rtc->data->has_losc_en) {
+ val &= ~SUN6I_LOSC_CTRL_EXT_LOSC_EN;
+ val |= index ? SUN6I_LOSC_CTRL_EXT_LOSC_EN : 0;
+ }
writel(val, rtc->base + SUN6I_LOSC_CTRL);
spin_unlock_irqrestore(&rtc->lock, flags);
@@ -185,22 +211,26 @@
.set_parent = sun6i_rtc_osc_set_parent,
};
-static void __init sun6i_rtc_clk_init(struct device_node *node)
+static void __init sun6i_rtc_clk_init(struct device_node *node,
+ const struct sun6i_rtc_clk_data *data)
{
struct clk_hw_onecell_data *clk_data;
struct sun6i_rtc_dev *rtc;
struct clk_init_data init = {
.ops = &sun6i_rtc_osc_ops,
+ .name = "losc",
};
+ const char *iosc_name = "rtc-int-osc";
const char *clkout_name = "osc32k-out";
const char *parents[2];
+ u32 reg;
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return;
- clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2),
- GFP_KERNEL);
+ rtc->data = data;
+ clk_data = kzalloc(struct_size(clk_data, hws, 3), GFP_KERNEL);
if (!clk_data) {
kfree(rtc);
return;
@@ -214,9 +244,18 @@
goto err;
}
+ reg = SUN6I_LOSC_CTRL_KEY;
+ if (rtc->data->has_auto_swt) {
+ /* Bypass auto-switch to int osc, on ext losc failure */
+ reg |= SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS;
+ writel(reg, rtc->base + SUN6I_LOSC_CTRL);
+ }
+
/* Switch to the external, more precise, oscillator */
- writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC,
- rtc->base + SUN6I_LOSC_CTRL);
+ reg |= SUN6I_LOSC_CTRL_EXT_OSC;
+ if (rtc->data->has_losc_en)
+ reg |= SUN6I_LOSC_CTRL_EXT_LOSC_EN;
+ writel(reg, rtc->base + SUN6I_LOSC_CTRL);
/* Yes, I know, this is ugly. */
sun6i_rtc = rtc;
@@ -225,10 +264,15 @@
if (!of_get_property(node, "clocks", NULL))
goto err;
+ /* Only read IOSC name from device tree if it is exported */
+ if (rtc->data->export_iosc)
+ of_property_read_string_index(node, "clock-output-names", 2,
+ &iosc_name);
+
rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL,
- "rtc-int-osc",
+ iosc_name,
NULL, 0,
- 667000,
+ rtc->data->rc_osc_rate,
300000000);
if (IS_ERR(rtc->int_osc)) {
pr_crit("Couldn't register the internal oscillator\n");
@@ -253,7 +297,7 @@
of_property_read_string_index(node, "clock-output-names", 1,
&clkout_name);
- rtc->ext_losc = clk_register_gate(NULL, clkout_name, rtc->hw.init->name,
+ rtc->ext_losc = clk_register_gate(NULL, clkout_name, init.name,
0, rtc->base + SUN6I_LOSC_OUT_GATING,
SUN6I_LOSC_OUT_GATING_EN_OFFSET, 0,
&rtc->lock);
@@ -265,14 +309,88 @@
clk_data->num = 2;
clk_data->hws[0] = &rtc->hw;
clk_data->hws[1] = __clk_get_hw(rtc->ext_losc);
+ if (rtc->data->export_iosc) {
+ clk_data->hws[2] = rtc->int_osc;
+ clk_data->num = 3;
+ }
of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
return;
err:
kfree(clk_data);
}
-CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc",
- sun6i_rtc_clk_init);
+
+static const struct sun6i_rtc_clk_data sun6i_a31_rtc_data = {
+ .rc_osc_rate = 667000, /* datasheet says 600 ~ 700 KHz */
+ .has_prescaler = 1,
+};
+
+static void __init sun6i_a31_rtc_clk_init(struct device_node *node)
+{
+ sun6i_rtc_clk_init(node, &sun6i_a31_rtc_data);
+}
+CLK_OF_DECLARE_DRIVER(sun6i_a31_rtc_clk, "allwinner,sun6i-a31-rtc",
+ sun6i_a31_rtc_clk_init);
+
+static const struct sun6i_rtc_clk_data sun8i_a23_rtc_data = {
+ .rc_osc_rate = 667000, /* datasheet says 600 ~ 700 KHz */
+ .has_prescaler = 1,
+ .has_out_clk = 1,
+};
+
+static void __init sun8i_a23_rtc_clk_init(struct device_node *node)
+{
+ sun6i_rtc_clk_init(node, &sun8i_a23_rtc_data);
+}
+CLK_OF_DECLARE_DRIVER(sun8i_a23_rtc_clk, "allwinner,sun8i-a23-rtc",
+ sun8i_a23_rtc_clk_init);
+
+static const struct sun6i_rtc_clk_data sun8i_h3_rtc_data = {
+ .rc_osc_rate = 16000000,
+ .fixed_prescaler = 32,
+ .has_prescaler = 1,
+ .has_out_clk = 1,
+ .export_iosc = 1,
+};
+
+static void __init sun8i_h3_rtc_clk_init(struct device_node *node)
+{
+ sun6i_rtc_clk_init(node, &sun8i_h3_rtc_data);
+}
+CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc",
+ sun8i_h3_rtc_clk_init);
+/* As far as we are concerned, clocks for H5 are the same as H3 */
+CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc",
+ sun8i_h3_rtc_clk_init);
+
+static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = {
+ .rc_osc_rate = 16000000,
+ .fixed_prescaler = 32,
+ .has_prescaler = 1,
+ .has_out_clk = 1,
+ .export_iosc = 1,
+ .has_losc_en = 1,
+ .has_auto_swt = 1,
+};
+
+static void __init sun50i_h6_rtc_clk_init(struct device_node *node)
+{
+ sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data);
+}
+CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc",
+ sun50i_h6_rtc_clk_init);
+
+static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = {
+ .rc_osc_rate = 32000,
+ .has_out_clk = 1,
+};
+
+static void __init sun8i_v3_rtc_clk_init(struct device_node *node)
+{
+ sun6i_rtc_clk_init(node, &sun8i_v3_rtc_data);
+}
+CLK_OF_DECLARE_DRIVER(sun8i_v3_rtc_clk, "allwinner,sun8i-v3-rtc",
+ sun8i_v3_rtc_clk_init);
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
{
@@ -515,6 +633,33 @@
.alarm_irq_enable = sun6i_rtc_alarm_irq_enable
};
+#ifdef CONFIG_PM_SLEEP
+/* Enable IRQ wake on suspend, to wake up from RTC. */
+static int sun6i_rtc_suspend(struct device *dev)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(chip->irq);
+
+ return 0;
+}
+
+/* Disable IRQ wake on resume. */
+static int sun6i_rtc_resume(struct device *dev)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(chip->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops,
+ sun6i_rtc_suspend, sun6i_rtc_resume);
+
static int sun6i_rtc_probe(struct platform_device *pdev)
{
struct sun6i_rtc_dev *chip = sun6i_rtc;
@@ -527,10 +672,8 @@
chip->dev = &pdev->dev;
chip->irq = platform_get_irq(pdev, 0);
- if (chip->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource\n");
+ if (chip->irq < 0)
return chip->irq;
- }
ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
0, dev_name(&pdev->dev), chip);
@@ -567,6 +710,8 @@
clk_prepare_enable(chip->losc);
+ device_init_wakeup(&pdev->dev, 1);
+
chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-sun6i",
&sun6i_rtc_ops, THIS_MODULE);
if (IS_ERR(chip->rtc)) {
@@ -579,8 +724,20 @@
return 0;
}
+/*
+ * As far as RTC functionality goes, all models are the same. The
+ * datasheets claim that different models have different number of
+ * registers available for non-volatile storage, but experiments show
+ * that all SoCs have 16 registers available for this purpose.
+ */
static const struct of_device_id sun6i_rtc_dt_ids[] = {
{ .compatible = "allwinner,sun6i-a31-rtc" },
+ { .compatible = "allwinner,sun8i-a23-rtc" },
+ { .compatible = "allwinner,sun8i-h3-rtc" },
+ { .compatible = "allwinner,sun8i-r40-rtc" },
+ { .compatible = "allwinner,sun8i-v3-rtc" },
+ { .compatible = "allwinner,sun50i-h5-rtc" },
+ { .compatible = "allwinner,sun50i-h6-rtc" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
@@ -590,6 +747,7 @@
.driver = {
.name = "sun6i-rtc",
.of_match_table = sun6i_rtc_dt_ids,
+ .pm = &sun6i_rtc_pm_ops,
},
};
builtin_platform_driver(sun6i_rtc_driver);
diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c
index 21865d3..9b6f248 100644
--- a/drivers/rtc/rtc-sunxi.c
+++ b/drivers/rtc/rtc-sunxi.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* An RTC driver for Allwinner A10/A20
*
* Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/delay.h>
@@ -455,10 +442,8 @@
return PTR_ERR(chip->base);
chip->irq = platform_get_irq(pdev, 0);
- if (chip->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource\n");
+ if (chip->irq < 0)
return chip->irq;
- }
ret = devm_request_irq(&pdev->dev, chip->irq, sunxi_rtc_alarmirq,
0, dev_name(&pdev->dev), chip);
if (ret) {
@@ -487,15 +472,7 @@
chip->rtc->ops = &sunxi_rtc_ops;
- ret = rtc_register_device(chip->rtc);
- if (ret) {
- dev_err(&pdev->dev, "unable to register device\n");
- return ret;
- }
-
- dev_info(&pdev->dev, "RTC enabled\n");
-
- return 0;
+ return rtc_register_device(chip->rtc);
}
static struct platform_driver sunxi_rtc_driver = {
diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c
index 8dc48fe..69d695b 100644
--- a/drivers/rtc/rtc-tegra.c
+++ b/drivers/rtc/rtc-tegra.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* An RTC driver for the NVIDIA Tegra 200 series internal RTC.
*
- * Copyright (c) 2010, NVIDIA Corporation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * Copyright (c) 2010-2019, NVIDIA Corporation.
*/
#include <linux/clk.h>
@@ -31,10 +18,10 @@
#include <linux/rtc.h>
#include <linux/slab.h>
-/* set to 1 = busy every eight 32kHz clocks during copy of sec+msec to AHB */
+/* Set to 1 = busy every eight 32 kHz clocks during copy of sec+msec to AHB. */
#define TEGRA_RTC_REG_BUSY 0x004
#define TEGRA_RTC_REG_SECONDS 0x008
-/* when msec is read, the seconds are buffered into shadow seconds. */
+/* When msec is read, the seconds are buffered into shadow seconds. */
#define TEGRA_RTC_REG_SHADOW_SECONDS 0x00c
#define TEGRA_RTC_REG_MILLI_SECONDS 0x010
#define TEGRA_RTC_REG_SECONDS_ALARM0 0x014
@@ -59,44 +46,48 @@
#define TEGRA_RTC_INTR_STATUS_SEC_ALARM0 (1<<0)
struct tegra_rtc_info {
- struct platform_device *pdev;
- struct rtc_device *rtc_dev;
- void __iomem *rtc_base; /* NULL if not initialized. */
- struct clk *clk;
- int tegra_rtc_irq; /* alarm and periodic irq */
- spinlock_t tegra_rtc_lock;
+ struct platform_device *pdev;
+ struct rtc_device *rtc;
+ void __iomem *base; /* NULL if not initialized */
+ struct clk *clk;
+ int irq; /* alarm and periodic IRQ */
+ spinlock_t lock;
};
-/* RTC hardware is busy when it is updating its values over AHB once
- * every eight 32kHz clocks (~250uS).
- * outside of these updates the CPU is free to write.
- * CPU is always free to read.
+/*
+ * RTC hardware is busy when it is updating its values over AHB once every
+ * eight 32 kHz clocks (~250 us). Outside of these updates the CPU is free to
+ * write. CPU is always free to read.
*/
static inline u32 tegra_rtc_check_busy(struct tegra_rtc_info *info)
{
- return readl(info->rtc_base + TEGRA_RTC_REG_BUSY) & 1;
+ return readl(info->base + TEGRA_RTC_REG_BUSY) & 1;
}
-/* Wait for hardware to be ready for writing.
- * This function tries to maximize the amount of time before the next update.
- * It does this by waiting for the RTC to become busy with its periodic update,
- * then returning once the RTC first becomes not busy.
+/*
+ * Wait for hardware to be ready for writing. This function tries to maximize
+ * the amount of time before the next update. It does this by waiting for the
+ * RTC to become busy with its periodic update, then returning once the RTC
+ * first becomes not busy.
+ *
* This periodic update (where the seconds and milliseconds are copied to the
- * AHB side) occurs every eight 32kHz clocks (~250uS).
- * The behavior of this function allows us to make some assumptions without
- * introducing a race, because 250uS is plenty of time to read/write a value.
+ * AHB side) occurs every eight 32 kHz clocks (~250 us). The behavior of this
+ * function allows us to make some assumptions without introducing a race,
+ * because 250 us is plenty of time to read/write a value.
*/
static int tegra_rtc_wait_while_busy(struct device *dev)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
+ int retries = 500; /* ~490 us is the worst case, ~250 us is best */
- int retries = 500; /* ~490 us is the worst case, ~250 us is best. */
-
- /* first wait for the RTC to become busy. this is when it
- * posts its updated seconds+msec registers to AHB side. */
+ /*
+ * First wait for the RTC to become busy. This is when it posts its
+ * updated seconds+msec registers to AHB side.
+ */
while (tegra_rtc_check_busy(info)) {
if (!retries--)
goto retry_failed;
+
udelay(1);
}
@@ -104,36 +95,30 @@
return 0;
retry_failed:
- dev_err(dev, "write failed:retry count exceeded.\n");
+ dev_err(dev, "write failed: retry count exceeded\n");
return -ETIMEDOUT;
}
static int tegra_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
- unsigned long sec, msec;
- unsigned long sl_irq_flags;
+ unsigned long flags;
+ u32 sec, msec;
- /* RTC hardware copies seconds to shadow seconds when a read
- * of milliseconds occurs. use a lock to keep other threads out. */
- spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
+ /*
+ * RTC hardware copies seconds to shadow seconds when a read of
+ * milliseconds occurs. use a lock to keep other threads out.
+ */
+ spin_lock_irqsave(&info->lock, flags);
- msec = readl(info->rtc_base + TEGRA_RTC_REG_MILLI_SECONDS);
- sec = readl(info->rtc_base + TEGRA_RTC_REG_SHADOW_SECONDS);
+ msec = readl(info->base + TEGRA_RTC_REG_MILLI_SECONDS);
+ sec = readl(info->base + TEGRA_RTC_REG_SHADOW_SECONDS);
- spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
+ spin_unlock_irqrestore(&info->lock, flags);
- rtc_time_to_tm(sec, tm);
+ rtc_time64_to_tm(sec, tm);
- dev_vdbg(dev, "time read as %lu. %d/%d/%d %d:%02u:%02u\n",
- sec,
- tm->tm_mon + 1,
- tm->tm_mday,
- tm->tm_year + 1900,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec
- );
+ dev_vdbg(dev, "time read as %u, %ptR\n", sec, tm);
return 0;
}
@@ -141,29 +126,21 @@
static int tegra_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
- unsigned long sec;
+ u32 sec;
int ret;
- /* convert tm to seconds. */
- rtc_tm_to_time(tm, &sec);
+ /* convert tm to seconds */
+ sec = rtc_tm_to_time64(tm);
- dev_vdbg(dev, "time set to %lu. %d/%d/%d %d:%02u:%02u\n",
- sec,
- tm->tm_mon+1,
- tm->tm_mday,
- tm->tm_year+1900,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec
- );
+ dev_vdbg(dev, "time set to %u, %ptR\n", sec, tm);
- /* seconds only written if wait succeeded. */
+ /* seconds only written if wait succeeded */
ret = tegra_rtc_wait_while_busy(dev);
if (!ret)
- writel(sec, info->rtc_base + TEGRA_RTC_REG_SECONDS);
+ writel(sec, info->base + TEGRA_RTC_REG_SECONDS);
dev_vdbg(dev, "time read back as %d\n",
- readl(info->rtc_base + TEGRA_RTC_REG_SECONDS));
+ readl(info->base + TEGRA_RTC_REG_SECONDS));
return ret;
}
@@ -171,22 +148,21 @@
static int tegra_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
- unsigned long sec;
- unsigned tmp;
+ u32 sec, value;
- sec = readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
+ sec = readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0);
if (sec == 0) {
- /* alarm is disabled. */
+ /* alarm is disabled */
alarm->enabled = 0;
} else {
- /* alarm is enabled. */
+ /* alarm is enabled */
alarm->enabled = 1;
- rtc_time_to_tm(sec, &alarm->time);
+ rtc_time64_to_tm(sec, &alarm->time);
}
- tmp = readl(info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
- alarm->pending = (tmp & TEGRA_RTC_INTR_STATUS_SEC_ALARM0) != 0;
+ value = readl(info->base + TEGRA_RTC_REG_INTR_STATUS);
+ alarm->pending = (value & TEGRA_RTC_INTR_STATUS_SEC_ALARM0) != 0;
return 0;
}
@@ -194,22 +170,22 @@
static int tegra_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
- unsigned status;
- unsigned long sl_irq_flags;
+ unsigned long flags;
+ u32 status;
tegra_rtc_wait_while_busy(dev);
- spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
+ spin_lock_irqsave(&info->lock, flags);
- /* read the original value, and OR in the flag. */
- status = readl(info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+ /* read the original value, and OR in the flag */
+ status = readl(info->base + TEGRA_RTC_REG_INTR_MASK);
if (enabled)
status |= TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* set it */
else
status &= ~TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* clear it */
- writel(status, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+ writel(status, info->base + TEGRA_RTC_REG_INTR_MASK);
- spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
@@ -217,32 +193,24 @@
static int tegra_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
- unsigned long sec;
+ u32 sec;
if (alarm->enabled)
- rtc_tm_to_time(&alarm->time, &sec);
+ sec = rtc_tm_to_time64(&alarm->time);
else
sec = 0;
tegra_rtc_wait_while_busy(dev);
- writel(sec, info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
+ writel(sec, info->base + TEGRA_RTC_REG_SECONDS_ALARM0);
dev_vdbg(dev, "alarm read back as %d\n",
- readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0));
+ readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0));
/* if successfully written and alarm is enabled ... */
if (sec) {
tegra_rtc_alarm_irq_enable(dev, 1);
-
- dev_vdbg(dev, "alarm set as %lu. %d/%d/%d %d:%02u:%02u\n",
- sec,
- alarm->time.tm_mon+1,
- alarm->time.tm_mday,
- alarm->time.tm_year+1900,
- alarm->time.tm_hour,
- alarm->time.tm_min,
- alarm->time.tm_sec);
+ dev_vdbg(dev, "alarm set as %u, %ptR\n", sec, &alarm->time);
} else {
- /* disable alarm if 0 or write error. */
+ /* disable alarm if 0 or write error */
dev_vdbg(dev, "alarm disabled\n");
tegra_rtc_alarm_irq_enable(dev, 0);
}
@@ -264,39 +232,39 @@
{
struct device *dev = data;
struct tegra_rtc_info *info = dev_get_drvdata(dev);
- unsigned long events = 0;
- unsigned status;
- unsigned long sl_irq_flags;
+ unsigned long events = 0, flags;
+ u32 status;
- status = readl(info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
+ status = readl(info->base + TEGRA_RTC_REG_INTR_STATUS);
if (status) {
- /* clear the interrupt masks and status on any irq. */
+ /* clear the interrupt masks and status on any IRQ */
tegra_rtc_wait_while_busy(dev);
- spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
- writel(0, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
- writel(status, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
- spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
+
+ spin_lock_irqsave(&info->lock, flags);
+ writel(0, info->base + TEGRA_RTC_REG_INTR_MASK);
+ writel(status, info->base + TEGRA_RTC_REG_INTR_STATUS);
+ spin_unlock_irqrestore(&info->lock, flags);
}
- /* check if Alarm */
- if ((status & TEGRA_RTC_INTR_STATUS_SEC_ALARM0))
+ /* check if alarm */
+ if (status & TEGRA_RTC_INTR_STATUS_SEC_ALARM0)
events |= RTC_IRQF | RTC_AF;
- /* check if Periodic */
- if ((status & TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM))
+ /* check if periodic */
+ if (status & TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM)
events |= RTC_IRQF | RTC_PF;
- rtc_update_irq(info->rtc_dev, 1, events);
+ rtc_update_irq(info->rtc, 1, events);
return IRQ_HANDLED;
}
static const struct rtc_class_ops tegra_rtc_ops = {
- .read_time = tegra_rtc_read_time,
- .set_time = tegra_rtc_set_time,
- .read_alarm = tegra_rtc_read_alarm,
- .set_alarm = tegra_rtc_set_alarm,
- .proc = tegra_rtc_proc,
+ .read_time = tegra_rtc_read_time,
+ .set_time = tegra_rtc_set_time,
+ .read_alarm = tegra_rtc_read_alarm,
+ .set_alarm = tegra_rtc_set_alarm,
+ .proc = tegra_rtc_proc,
.alarm_irq_enable = tegra_rtc_alarm_irq_enable,
};
@@ -306,25 +274,33 @@
};
MODULE_DEVICE_TABLE(of, tegra_rtc_dt_match);
-static int __init tegra_rtc_probe(struct platform_device *pdev)
+static int tegra_rtc_probe(struct platform_device *pdev)
{
struct tegra_rtc_info *info;
struct resource *res;
int ret;
- info = devm_kzalloc(&pdev->dev, sizeof(struct tegra_rtc_info),
- GFP_KERNEL);
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->rtc_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(info->rtc_base))
- return PTR_ERR(info->rtc_base);
+ info->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->base))
+ return PTR_ERR(info->base);
- info->tegra_rtc_irq = platform_get_irq(pdev, 0);
- if (info->tegra_rtc_irq <= 0)
- return -EBUSY;
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0)
+ return ret;
+
+ info->irq = ret;
+
+ info->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(info->rtc))
+ return PTR_ERR(info->rtc);
+
+ info->rtc->ops = &tegra_rtc_ops;
+ info->rtc->range_max = U32_MAX;
info->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(info->clk))
@@ -334,38 +310,30 @@
if (ret < 0)
return ret;
- /* set context info. */
+ /* set context info */
info->pdev = pdev;
- spin_lock_init(&info->tegra_rtc_lock);
+ spin_lock_init(&info->lock);
platform_set_drvdata(pdev, info);
- /* clear out the hardware. */
- writel(0, info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
- writel(0xffffffff, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
- writel(0, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+ /* clear out the hardware */
+ writel(0, info->base + TEGRA_RTC_REG_SECONDS_ALARM0);
+ writel(0xffffffff, info->base + TEGRA_RTC_REG_INTR_STATUS);
+ writel(0, info->base + TEGRA_RTC_REG_INTR_MASK);
device_init_wakeup(&pdev->dev, 1);
- info->rtc_dev = devm_rtc_device_register(&pdev->dev,
- dev_name(&pdev->dev), &tegra_rtc_ops,
- THIS_MODULE);
- if (IS_ERR(info->rtc_dev)) {
- ret = PTR_ERR(info->rtc_dev);
- dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
- ret);
+ ret = devm_request_irq(&pdev->dev, info->irq, tegra_rtc_irq_handler,
+ IRQF_TRIGGER_HIGH, dev_name(&pdev->dev),
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request interrupt: %d\n", ret);
goto disable_clk;
}
- ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
- tegra_rtc_irq_handler, IRQF_TRIGGER_HIGH,
- dev_name(&pdev->dev), &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev,
- "Unable to request interrupt for device (err=%d).\n",
- ret);
+ ret = rtc_register_device(info->rtc);
+ if (ret)
goto disable_clk;
- }
dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
@@ -392,20 +360,20 @@
tegra_rtc_wait_while_busy(dev);
- /* only use ALARM0 as a wake source. */
- writel(0xffffffff, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
+ /* only use ALARM0 as a wake source */
+ writel(0xffffffff, info->base + TEGRA_RTC_REG_INTR_STATUS);
writel(TEGRA_RTC_INTR_STATUS_SEC_ALARM0,
- info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+ info->base + TEGRA_RTC_REG_INTR_MASK);
dev_vdbg(dev, "alarm sec = %d\n",
- readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0));
+ readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0));
- dev_vdbg(dev, "Suspend (device_may_wakeup=%d) irq:%d\n",
- device_may_wakeup(dev), info->tegra_rtc_irq);
+ dev_vdbg(dev, "Suspend (device_may_wakeup=%d) IRQ:%d\n",
+ device_may_wakeup(dev), info->irq);
- /* leave the alarms on as a wake source. */
+ /* leave the alarms on as a wake source */
if (device_may_wakeup(dev))
- enable_irq_wake(info->tegra_rtc_irq);
+ enable_irq_wake(info->irq);
return 0;
}
@@ -415,10 +383,11 @@
struct tegra_rtc_info *info = dev_get_drvdata(dev);
dev_vdbg(dev, "Resume (device_may_wakeup=%d)\n",
- device_may_wakeup(dev));
- /* alarms were left on as a wake source, turn them off. */
+ device_may_wakeup(dev));
+
+ /* alarms were left on as a wake source, turn them off */
if (device_may_wakeup(dev))
- disable_irq_wake(info->tegra_rtc_irq);
+ disable_irq_wake(info->irq);
return 0;
}
@@ -428,22 +397,21 @@
static void tegra_rtc_shutdown(struct platform_device *pdev)
{
- dev_vdbg(&pdev->dev, "disabling interrupts.\n");
+ dev_vdbg(&pdev->dev, "disabling interrupts\n");
tegra_rtc_alarm_irq_enable(&pdev->dev, 0);
}
-MODULE_ALIAS("platform:tegra_rtc");
static struct platform_driver tegra_rtc_driver = {
- .remove = tegra_rtc_remove,
- .shutdown = tegra_rtc_shutdown,
- .driver = {
- .name = "tegra_rtc",
+ .probe = tegra_rtc_probe,
+ .remove = tegra_rtc_remove,
+ .shutdown = tegra_rtc_shutdown,
+ .driver = {
+ .name = "tegra_rtc",
.of_match_table = tegra_rtc_dt_match,
- .pm = &tegra_rtc_pm_ops,
+ .pm = &tegra_rtc_pm_ops,
},
};
-
-module_platform_driver_probe(tegra_rtc_driver, tegra_rtc_probe);
+module_platform_driver(tegra_rtc_driver);
MODULE_AUTHOR("Jon Mayo <jmayo@nvidia.com>");
MODULE_DESCRIPTION("driver for Tegra internal RTC");
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
index ade6a82..74b3a06 100644
--- a/drivers/rtc/rtc-test.c
+++ b/drivers/rtc/rtc-test.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* An RTC test device/driver
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -73,11 +70,11 @@
return 0;
}
-static int test_rtc_set_mmss64(struct device *dev, time64_t secs)
+static int test_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct rtc_test_data *rtd = dev_get_drvdata(dev);
- rtd->offset = secs - ktime_get_real_seconds();
+ rtd->offset = rtc_tm_to_time64(tm) - ktime_get_real_seconds();
return 0;
}
@@ -97,15 +94,15 @@
static const struct rtc_class_ops test_rtc_ops_noalm = {
.read_time = test_rtc_read_time,
- .set_mmss64 = test_rtc_set_mmss64,
+ .set_time = test_rtc_set_time,
.alarm_irq_enable = test_rtc_alarm_irq_enable,
};
static const struct rtc_class_ops test_rtc_ops = {
.read_time = test_rtc_read_time,
+ .set_time = test_rtc_set_time,
.read_alarm = test_rtc_read_alarm,
.set_alarm = test_rtc_set_alarm,
- .set_mmss64 = test_rtc_set_mmss64,
.alarm_irq_enable = test_rtc_alarm_irq_enable,
};
@@ -136,6 +133,7 @@
break;
default:
rtd->rtc->ops = &test_rtc_ops;
+ device_init_wakeup(&plat_dev->dev, 1);
}
timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0);
@@ -155,7 +153,8 @@
{
int i, err;
- if ((err = platform_driver_register(&test_driver)))
+ err = platform_driver_register(&test_driver);
+ if (err)
return err;
err = -ENOMEM;
@@ -197,7 +196,7 @@
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("RTC test driver/device");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
module_init(test_init);
module_exit(test_exit);
diff --git a/drivers/rtc/rtc-tps6586x.c b/drivers/rtc/rtc-tps6586x.c
index d6434e5..859d901 100644
--- a/drivers/rtc/rtc-tps6586x.c
+++ b/drivers/rtc/rtc-tps6586x.c
@@ -259,7 +259,6 @@
rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc->rtc)) {
ret = PTR_ERR(rtc->rtc);
- dev_err(&pdev->dev, "RTC allocate device: ret %d\n", ret);
goto fail_rtc_register;
}
@@ -280,10 +279,8 @@
disable_irq(rtc->irq);
ret = rtc_register_device(rtc->rtc);
- if (ret) {
- dev_err(&pdev->dev, "RTC device register: ret %d\n", ret);
+ if (ret)
goto fail_rtc_register;
- }
return 0;
diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c
index a9bbd02..2c0467a 100644
--- a/drivers/rtc/rtc-tps65910.c
+++ b/drivers/rtc/rtc-tps65910.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* rtc-tps65910.c -- TPS65910 Real Time Clock interface
*
@@ -7,11 +8,6 @@
* Based on original TI driver rtc-twl.c
* Copyright (C) 2007 MontaVista Software, Inc
* Author: Alexandre Rusev <source@mvista.com>
- *
- * 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.
*/
#include <linux/kernel.h>
@@ -147,7 +143,7 @@
struct tps65910 *tps = dev_get_drvdata(dev->parent);
int ret;
- ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, alarm_data,
+ ret = regmap_bulk_read(tps->regmap, TPS65910_ALARM_SECONDS, alarm_data,
NUM_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_read_alarm error %d\n", ret);
@@ -429,13 +425,7 @@
tps_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
tps_rtc->rtc->range_max = RTC_TIMESTAMP_END_2099;
- ret = rtc_register_device(tps_rtc->rtc);
- if (ret) {
- dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
- return ret;
- }
-
- return 0;
+ return rtc_register_device(tps_rtc->rtc);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index 3472e79..c24d1e1 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* rtc-twl.c -- TWL Real Time Clock interface
*
@@ -11,11 +12,6 @@
* Copyright (C) 2003 MontaVista Software, Inc.
* Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
* Copyright (C) 2006 David Brownell
- *
- * 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 08dbefc..5a29915 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -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>
@@ -45,11 +42,6 @@
spinlock_t lock;
};
-static struct tx4939rtc_plat_data *get_tx4939rtc_plat_data(struct device *dev)
-{
- return platform_get_drvdata(to_platform_device(dev));
-}
-
static int tx4939_rtc_cmd(struct tx4939_rtc_reg __iomem *rtcreg, int cmd)
{
int i = 0;
@@ -65,10 +57,11 @@
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 tx4939rtc_plat_data *pdata = dev_get_drvdata(dev);
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ unsigned long secs = rtc_tm_to_time64(tm);
int i, ret;
unsigned char buf[6];
@@ -91,7 +84,7 @@
static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939rtc_plat_data *pdata = dev_get_drvdata(dev);
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
int i, ret;
unsigned long sec;
@@ -111,26 +104,19 @@
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;
}
static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
- struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939rtc_plat_data *pdata = dev_get_drvdata(dev);
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
int i, ret;
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;
@@ -149,7 +135,7 @@
static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
- struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939rtc_plat_data *pdata = dev_get_drvdata(dev);
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
int i, ret;
unsigned long sec;
@@ -173,13 +159,13 @@
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);
}
static int tx4939_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
- struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939rtc_plat_data *pdata = dev_get_drvdata(dev);
spin_lock_irq(&pdata->lock);
tx4939_rtc_cmd(pdata->rtcreg,
@@ -191,7 +177,7 @@
static irqreturn_t tx4939_rtc_interrupt(int irq, void *dev_id)
{
- struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev_id);
+ struct tx4939rtc_plat_data *pdata = dev_get_drvdata(dev_id);
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
unsigned long events = RTC_IRQF;
@@ -210,7 +196,7 @@
.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,
};
@@ -253,9 +239,7 @@
struct resource *res;
int irq, ret;
struct nvmem_config nvmem_cfg = {
- .name = "rv8803_nvram",
- .word_size = 4,
- .stride = 4,
+ .name = "tx4939_nvram",
.size = TX4939_RTC_REG_RAMSIZE,
.reg_read = tx4939_nvram_read,
.reg_write = tx4939_nvram_write,
@@ -285,6 +269,7 @@
rtc->ops = &tx4939_rtc_ops;
rtc->nvram_old_abi = true;
+ rtc->range_max = U32_MAX;
pdata->rtc = rtc;
@@ -317,5 +302,5 @@
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");
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
index 1f3117b..63ffba2 100644
--- a/drivers/rtc/rtc-v3020.c
+++ b/drivers/rtc/rtc-v3020.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* drivers/rtc/rtc-v3020.c
*
* Copyright (C) 2006 8D Technologies inc.
* Copyright (C) 2004 Compulab Ltd.
*
- * 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.
- *
* Driver for the V3020 RTC
*
* Changelog:
@@ -17,7 +14,6 @@
*
* ??-???-2004: Someone at Compulab
* - Initial driver creation.
- *
*/
#include <linux/platform_device.h>
#include <linux/module.h>
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index 70f013e..c752305 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for NEC VR4100 series Real Time Clock unit.
*
* Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/err.h>
#include <linux/fs.h>
@@ -136,8 +123,7 @@
time64_t epoch_sec, current_sec;
epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0);
- current_sec = mktime64(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
- time->tm_hour, time->tm_min, time->tm_sec);
+ current_sec = rtc_tm_to_time64(time);
write_elapsed_second(current_sec - epoch_sec);
@@ -158,7 +144,7 @@
spin_unlock_irq(&rtc_lock);
- rtc_time_to_tm((high << 17) | (mid << 1) | (low >> 15), time);
+ rtc_time64_to_tm((high << 17) | (mid << 1) | (low >> 15), time);
return 0;
}
@@ -166,10 +152,8 @@
static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
time64_t alarm_sec;
- struct rtc_time *time = &wkalrm->time;
- alarm_sec = mktime64(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
- time->tm_hour, time->tm_min, time->tm_sec);
+ alarm_sec = rtc_tm_to_time64(&wkalrm->time);
spin_lock_irq(&rtc_lock);
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
index 27e8969..d5d14cf 100644
--- a/drivers/rtc/rtc-vt8500.c
+++ b/drivers/rtc/rtc-vt8500.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/rtc/rtc-vt8500.c
*
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
*
* Based on rtc-pxa.c
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/module.h>
@@ -220,10 +212,8 @@
platform_set_drvdata(pdev, vt8500_rtc);
vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
- if (vt8500_rtc->irq_alarm < 0) {
- dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
+ if (vt8500_rtc->irq_alarm < 0)
return vt8500_rtc->irq_alarm;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vt8500_rtc->regbase = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/rtc/rtc-wilco-ec.c b/drivers/rtc/rtc-wilco-ec.c
new file mode 100644
index 0000000..8ad4c4e
--- /dev/null
+++ b/drivers/rtc/rtc-wilco-ec.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RTC interface for Wilco Embedded Controller with R/W abilities
+ *
+ * Copyright 2018 Google LLC
+ *
+ * The corresponding platform device is typically registered in
+ * drivers/platform/chrome/wilco_ec/core.c
+ */
+
+#include <linux/bcd.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/wilco-ec.h>
+#include <linux/rtc.h>
+#include <linux/timekeeping.h>
+
+#define EC_COMMAND_CMOS 0x7c
+#define EC_CMOS_TOD_WRITE 0x02
+#define EC_CMOS_TOD_READ 0x08
+
+/* Message sent to the EC to request the current time. */
+struct ec_rtc_read_request {
+ u8 command;
+ u8 reserved;
+ u8 param;
+} __packed;
+static struct ec_rtc_read_request read_rq = {
+ .command = EC_COMMAND_CMOS,
+ .param = EC_CMOS_TOD_READ,
+};
+
+/**
+ * struct ec_rtc_read_response - Format of RTC returned by EC.
+ * @reserved: Unused byte
+ * @second: Second value (0..59)
+ * @minute: Minute value (0..59)
+ * @hour: Hour value (0..23)
+ * @day: Day value (1..31)
+ * @month: Month value (1..12)
+ * @year: Year value (full year % 100)
+ * @century: Century value (full year / 100)
+ *
+ * All values are presented in binary (not BCD).
+ */
+struct ec_rtc_read_response {
+ u8 reserved;
+ u8 second;
+ u8 minute;
+ u8 hour;
+ u8 day;
+ u8 month;
+ u8 year;
+ u8 century;
+} __packed;
+
+/**
+ * struct ec_rtc_write_request - Format of RTC sent to the EC.
+ * @command: Always EC_COMMAND_CMOS
+ * @reserved: Unused byte
+ * @param: Always EC_CMOS_TOD_WRITE
+ * @century: Century value (full year / 100)
+ * @year: Year value (full year % 100)
+ * @month: Month value (1..12)
+ * @day: Day value (1..31)
+ * @hour: Hour value (0..23)
+ * @minute: Minute value (0..59)
+ * @second: Second value (0..59)
+ * @weekday: Day of the week (0=Saturday)
+ *
+ * All values are presented in BCD.
+ */
+struct ec_rtc_write_request {
+ u8 command;
+ u8 reserved;
+ u8 param;
+ u8 century;
+ u8 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 minute;
+ u8 second;
+ u8 weekday;
+} __packed;
+
+static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm)
+{
+ struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
+ struct ec_rtc_read_response rtc;
+ struct wilco_ec_message msg;
+ int ret;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.type = WILCO_EC_MSG_LEGACY;
+ msg.request_data = &read_rq;
+ msg.request_size = sizeof(read_rq);
+ msg.response_data = &rtc;
+ msg.response_size = sizeof(rtc);
+
+ ret = wilco_ec_mailbox(ec, &msg);
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = rtc.second;
+ tm->tm_min = rtc.minute;
+ tm->tm_hour = rtc.hour;
+ tm->tm_mday = rtc.day;
+ tm->tm_mon = rtc.month - 1;
+ tm->tm_year = rtc.year + (rtc.century * 100) - 1900;
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+ /* Don't compute day of week, we don't need it. */
+ tm->tm_wday = -1;
+
+ return 0;
+}
+
+static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
+{
+ struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
+ struct ec_rtc_write_request rtc;
+ struct wilco_ec_message msg;
+ int year = tm->tm_year + 1900;
+ /*
+ * Convert from 0=Sunday to 0=Saturday for the EC
+ * We DO need to set weekday because the EC controls battery charging
+ * schedules that depend on the day of the week.
+ */
+ int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1;
+ int ret;
+
+ rtc.command = EC_COMMAND_CMOS;
+ rtc.param = EC_CMOS_TOD_WRITE;
+ rtc.century = bin2bcd(year / 100);
+ rtc.year = bin2bcd(year % 100);
+ rtc.month = bin2bcd(tm->tm_mon + 1);
+ rtc.day = bin2bcd(tm->tm_mday);
+ rtc.hour = bin2bcd(tm->tm_hour);
+ rtc.minute = bin2bcd(tm->tm_min);
+ rtc.second = bin2bcd(tm->tm_sec);
+ rtc.weekday = bin2bcd(wday);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.type = WILCO_EC_MSG_LEGACY;
+ msg.request_data = &rtc;
+ msg.request_size = sizeof(rtc);
+
+ ret = wilco_ec_mailbox(ec, &msg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct rtc_class_ops wilco_ec_rtc_ops = {
+ .read_time = wilco_ec_rtc_read,
+ .set_time = wilco_ec_rtc_write,
+};
+
+static int wilco_ec_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->ops = &wilco_ec_rtc_ops;
+ /* EC only supports this century */
+ rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->range_max = RTC_TIMESTAMP_END_2099;
+ rtc->owner = THIS_MODULE;
+
+ return rtc_register_device(rtc);
+}
+
+static struct platform_driver wilco_ec_rtc_driver = {
+ .driver = {
+ .name = "rtc-wilco-ec",
+ },
+ .probe = wilco_ec_rtc_probe,
+};
+
+module_platform_driver(wilco_ec_rtc_driver);
+
+MODULE_ALIAS("platform:rtc-wilco-ec");
+MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Wilco EC RTC driver");
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
index 7b824da..ccef887 100644
--- a/drivers/rtc/rtc-wm831x.c
+++ b/drivers/rtc/rtc-wm831x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Real Time Clock driver for Wolfson Microelectronics WM831x
*
@@ -5,11 +6,6 @@
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
- * 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.
- *
*/
#include <linux/module.h>
@@ -155,7 +151,7 @@
if (memcmp(time1, time2, sizeof(time1)) == 0) {
u32 time = (time1[0] << 16) | time1[1];
- rtc_time_to_tm(time, tm);
+ rtc_time64_to_tm(time, tm);
return 0;
}
@@ -169,15 +165,17 @@
/*
* Set current time and date in RTC
*/
-static int wm831x_rtc_set_mmss(struct device *dev, unsigned long time)
+static int wm831x_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
struct wm831x *wm831x = wm831x_rtc->wm831x;
struct rtc_time new_tm;
- unsigned long new_time;
+ unsigned long time, new_time;
int ret;
int count = 0;
+ time = rtc_tm_to_time64(tm);
+
ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1,
(time >> 16) & 0xffff);
if (ret < 0) {
@@ -215,11 +213,7 @@
if (ret < 0)
return ret;
- ret = rtc_tm_to_time(&new_tm, &new_time);
- if (ret < 0) {
- dev_err(dev, "Failed to convert time: %d\n", ret);
- return ret;
- }
+ new_time = rtc_tm_to_time64(&new_tm);
/* Allow a second of change in case of tick */
if (new_time - time > 1) {
@@ -249,7 +243,7 @@
time = (data[0] << 16) | data[1];
- rtc_time_to_tm(time, &alrm->time);
+ rtc_time64_to_tm(time, &alrm->time);
ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL);
if (ret < 0) {
@@ -288,11 +282,7 @@
int ret;
unsigned long time;
- ret = rtc_tm_to_time(&alrm->time, &time);
- if (ret < 0) {
- dev_err(dev, "Failed to convert time: %d\n", ret);
- return ret;
- }
+ time = rtc_tm_to_time64(&alrm->time);
ret = wm831x_rtc_stop_alarm(wm831x_rtc);
if (ret < 0) {
@@ -346,7 +336,7 @@
static const struct rtc_class_ops wm831x_rtc_ops = {
.read_time = wm831x_rtc_readtime,
- .set_mmss = wm831x_rtc_set_mmss,
+ .set_time = wm831x_rtc_settime,
.read_alarm = wm831x_rtc_readalarm,
.set_alarm = wm831x_rtc_setalarm,
.alarm_irq_enable = wm831x_rtc_alarm_irq_enable,
@@ -356,11 +346,10 @@
/* Turn off the alarm if it should not be a wake source. */
static int wm831x_rtc_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
int ret, enable;
- if (wm831x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev))
+ if (wm831x_rtc->alarm_enabled && device_may_wakeup(dev))
enable = WM831X_RTC_ALM_ENA;
else
enable = 0;
@@ -368,7 +357,7 @@
ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
WM831X_RTC_ALM_ENA, enable);
if (ret != 0)
- dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret);
+ dev_err(dev, "Failed to update RTC alarm: %d\n", ret);
return 0;
}
@@ -378,15 +367,13 @@
*/
static int wm831x_rtc_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
int ret;
if (wm831x_rtc->alarm_enabled) {
ret = wm831x_rtc_start_alarm(wm831x_rtc);
if (ret != 0)
- dev_err(&pdev->dev,
- "Failed to restart RTC alarm: %d\n", ret);
+ dev_err(dev, "Failed to restart RTC alarm: %d\n", ret);
}
return 0;
@@ -395,14 +382,13 @@
/* Unconditionally disable the alarm */
static int wm831x_rtc_freeze(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
int ret;
ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
WM831X_RTC_ALM_ENA, 0);
if (ret != 0)
- dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret);
+ dev_err(dev, "Failed to stop RTC alarm: %d\n", ret);
return 0;
}
@@ -429,23 +415,28 @@
ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
- goto err;
+ return ret;
}
if (ret & WM831X_RTC_ALM_ENA)
wm831x_rtc->alarm_enabled = 1;
device_init_wakeup(&pdev->dev, 1);
- wm831x_rtc->rtc = devm_rtc_device_register(&pdev->dev, "wm831x",
- &wm831x_rtc_ops, THIS_MODULE);
- if (IS_ERR(wm831x_rtc->rtc)) {
- ret = PTR_ERR(wm831x_rtc->rtc);
- goto err;
- }
+ wm831x_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(wm831x_rtc->rtc))
+ return PTR_ERR(wm831x_rtc->rtc);
+
+ wm831x_rtc->rtc->ops = &wm831x_rtc_ops;
+ wm831x_rtc->rtc->range_max = U32_MAX;
+
+ ret = rtc_register_device(wm831x_rtc->rtc);
+ if (ret)
+ return ret;
ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
wm831x_alm_irq,
- IRQF_TRIGGER_RISING, "RTC alarm",
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "RTC alarm",
wm831x_rtc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
@@ -455,9 +446,6 @@
wm831x_rtc_add_randomness(wm831x);
return 0;
-
-err:
- return ret;
}
static const struct dev_pm_ops wm831x_rtc_pm_ops = {
diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c
index 483c799..2018614 100644
--- a/drivers/rtc/rtc-wm8350.c
+++ b/drivers/rtc/rtc-wm8350.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Real Time Clock driver for Wolfson Microelectronics WM8350
*
@@ -5,12 +6,6 @@
*
* Author: Liam Girdwood
* linux@wolfsonmicro.com
- *
- * 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.
- *
*/
#include <linux/module.h>
@@ -340,8 +335,7 @@
#ifdef CONFIG_PM_SLEEP
static int wm8350_rtc_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
int ret = 0;
u16 reg;
@@ -351,8 +345,7 @@
reg & WM8350_RTC_ALMSTS) {
ret = wm8350_rtc_stop_alarm(wm8350);
if (ret != 0)
- dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n",
- ret);
+ dev_err(dev, "Failed to stop RTC alarm: %d\n", ret);
}
return ret;
@@ -360,15 +353,13 @@
static int wm8350_rtc_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
int ret;
if (wm8350->rtc.alarm_enabled) {
ret = wm8350_rtc_start_alarm(wm8350);
if (ret != 0)
- dev_err(&pdev->dev,
- "Failed to restart RTC alarm: %d\n", ret);
+ dev_err(dev, "Failed to restart RTC alarm: %d\n", ret);
}
return 0;
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index f08f18e..d1d5a44 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An i2c driver for the Xicor/Intersil X1205 RTC
* Copyright 2004 Karen Spearel
@@ -11,10 +12,6 @@
*
* Information and datasheet:
* http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/i2c.h>
@@ -673,9 +670,16 @@
};
MODULE_DEVICE_TABLE(i2c, x1205_id);
+static const struct of_device_id x1205_dt_ids[] = {
+ { .compatible = "xircom,x1205", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, x1205_dt_ids);
+
static struct i2c_driver x1205_driver = {
.driver = {
.name = "rtc-x1205",
+ .of_match_table = x1205_dt_ids,
},
.probe = x1205_probe,
.remove = x1205_remove,
diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c
index 1538208..9683fbf 100644
--- a/drivers/rtc/rtc-xgene.c
+++ b/drivers/rtc/rtc-xgene.c
@@ -1,34 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* APM X-Gene SoC Real Time Clock Driver
*
* Copyright (c) 2014, Applied Micro Circuits Corporation
* Author: Rameshwar Prasad Sahu <rsahu@apm.com>
* Loc Ho <lho@apm.com>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
*/
+#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
/* RTC CSR Registers */
#define RTC_CCVR 0x00
@@ -48,7 +35,6 @@
struct xgene_rtc_dev {
struct rtc_device *rtc;
struct device *dev;
- unsigned long alarm_time;
void __iomem *csr_base;
struct clk *clk;
unsigned int irq_wake;
@@ -59,11 +45,11 @@
{
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
- rtc_time_to_tm(readl(pdata->csr_base + RTC_CCVR), tm);
+ rtc_time64_to_tm(readl(pdata->csr_base + RTC_CCVR), tm);
return 0;
}
-static int xgene_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int xgene_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
@@ -71,7 +57,7 @@
* NOTE: After the following write, the RTC_CCVR is only reflected
* after the update cycle of 1 seconds.
*/
- writel((u32) secs, pdata->csr_base + RTC_CLR);
+ writel((u32)rtc_tm_to_time64(tm), pdata->csr_base + RTC_CLR);
readl(pdata->csr_base + RTC_CLR); /* Force a barrier */
return 0;
@@ -81,7 +67,8 @@
{
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
- rtc_time_to_tm(pdata->alarm_time, &alrm->time);
+ /* If possible, CMR should be read here */
+ rtc_time64_to_tm(0, &alrm->time);
alrm->enabled = readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE;
return 0;
@@ -115,11 +102,8 @@
static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
- unsigned long alarm_time;
- rtc_tm_to_time(&alrm->time, &alarm_time);
- pdata->alarm_time = alarm_time;
- writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR);
+ writel((u32)rtc_tm_to_time64(&alrm->time), pdata->csr_base + RTC_CMR);
xgene_rtc_alarm_irq_enable(dev, alrm->enabled);
@@ -128,7 +112,7 @@
static const struct rtc_class_ops xgene_rtc_ops = {
.read_time = xgene_rtc_read_time,
- .set_mmss = xgene_rtc_set_mmss,
+ .set_time = xgene_rtc_set_time,
.read_alarm = xgene_rtc_read_alarm,
.set_alarm = xgene_rtc_set_alarm,
.alarm_irq_enable = xgene_rtc_alarm_irq_enable,
@@ -136,7 +120,7 @@
static irqreturn_t xgene_rtc_interrupt(int irq, void *id)
{
- struct xgene_rtc_dev *pdata = (struct xgene_rtc_dev *) id;
+ struct xgene_rtc_dev *pdata = id;
/* Check if interrupt asserted */
if (!(readl(pdata->csr_base + RTC_STAT) & RTC_STAT_BIT))
@@ -168,11 +152,13 @@
if (IS_ERR(pdata->csr_base))
return PTR_ERR(pdata->csr_base);
+ pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, xgene_rtc_interrupt, 0,
dev_name(&pdev->dev), pdata);
if (ret) {
@@ -198,15 +184,16 @@
return ret;
}
- pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &xgene_rtc_ops, THIS_MODULE);
- if (IS_ERR(pdata->rtc)) {
- clk_disable_unprepare(pdata->clk);
- return PTR_ERR(pdata->rtc);
- }
-
/* HW does not support update faster than 1 seconds */
pdata->rtc->uie_unsupported = 1;
+ pdata->rtc->ops = &xgene_rtc_ops;
+ pdata->rtc->range_max = U32_MAX;
+
+ ret = rtc_register_device(pdata->rtc);
+ if (ret) {
+ clk_disable_unprepare(pdata->clk);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
index c532bd1..2c76275 100644
--- a/drivers/rtc/rtc-zynqmp.c
+++ b/drivers/rtc/rtc-zynqmp.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Xilinx Zynq Ultrascale+ MPSoC Real Time Clock Driver
*
* Copyright (C) 2015 Xilinx, Inc.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- *
*/
#include <linux/delay.h>
@@ -49,7 +38,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 +59,6 @@
*/
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 +139,6 @@
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 +204,13 @@
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);
@@ -229,10 +218,8 @@
return PTR_ERR(xrtcdev->reg_base);
xrtcdev->alarm_irq = platform_get_irq_byname(pdev, "alarm");
- if (xrtcdev->alarm_irq < 0) {
- dev_err(&pdev->dev, "no irq resource\n");
+ if (xrtcdev->alarm_irq < 0)
return xrtcdev->alarm_irq;
- }
ret = devm_request_irq(&pdev->dev, xrtcdev->alarm_irq,
xlnx_rtc_interrupt, 0,
dev_name(&pdev->dev), xrtcdev);
@@ -242,10 +229,8 @@
}
xrtcdev->sec_irq = platform_get_irq_byname(pdev, "sec");
- if (xrtcdev->sec_irq < 0) {
- dev_err(&pdev->dev, "no irq resource\n");
+ if (xrtcdev->sec_irq < 0)
return xrtcdev->sec_irq;
- }
ret = devm_request_irq(&pdev->dev, xrtcdev->sec_irq,
xlnx_rtc_interrupt, 0,
dev_name(&pdev->dev), xrtcdev);
@@ -263,9 +248,7 @@
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)
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/sysfs.c
similarity index 92%
rename from drivers/rtc/rtc-sysfs.c
rename to drivers/rtc/sysfs.c
index f1ff30a..be3531e 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/sysfs.c
@@ -1,20 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* RTC subsystem, sysfs interface
*
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
+ */
#include <linux/module.h>
#include <linux/rtc.h>
#include "rtc-core.h"
-
/* device attributes */
/*
@@ -39,12 +35,10 @@
struct rtc_time tm;
retval = rtc_read_time(to_rtc_device(dev), &tm);
- if (retval == 0) {
- retval = sprintf(buf, "%04d-%02d-%02d\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
- }
+ if (retval)
+ return retval;
- return retval;
+ return sprintf(buf, "%ptRd\n", &tm);
}
static DEVICE_ATTR_RO(date);
@@ -55,12 +49,10 @@
struct rtc_time tm;
retval = rtc_read_time(to_rtc_device(dev), &tm);
- if (retval == 0) {
- retval = sprintf(buf, "%02d:%02d:%02d\n",
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- }
+ if (retval)
+ return retval;
- return retval;
+ return sprintf(buf, "%ptRt\n", &tm);
}
static DEVICE_ATTR_RO(time);
@@ -90,7 +82,7 @@
static ssize_t
max_user_freq_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t n)
+ const char *buf, size_t n)
{
struct rtc_device *rtc = to_rtc_device(dev);
unsigned long val;
@@ -120,12 +112,11 @@
{
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
if (rtc_hctosys_ret == 0 &&
- strcmp(dev_name(&to_rtc_device(dev)->dev),
- CONFIG_RTC_HCTOSYS_DEVICE) == 0)
+ strcmp(dev_name(&to_rtc_device(dev)->dev),
+ CONFIG_RTC_HCTOSYS_DEVICE) == 0)
return sprintf(buf, "1\n");
- else
#endif
- return sprintf(buf, "0\n");
+ return sprintf(buf, "0\n");
}
static DEVICE_ATTR_RO(hctosys);
@@ -179,15 +170,15 @@
if (*buf_ptr == '=') {
buf_ptr++;
push = 1;
- } else
+ } else {
adjust = 1;
+ }
}
retval = kstrtos64(buf_ptr, 0, &alarm);
if (retval)
return retval;
- if (adjust) {
+ if (adjust)
alarm += now;
- }
if (alarm > now || push) {
/* Avoid accidentally clobbering active alarms; we can't
* entirely prevent that here, without even the minimal
@@ -338,8 +329,8 @@
new_cnt = old_cnt + add_cnt + 1;
groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL);
- if (IS_ERR_OR_NULL(groups))
- return PTR_ERR(groups);
+ if (!groups)
+ return -ENOMEM;
memcpy(groups, rtc->dev.groups, old_cnt * sizeof(*groups));
memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups));
groups[old_cnt + add_cnt] = NULL;
diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c
index 718293d..8b70f05 100644
--- a/drivers/rtc/systohc.c
+++ b/drivers/rtc/systohc.c
@@ -1,9 +1,4 @@
-/*
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
#include <linux/rtc.h>
#include <linux/time.h>
@@ -35,8 +30,7 @@
if (!rtc)
goto out_err;
- if (!rtc->ops || (!rtc->ops->set_time && !rtc->ops->set_mmss64 &&
- !rtc->ops->set_mmss))
+ if (!rtc->ops || !rtc->ops->set_time)
goto out_close;
/* Compute the value of tv_nsec we require the caller to supply in
@@ -58,9 +52,6 @@
rtc_time64_to_tm(to_set.tv_sec, &tm);
- /* rtc_hctosys exclusively uses UTC, so we call set_time here, not
- * set_mmss.
- */
err = rtc_set_time(rtc, &tm);
out_close: