summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-01 15:39:58 +0100
committerMark Brown <broonie@kernel.org>2026-10-01 15:39:58 +0100
commit20f03c0baae90a8cc3f665cddbd8a1fadb080b3b (patch)
treef411307d289bde5e62d8be8f378dfd5a41b5e174
parent26e43d5eb9c6dd8e785d5853dceb23755e852d92 (diff)
parent8ef8e9839a23ca7f5dda6a69b670633de5a23716 (diff)
downloadlinux-next-20f03c0baae90a8cc3f665cddbd8a1fadb080b3b.tar.gz
linux-next-20f03c0baae90a8cc3f665cddbd8a1fadb080b3b.zip
Merge branch 'rtc-next' of https://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git
-rw-r--r--Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml9
-rw-r--r--Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml30
-rw-r--r--drivers/rtc/rtc-bd70528.c5
-rw-r--r--drivers/rtc/rtc-ftrtc010.c74
-rw-r--r--drivers/rtc/rtc-mt6397.c91
-rw-r--r--drivers/rtc/rtc-omap.c5
-rw-r--r--drivers/rtc/rtc-rzn1.c52
-rw-r--r--drivers/soc/ti/pm33xx.c2
-rw-r--r--include/linux/mfd/mt6397/rtc.h7
-rw-r--r--include/linux/rtc/rtc-omap.h2
10 files changed, 194 insertions, 83 deletions
diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
index 3cbc0dc12c31..5c89c589b53c 100644
--- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
+++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
@@ -81,6 +81,15 @@ properties:
start-year: true
+ nvmem-layout:
+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml
+ description:
+ The RTC carries four bytes of storage that neither the clock nor the
+ alarm uses, in the high half of four of the alarm registers, and
+ offers them as a battery-backed nvmem provider. MediaTek's
+ documentation names them RTC_NEW_SPARE0 to RTC_NEW_SPARE3 and gives
+ the first to a fuel gauge.
+
required:
- compatible
diff --git a/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml b/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
index ea7b039a91e7..b448725671ec 100644
--- a/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
+++ b/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
@@ -14,14 +14,16 @@ properties:
oneOf:
- items:
- enum:
- - renesas,r9a06g032-rtc
- - const: renesas,rzn1-rtc
+ - renesas,r9a06g032-rtc # RZ/N1D
+ - const: renesas,rzn1-rtc # RZ/N1
- - const: renesas,r9a09g077-rtc
+ - const: renesas,r9a09g077-rtc # RZ/T2H
- items:
- - const: renesas,r9a09g087-rtc
- - const: renesas,r9a09g077-rtc
+ - const: renesas,r9a09g087-rtc # RZ/N2H
+ - const: renesas,r9a09g077-rtc # RZ/T2H
+
+ - const: renesas,r8a78000-rtc # R-Car X5H
reg:
maxItems: 1
@@ -52,8 +54,6 @@ properties:
required:
- compatible
- reg
- - interrupts
- - interrupt-names
- clocks
- clock-names
- power-domains
@@ -66,6 +66,7 @@ allOf:
compatible:
contains:
enum:
+ - renesas,r8a78000-rtc
- renesas,r9a09g077-rtc
- renesas,r9a09g087-rtc
then:
@@ -75,6 +76,20 @@ allOf:
clock-names:
minItems: 2
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: renesas,r8a78000-rtc
+ then:
+ properties:
+ interrupts: false
+ interrupt-names: false
+ else:
+ required:
+ - interrupts
+ - interrupt-names
+
unevaluatedProperties: false
examples:
@@ -91,5 +106,4 @@ examples:
clocks = <&sysctrl R9A06G032_HCLK_RTC>;
clock-names = "hclk";
power-domains = <&sysctrl>;
- start-year = <2000>;
};
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
index fd415e327ea6..dd5569d17567 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
@@ -328,6 +329,10 @@ static int bd70528_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
device_wakeup_enable(&pdev->dev);
+ ret = devm_pm_set_wake_irq(&pdev->dev, irq);
+ if (ret)
+ return ret;
+
rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc)) {
dev_err(&pdev->dev, "RTC device creation failed\n");
diff --git a/drivers/rtc/rtc-ftrtc010.c b/drivers/rtc/rtc-ftrtc010.c
index 98fa0f518303..b29c96be40f4 100644
--- a/drivers/rtc/rtc-ftrtc010.c
+++ b/drivers/rtc/rtc-ftrtc010.c
@@ -72,7 +72,7 @@ static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm)
days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD);
- time = offset + days * 86400 + hour * 3600 + min * 60 + sec;
+ time = (timeu64_t)(offset + days * 86400 + hour * 3600 + min * 60 + sec);
rtc_time64_to_tm(time, tm);
@@ -92,7 +92,8 @@ static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm)
hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
- offset = time - (day * 86400 + hour * 3600 + min * 60 + sec);
+ offset = time - ((timeu64_t)day * 86400 + (timeu64_t)hour * 3600 +
+ (timeu64_t)min * 60 + (timeu64_t)sec);
writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD);
writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR);
@@ -119,51 +120,33 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, rtc);
- rtc->pclk = devm_clk_get(dev, "PCLK");
- if (IS_ERR(rtc->pclk)) {
+ rtc->pclk = devm_clk_get_enabled(dev, "PCLK");
+ if (IS_ERR_OR_NULL(rtc->pclk)) {
dev_err(dev, "could not get PCLK\n");
- } else {
- ret = clk_prepare_enable(rtc->pclk);
- if (ret) {
- dev_err(dev, "failed to enable PCLK\n");
- return ret;
- }
+ return PTR_ERR_OR_ZERO(rtc->pclk);
}
- rtc->extclk = devm_clk_get(dev, "EXTCLK");
- if (IS_ERR(rtc->extclk)) {
+ rtc->extclk = devm_clk_get_enabled(dev, "EXTCLK");
+ if (IS_ERR_OR_NULL(rtc->extclk)) {
dev_err(dev, "could not get EXTCLK\n");
- } else {
- ret = clk_prepare_enable(rtc->extclk);
- if (ret) {
- dev_err(dev, "failed to enable EXTCLK\n");
- goto err_disable_pclk;
- }
+ return PTR_ERR_OR_ZERO(rtc->extclk);
}
rtc->rtc_irq = platform_get_irq(pdev, 0);
- if (rtc->rtc_irq < 0) {
- ret = rtc->rtc_irq;
- goto err_disable_extclk;
- }
+ if (rtc->rtc_irq < 0)
+ return rtc->rtc_irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto err_disable_extclk;
- }
+ if (!res)
+ return -ENODEV;
rtc->rtc_base = devm_ioremap(dev, res->start,
resource_size(res));
- if (!rtc->rtc_base) {
- ret = -ENOMEM;
- goto err_disable_extclk;
- }
+ if (!rtc->rtc_base)
+ return -ENOMEM;
rtc_dev = devm_rtc_allocate_device(dev);
- if (IS_ERR(rtc_dev)) {
- ret = PTR_ERR(rtc_dev);
- goto err_disable_extclk;
- }
+ if (IS_ERR(rtc_dev))
+ return PTR_ERR(rtc_dev);
rtc_dev->ops = &ftrtc010_rtc_ops;
@@ -178,25 +161,13 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
IRQF_SHARED, pdev->name, dev);
if (unlikely(ret))
- goto err_disable_extclk;
+ return ret;
- return devm_rtc_register_device(rtc_dev);
+ ret = devm_rtc_register_device(rtc_dev);
+ if (ret)
+ return ret;
-err_disable_extclk:
- clk_disable_unprepare(rtc->extclk);
-err_disable_pclk:
- clk_disable_unprepare(rtc->pclk);
- return ret;
-}
-
-static void ftrtc010_rtc_remove(struct platform_device *pdev)
-{
- struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev);
-
- if (!IS_ERR(rtc->extclk))
- clk_disable_unprepare(rtc->extclk);
- if (!IS_ERR(rtc->pclk))
- clk_disable_unprepare(rtc->pclk);
+ return 0;
}
static const struct of_device_id ftrtc010_rtc_dt_match[] = {
@@ -212,7 +183,6 @@ static struct platform_driver ftrtc010_rtc_driver = {
.of_match_table = ftrtc010_rtc_dt_match,
},
.probe = ftrtc010_rtc_probe,
- .remove = ftrtc010_rtc_remove,
};
module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe);
diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c
index 3d857681f760..d6e156516aa6 100644
--- a/drivers/rtc/rtc-mt6397.c
+++ b/drivers/rtc/rtc-mt6397.c
@@ -4,6 +4,8 @@
* Author: Tianping.Fang <tianping.fang@mediatek.com>
*/
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mfd/mt6397/core.h>
@@ -243,10 +245,91 @@ static const struct rtc_class_ops mtk_rtc_ops = {
.set_alarm = mtk_rtc_set_alarm,
};
+/*
+ * The spare byte of each of these registers, in the order a board addresses
+ * them as RTC_NEW_SPARE0 to RTC_NEW_SPARE3.
+ */
+static const u32 mtk_rtc_spare_reg[] = {
+ RTC_AL_HOU, RTC_AL_DOM, RTC_AL_DOW, RTC_AL_MTH,
+};
+
+static int mtk_rtc_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct mt6397_rtc *rtc = priv;
+ u8 *buf = val;
+ u32 data;
+ int ret = 0;
+
+ if (offset >= ARRAY_SIZE(mtk_rtc_spare_reg) ||
+ bytes > ARRAY_SIZE(mtk_rtc_spare_reg) - offset)
+ return -EINVAL;
+
+ mutex_lock(&rtc->lock);
+
+ while (bytes--) {
+ ret = regmap_read(rtc->regmap,
+ rtc->addr_base + mtk_rtc_spare_reg[offset++],
+ &data);
+ if (ret)
+ break;
+
+ *buf++ = FIELD_GET(RTC_SPARE_MASK, data);
+ }
+
+ mutex_unlock(&rtc->lock);
+
+ return ret;
+}
+
+static int mtk_rtc_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct mt6397_rtc *rtc = priv;
+ u8 *buf = val;
+ int ret = 0;
+
+ if (offset >= ARRAY_SIZE(mtk_rtc_spare_reg) ||
+ bytes > ARRAY_SIZE(mtk_rtc_spare_reg) - offset)
+ return -EINVAL;
+
+ mutex_lock(&rtc->lock);
+
+ while (bytes--) {
+ ret = regmap_update_bits(rtc->regmap,
+ rtc->addr_base + mtk_rtc_spare_reg[offset++],
+ RTC_SPARE_MASK,
+ FIELD_PREP(RTC_SPARE_MASK, *buf++));
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * None of it reaches the always-on domain until the write trigger,
+ * which commits every pending alarm register at once -- so this runs
+ * under the same lock the alarm paths take, rather than landing in
+ * the middle of one of them.
+ */
+ ret = mtk_rtc_write_trigger(rtc);
+out:
+ mutex_unlock(&rtc->lock);
+
+ return ret;
+}
+
static int mtk_rtc_probe(struct platform_device *pdev)
{
struct resource *res;
struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
+ struct nvmem_config nvmem_cfg = {
+ .name = "mt6397_rtc_spare",
+ .word_size = 1,
+ .stride = 1,
+ .size = ARRAY_SIZE(mtk_rtc_spare_reg),
+ .type = NVMEM_TYPE_BATTERY_BACKED,
+ .reg_read = mtk_rtc_nvram_read,
+ .reg_write = mtk_rtc_nvram_write,
+ };
struct mt6397_rtc *rtc;
int ret;
@@ -293,7 +376,13 @@ static int mtk_rtc_probe(struct platform_device *pdev)
rtc->rtc_dev->start_secs = mktime64(1968, 1, 2, 0, 0, 0);
rtc->rtc_dev->set_start_time = true;
- return devm_rtc_register_device(rtc->rtc_dev);
+ ret = devm_rtc_register_device(rtc->rtc_dev);
+ if (ret)
+ return ret;
+
+ nvmem_cfg.priv = rtc;
+
+ return devm_rtc_nvmem_register(rtc->rtc_dev, &nvmem_cfg);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 0f90065e352c..18492165a19f 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -408,7 +408,7 @@ static struct omap_rtc *omap_rtc_power_off_rtc;
* generates pmic_pwr_enable control, which can be used to control an external
* PMIC.
*/
-int omap_rtc_power_off_program(struct device *dev)
+int omap_rtc_power_off_program(void)
{
struct omap_rtc *rtc = omap_rtc_power_off_rtc;
struct rtc_time tm;
@@ -481,10 +481,9 @@ EXPORT_SYMBOL(omap_rtc_power_off_program);
*/
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);
+ omap_rtc_power_off_program();
/* 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);
diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c
index b9d69ebdd224..1db518ab3a3a 100644
--- a/drivers/rtc/rtc-rzn1.c
+++ b/drivers/rtc/rtc-rzn1.c
@@ -68,6 +68,7 @@
struct rzn1_rtc_data {
bool has_subu;
+ bool broken_irqs;
};
struct rzn1_rtc {
@@ -396,6 +397,11 @@ static const struct rtc_class_ops rzn1_rtc_ops_scmp = {
.alarm_irq_enable = rzn1_rtc_alarm_irq_enable,
};
+static const struct rtc_class_ops rzn1_rtc_ops_scmp_no_alarm = {
+ .read_time = rzn1_rtc_read_time,
+ .set_time = rzn1_rtc_set_time,
+};
+
static void rzn1_rtc_disable_hardware(void *data)
{
struct device *dev = data;
@@ -431,10 +437,6 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
if (IS_ERR(rtc->base))
return dev_err_probe(dev, PTR_ERR(rtc->base), "Missing reg\n");
- irq = platform_get_irq_byname(pdev, "alarm");
- if (irq < 0)
- return irq;
-
rtc->rtcdev = devm_rtc_allocate_device(dev);
if (IS_ERR(rtc->rtcdev))
return PTR_ERR(rtc->rtcdev);
@@ -503,25 +505,40 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
spin_lock_init(&rtc->ctl1_access_lock);
- ret = devm_request_irq(dev, irq, rzn1_rtc_alarm_irq, 0, "RZN1 RTC Alarm", rtc);
- if (ret)
- return dev_err_probe(dev, ret, "RTC alarm interrupt not available\n");
-
- irq = platform_get_irq_byname_optional(pdev, "pps");
- if (irq == -EPROBE_DEFER)
- return irq;
- if (irq >= 0)
- ret = devm_request_irq(dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc);
-
- if (irq < 0 || ret) {
- set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
+ if (data->broken_irqs) {
+ /* All known broken irqs are on SCMP only variants */
+ rtc->rtcdev->ops = &rzn1_rtc_ops_scmp_no_alarm;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
- dev_warn(dev, "RTC pps interrupt not available. Alarm has only minute accuracy\n");
+ } else {
+ irq = platform_get_irq_byname(pdev, "alarm");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, rzn1_rtc_alarm_irq, 0, "RZN1 RTC Alarm", rtc);
+ if (ret)
+ return dev_err_probe(dev, ret, "RTC alarm interrupt not available\n");
+
+ irq = platform_get_irq_byname_optional(pdev, "pps");
+ if (irq == -EPROBE_DEFER)
+ return irq;
+ if (irq >= 0)
+ ret = devm_request_irq(dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc);
+
+ if (irq < 0 || ret) {
+ set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
+ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
+ dev_warn(dev, "RTC pps interrupt not available. Alarm has only minute accuracy\n");
+ }
}
return devm_rtc_register_device(rtc->rtcdev);
}
+static const struct rzn1_rtc_data rzn1_rtc_r8a78000_data = {
+ .has_subu = false,
+ .broken_irqs = true,
+};
+
static const struct rzn1_rtc_data rzn1_rtc_rzt2h_data = {
.has_subu = false,
};
@@ -531,6 +548,7 @@ static const struct rzn1_rtc_data rzn1_rtc_rzn1_data = {
};
static const struct of_device_id rzn1_rtc_of_match[] = {
+ { .compatible = "renesas,r8a78000-rtc", .data = &rzn1_rtc_r8a78000_data },
{ .compatible = "renesas,r9a09g077-rtc", .data = &rzn1_rtc_rzt2h_data },
{ .compatible = "renesas,rzn1-rtc", .data = &rzn1_rtc_rzn1_data },
{ /* sentinel */ }
diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c
index dc52a2197d24..82979122417d 100644
--- a/drivers/soc/ti/pm33xx.c
+++ b/drivers/soc/ti/pm33xx.c
@@ -174,7 +174,7 @@ static struct wkup_m3_wakeup_src rtc_wake_src(void)
static int am33xx_rtc_only_idle(unsigned long wfi_flags)
{
- omap_rtc_power_off_program(&omap_rtc->dev);
+ omap_rtc_power_off_program();
am33xx_do_wfi_sram(wfi_flags);
return 0;
}
diff --git a/include/linux/mfd/mt6397/rtc.h b/include/linux/mfd/mt6397/rtc.h
index 27883af44f87..f4da579ec638 100644
--- a/include/linux/mfd/mt6397/rtc.h
+++ b/include/linux/mfd/mt6397/rtc.h
@@ -49,6 +49,13 @@
#define RTC_AL_SEC 0x0018
+/* The high byte of four of the alarms is spare, always-on storage */
+#define RTC_AL_HOU 0x001c
+#define RTC_AL_DOM 0x001e
+#define RTC_AL_DOW 0x0020
+#define RTC_AL_MTH 0x0022
+#define RTC_SPARE_MASK GENMASK(15, 8)
+
#define RTC_AL_SEC_MASK 0x003f
#define RTC_AL_MIN_MASK 0x003f
#define RTC_AL_HOU_MASK 0x001f
diff --git a/include/linux/rtc/rtc-omap.h b/include/linux/rtc/rtc-omap.h
index 9f03a329e63f..d6ec7c4daea6 100644
--- a/include/linux/rtc/rtc-omap.h
+++ b/include/linux/rtc/rtc-omap.h
@@ -3,5 +3,5 @@
#ifndef _LINUX_RTCOMAP_H_
#define _LINUX_RTCOMAP_H_
-int omap_rtc_power_off_program(struct device *dev);
+int omap_rtc_power_off_program(void);
#endif /* _LINUX_RTCOMAP_H_ */