diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-01 15:39:58 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-01 15:39:58 +0100 |
| commit | 20f03c0baae90a8cc3f665cddbd8a1fadb080b3b (patch) | |
| tree | f411307d289bde5e62d8be8f378dfd5a41b5e174 | |
| parent | 26e43d5eb9c6dd8e785d5853dceb23755e852d92 (diff) | |
| parent | 8ef8e9839a23ca7f5dda6a69b670633de5a23716 (diff) | |
| download | linux-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.yaml | 9 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml | 30 | ||||
| -rw-r--r-- | drivers/rtc/rtc-bd70528.c | 5 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ftrtc010.c | 74 | ||||
| -rw-r--r-- | drivers/rtc/rtc-mt6397.c | 91 | ||||
| -rw-r--r-- | drivers/rtc/rtc-omap.c | 5 | ||||
| -rw-r--r-- | drivers/rtc/rtc-rzn1.c | 52 | ||||
| -rw-r--r-- | drivers/soc/ti/pm33xx.c | 2 | ||||
| -rw-r--r-- | include/linux/mfd/mt6397/rtc.h | 7 | ||||
| -rw-r--r-- | include/linux/rtc/rtc-omap.h | 2 |
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_ */ |
