From 16ad2bc09efbcb9cb24f8d879ac218421cbef690 Mon Sep 17 00:00:00 2001 From: Valentin Caron Date: Mon, 22 Jul 2024 18:00:20 +0200 Subject: rtc: stm32: add pinctrl and pinmux interfaces STM32 RTC is capable to handle 3 specific pins of the soc. "out1, out2 and out2_rmp". To handle this, we use pinctrl framework. There is a single pin per group. Signed-off-by: Valentin Caron Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20240722160022.454226-3-valentin.caron@foss.st.com Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 5 ++ drivers/rtc/rtc-stm32.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 2a95b05982ad..4b1a87027a3d 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1923,6 +1923,11 @@ config RTC_DRV_STM32 tristate "STM32 RTC" select REGMAP_MMIO depends on ARCH_STM32 || COMPILE_TEST + depends on OF + depends on PINCTRL + select PINMUX + select PINCONF + select GENERIC_PINCONF help If you say yes here you get support for the STM32 On-Chip Real Time Clock. diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 98b07969609d..6dfd9dc07e2e 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -107,6 +110,14 @@ /* STM32 RTC driver time helpers */ #define SEC_PER_DAY (24 * 60 * 60) +/* STM32 RTC pinctrl helpers */ +#define STM32_RTC_PINMUX(_name, _action, ...) { \ + .name = (_name), \ + .action = (_action), \ + .groups = ((const char *[]){ __VA_ARGS__ }), \ + .num_groups = ARRAY_SIZE(((const char *[]){ __VA_ARGS__ })), \ +} + struct stm32_rtc; struct stm32_rtc_registers { @@ -171,6 +182,106 @@ static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc) writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + regs->wpr); } +enum stm32_rtc_pin_name { + NONE, + OUT1, + OUT2, + OUT2_RMP +}; + +static const struct pinctrl_pin_desc stm32_rtc_pinctrl_pins[] = { + PINCTRL_PIN(OUT1, "out1"), + PINCTRL_PIN(OUT2, "out2"), + PINCTRL_PIN(OUT2_RMP, "out2_rmp"), +}; + +static int stm32_rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(stm32_rtc_pinctrl_pins); +} + +static const char *stm32_rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return stm32_rtc_pinctrl_pins[selector].name; +} + +static int stm32_rtc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &stm32_rtc_pinctrl_pins[selector].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops stm32_rtc_pinctrl_ops = { + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinconf_generic_dt_free_map, + .get_groups_count = stm32_rtc_pinctrl_get_groups_count, + .get_group_name = stm32_rtc_pinctrl_get_group_name, + .get_group_pins = stm32_rtc_pinctrl_get_group_pins, +}; + +struct stm32_rtc_pinmux_func { + const char *name; + const char * const *groups; + const unsigned int num_groups; + int (*action)(struct pinctrl_dev *pctl_dev, unsigned int pin); +}; + +static const struct stm32_rtc_pinmux_func stm32_rtc_pinmux_functions[] = { +}; + +static int stm32_rtc_pinmux_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(stm32_rtc_pinmux_functions); +} + +static const char *stm32_rtc_pinmux_get_fname(struct pinctrl_dev *pctldev, unsigned int selector) +{ + return stm32_rtc_pinmux_functions[selector].name; +} + +static int stm32_rtc_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned int selector, + const char * const **groups, unsigned int * const num_groups) +{ + *groups = stm32_rtc_pinmux_functions[selector].groups; + *num_groups = stm32_rtc_pinmux_functions[selector].num_groups; + return 0; +} + +static int stm32_rtc_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned int group) +{ + struct stm32_rtc_pinmux_func selected_func = stm32_rtc_pinmux_functions[selector]; + struct pinctrl_pin_desc pin = stm32_rtc_pinctrl_pins[group]; + + /* Call action */ + if (selected_func.action) + return selected_func.action(pctldev, pin.number); + + return -EINVAL; +} + +static const struct pinmux_ops stm32_rtc_pinmux_ops = { + .get_functions_count = stm32_rtc_pinmux_get_functions_count, + .get_function_name = stm32_rtc_pinmux_get_fname, + .get_function_groups = stm32_rtc_pinmux_get_groups, + .set_mux = stm32_rtc_pinmux_set_mux, + .strict = true, +}; + +static struct pinctrl_desc stm32_rtc_pdesc = { + .name = DRIVER_NAME, + .pins = stm32_rtc_pinctrl_pins, + .npins = ARRAY_SIZE(stm32_rtc_pinctrl_pins), + .owner = THIS_MODULE, + .pctlops = &stm32_rtc_pinctrl_ops, + .pmxops = &stm32_rtc_pinmux_ops, +}; + static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc) { const struct stm32_rtc_registers *regs = &rtc->data->regs; @@ -791,6 +902,7 @@ static int stm32_rtc_probe(struct platform_device *pdev) { struct stm32_rtc *rtc; const struct stm32_rtc_registers *regs; + struct pinctrl_dev *pctl; int ret; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); @@ -912,6 +1024,14 @@ static int stm32_rtc_probe(struct platform_device *pdev) goto err; } + ret = devm_pinctrl_register_and_init(&pdev->dev, &stm32_rtc_pdesc, rtc, &pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pinctrl register failed"); + + ret = pinctrl_enable(pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pinctrl enable failed"); + /* * If INITS flag is reset (calendar year field set to 0x00), calendar * must be initialized -- cgit v1.2.3 From bb7b0df2be5c6b7bfe0cf9c93067c5b1489566ca Mon Sep 17 00:00:00 2001 From: Valentin Caron Date: Mon, 22 Jul 2024 18:00:21 +0200 Subject: rtc: stm32: add Low Speed Clock Output (LSCO) support RTC is able to output on a pin the "LSE" internal clock. STM32 RTC is now registered as a clock provider. It provides rtc_lsco clock, that means RTC_LSCO is output on either RTC_OUT1 or RTC_OUT2_RMP, depending on pinmux DT property. The clock is marked as CLK_IGNORE_UNUSED and CLK_IS_CRITICAL because RTC_LSCO can be early required by devices needed it to init. Add LSCO in pinmux functions. Add "stm32_rtc_clean_outs" to disable LSCO. As RTC is part of "backup" power domain, it is not reset during shutdown or reboot. So force LSCO disable at probe. Co-developed-by: Amelie Delaunay Signed-off-by: Amelie Delaunay Signed-off-by: Valentin Caron Link: https://lore.kernel.org/r/20240722160022.454226-4-valentin.caron@foss.st.com Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 1 + drivers/rtc/rtc-stm32.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4b1a87027a3d..b3469f6986e9 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1928,6 +1928,7 @@ config RTC_DRV_STM32 select PINMUX select PINCONF select GENERIC_PINCONF + depends on COMMON_CLK help If you say yes here you get support for the STM32 On-Chip Real Time Clock. diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 6dfd9dc07e2e..675860a13051 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,10 @@ #define STM32_RTC_CR_FMT BIT(6) #define STM32_RTC_CR_ALRAE BIT(8) #define STM32_RTC_CR_ALRAIE BIT(12) +#define STM32_RTC_CR_OSEL GENMASK(22, 21) +#define STM32_RTC_CR_COE BIT(23) +#define STM32_RTC_CR_TAMPOE BIT(26) +#define STM32_RTC_CR_OUT2EN BIT(31) /* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */ #define STM32_RTC_ISR_ALRAWF BIT(0) @@ -81,6 +86,12 @@ /* STM32_RTC_SR/_SCR bit fields */ #define STM32_RTC_SR_ALRA BIT(0) +/* STM32_RTC_CFGR bit fields */ +#define STM32_RTC_CFGR_OUT2_RMP BIT(0) +#define STM32_RTC_CFGR_LSCOEN GENMASK(2, 1) +#define STM32_RTC_CFGR_LSCOEN_OUT1 1 +#define STM32_RTC_CFGR_LSCOEN_OUT2_RMP 2 + /* STM32_RTC_VERR bit fields */ #define STM32_RTC_VERR_MINREV_SHIFT 0 #define STM32_RTC_VERR_MINREV GENMASK(3, 0) @@ -130,6 +141,7 @@ struct stm32_rtc_registers { u16 wpr; u16 sr; u16 scr; + u16 cfgr; u16 verr; }; @@ -145,6 +157,7 @@ struct stm32_rtc_data { bool need_dbp; bool need_accuracy; bool rif_protected; + bool has_lsco; }; struct stm32_rtc { @@ -157,6 +170,7 @@ struct stm32_rtc { struct clk *rtc_ck; const struct stm32_rtc_data *data; int irq_alarm; + struct clk *clk_lsco; }; struct stm32_rtc_rif_resource { @@ -231,7 +245,68 @@ struct stm32_rtc_pinmux_func { int (*action)(struct pinctrl_dev *pctl_dev, unsigned int pin); }; +static int stm32_rtc_pinmux_lsco_available(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + unsigned int calib = STM32_RTC_CR_COE; + unsigned int tampalrm = STM32_RTC_CR_TAMPOE | STM32_RTC_CR_OSEL; + + switch (pin) { + case OUT1: + if ((!(cr & STM32_RTC_CR_OUT2EN) && + ((cr & calib) || cr & tampalrm)) || + ((cr & calib) && (cr & tampalrm))) + return -EBUSY; + break; + case OUT2_RMP: + if ((cr & STM32_RTC_CR_OUT2EN) && + (cfgr & STM32_RTC_CFGR_OUT2_RMP) && + ((cr & calib) || (cr & tampalrm))) + return -EBUSY; + break; + default: + return -EINVAL; + } + + if (clk_get_rate(rtc->rtc_ck) != 32768) + return -ERANGE; + + return 0; +} + +static int stm32_rtc_pinmux_action_lsco(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + struct device *dev = rtc->rtc_dev->dev.parent; + u8 lscoen; + int ret; + + if (!rtc->data->has_lsco) + return -EPERM; + + ret = stm32_rtc_pinmux_lsco_available(pctldev, pin); + if (ret) + return ret; + + lscoen = (pin == OUT1) ? STM32_RTC_CFGR_LSCOEN_OUT1 : STM32_RTC_CFGR_LSCOEN_OUT2_RMP; + + rtc->clk_lsco = clk_register_gate(dev, "rtc_lsco", __clk_get_name(rtc->rtc_ck), + CLK_IGNORE_UNUSED | CLK_IS_CRITICAL, + rtc->base + regs.cfgr, lscoen, 0, NULL); + if (IS_ERR(rtc->clk_lsco)) + return PTR_ERR(rtc->clk_lsco); + + of_clk_add_provider(dev->of_node, of_clk_src_simple_get, rtc->clk_lsco); + + return 0; +} + static const struct stm32_rtc_pinmux_func stm32_rtc_pinmux_functions[] = { + STM32_RTC_PINMUX("lsco", &stm32_rtc_pinmux_action_lsco, "out1", "out2_rmp"), }; static int stm32_rtc_pinmux_get_functions_count(struct pinctrl_dev *pctldev) @@ -687,6 +762,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { .need_dbp = true, .need_accuracy = false, .rif_protected = false, + .has_lsco = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -697,6 +773,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { .wpr = 0x24, .sr = 0x0C, /* set to ISR offset to ease alarm management */ .scr = UNDEF_REG, + .cfgr = UNDEF_REG, .verr = UNDEF_REG, }, .events = { @@ -710,6 +787,7 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { .need_dbp = true, .need_accuracy = false, .rif_protected = false, + .has_lsco = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -720,6 +798,7 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { .wpr = 0x24, .sr = 0x0C, /* set to ISR offset to ease alarm management */ .scr = UNDEF_REG, + .cfgr = UNDEF_REG, .verr = UNDEF_REG, }, .events = { @@ -742,6 +821,7 @@ static const struct stm32_rtc_data stm32mp1_data = { .need_dbp = false, .need_accuracy = true, .rif_protected = false, + .has_lsco = true, .regs = { .tr = 0x00, .dr = 0x04, @@ -752,6 +832,7 @@ static const struct stm32_rtc_data stm32mp1_data = { .wpr = 0x24, .sr = 0x50, .scr = 0x5C, + .cfgr = 0x60, .verr = 0x3F4, }, .events = { @@ -765,6 +846,7 @@ static const struct stm32_rtc_data stm32mp25_data = { .need_dbp = false, .need_accuracy = true, .rif_protected = true, + .has_lsco = true, .regs = { .tr = 0x00, .dr = 0x04, @@ -775,6 +857,7 @@ static const struct stm32_rtc_data stm32mp25_data = { .wpr = 0x24, .sr = 0x50, .scr = 0x5C, + .cfgr = 0x60, .verr = 0x3F4, }, .events = { @@ -792,6 +875,19 @@ static const struct of_device_id stm32_rtc_of_match[] = { }; MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); +static void stm32_rtc_clean_outs(struct stm32_rtc *rtc) +{ + struct stm32_rtc_registers regs = rtc->data->regs; + + if (regs.cfgr != UNDEF_REG) { + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + + cfgr &= ~STM32_RTC_CFGR_LSCOEN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + writel_relaxed(cfgr, rtc->base + regs.cfgr); + } +} + static int stm32_rtc_check_rif(struct stm32_rtc *stm32_rtc, struct stm32_rtc_rif_resource res) { @@ -1024,6 +1120,8 @@ static int stm32_rtc_probe(struct platform_device *pdev) goto err; } + stm32_rtc_clean_outs(rtc); + ret = devm_pinctrl_register_and_init(&pdev->dev, &stm32_rtc_pdesc, rtc, &pctl); if (ret) return dev_err_probe(&pdev->dev, ret, "pinctrl register failed"); @@ -1070,6 +1168,9 @@ static void stm32_rtc_remove(struct platform_device *pdev) const struct stm32_rtc_registers *regs = &rtc->data->regs; unsigned int cr; + if (!IS_ERR_OR_NULL(rtc->clk_lsco)) + clk_unregister_gate(rtc->clk_lsco); + /* Disable interrupts */ stm32_rtc_wpr_unlock(rtc); cr = readl_relaxed(rtc->base + regs->cr); -- cgit v1.2.3 From 04dcadb87da68d1349b658b1fef04f077b78c13c Mon Sep 17 00:00:00 2001 From: Valentin Caron Date: Mon, 22 Jul 2024 18:00:22 +0200 Subject: rtc: stm32: add alarm A out feature STM32 RTC can pulse some SOC pins when an RTC alarm expires. This patch adds this functionality for alarm A. The pulse can out on three pins RTC_OUT1, RTC_OUT2, RTC_OUT2_RMP (PC13, PB2, PI8 on stm32mp15) (PC13, PB2, PI1 on stm32mp13) (PC13, PF4/PF6, PI8 on stm32mp25). This patch only adds the functionality for devices which are using st,stm32mp1-rtc and st,stm32mp25-rtc compatible. Add "alarm-a" in pinmux functions. Signed-off-by: Valentin Caron Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20240722160022.454226-5-valentin.caron@foss.st.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-stm32.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 675860a13051..3e4f2ee22b0b 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -47,8 +47,10 @@ #define STM32_RTC_CR_ALRAE BIT(8) #define STM32_RTC_CR_ALRAIE BIT(12) #define STM32_RTC_CR_OSEL GENMASK(22, 21) +#define STM32_RTC_CR_OSEL_ALARM_A FIELD_PREP(STM32_RTC_CR_OSEL, 0x01) #define STM32_RTC_CR_COE BIT(23) #define STM32_RTC_CR_TAMPOE BIT(26) +#define STM32_RTC_CR_TAMPALRM_TYPE BIT(30) #define STM32_RTC_CR_OUT2EN BIT(31) /* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */ @@ -158,6 +160,7 @@ struct stm32_rtc_data { bool need_accuracy; bool rif_protected; bool has_lsco; + bool has_alarm_out; }; struct stm32_rtc { @@ -245,6 +248,47 @@ struct stm32_rtc_pinmux_func { int (*action)(struct pinctrl_dev *pctl_dev, unsigned int pin); }; +static int stm32_rtc_pinmux_action_alarm(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + + if (!rtc->data->has_alarm_out) + return -EPERM; + + cr &= ~STM32_RTC_CR_OSEL; + cr |= STM32_RTC_CR_OSEL_ALARM_A; + cr &= ~STM32_RTC_CR_TAMPOE; + cr &= ~STM32_RTC_CR_COE; + cr &= ~STM32_RTC_CR_TAMPALRM_TYPE; + + switch (pin) { + case OUT1: + cr &= ~STM32_RTC_CR_OUT2EN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + break; + case OUT2: + cr |= STM32_RTC_CR_OUT2EN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + break; + case OUT2_RMP: + cr |= STM32_RTC_CR_OUT2EN; + cfgr |= STM32_RTC_CFGR_OUT2_RMP; + break; + default: + return -EINVAL; + } + + stm32_rtc_wpr_unlock(rtc); + writel_relaxed(cr, rtc->base + regs.cr); + writel_relaxed(cfgr, rtc->base + regs.cfgr); + stm32_rtc_wpr_lock(rtc); + + return 0; +} + static int stm32_rtc_pinmux_lsco_available(struct pinctrl_dev *pctldev, unsigned int pin) { struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); @@ -307,6 +351,7 @@ static int stm32_rtc_pinmux_action_lsco(struct pinctrl_dev *pctldev, unsigned in static const struct stm32_rtc_pinmux_func stm32_rtc_pinmux_functions[] = { STM32_RTC_PINMUX("lsco", &stm32_rtc_pinmux_action_lsco, "out1", "out2_rmp"), + STM32_RTC_PINMUX("alarm-a", &stm32_rtc_pinmux_action_alarm, "out1", "out2", "out2_rmp"), }; static int stm32_rtc_pinmux_get_functions_count(struct pinctrl_dev *pctldev) @@ -763,6 +808,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { .need_accuracy = false, .rif_protected = false, .has_lsco = false, + .has_alarm_out = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -788,6 +834,7 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { .need_accuracy = false, .rif_protected = false, .has_lsco = false, + .has_alarm_out = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -822,6 +869,7 @@ static const struct stm32_rtc_data stm32mp1_data = { .need_accuracy = true, .rif_protected = false, .has_lsco = true, + .has_alarm_out = true, .regs = { .tr = 0x00, .dr = 0x04, @@ -847,6 +895,7 @@ static const struct stm32_rtc_data stm32mp25_data = { .need_accuracy = true, .rif_protected = true, .has_lsco = true, + .has_alarm_out = true, .regs = { .tr = 0x00, .dr = 0x04, @@ -878,6 +927,17 @@ MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); static void stm32_rtc_clean_outs(struct stm32_rtc *rtc) { struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + + cr &= ~STM32_RTC_CR_OSEL; + cr &= ~STM32_RTC_CR_TAMPOE; + cr &= ~STM32_RTC_CR_COE; + cr &= ~STM32_RTC_CR_TAMPALRM_TYPE; + cr &= ~STM32_RTC_CR_OUT2EN; + + stm32_rtc_wpr_unlock(rtc); + writel_relaxed(cr, rtc->base + regs.cr); + stm32_rtc_wpr_lock(rtc); if (regs.cfgr != UNDEF_REG) { unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); -- cgit v1.2.3 From 2d611fbe9a85971a38fe93a29512bd241e4d7035 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 16 Jul 2024 11:02:52 +0800 Subject: rtc: twl: convert comma to semicolon Replace a comma between expression statements by a semicolon. Fixes: 7130856f5605 ("rtc: twl: add NVRAM support") Signed-off-by: Chen Ni Link: https://lore.kernel.org/r/20240716030252.400340-1-nichen@iscas.ac.cn Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-twl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 2cfacdd37e09..4e24c12004f1 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -591,8 +591,8 @@ static int twl_rtc_probe(struct platform_device *pdev) memset(&nvmem_cfg, 0, sizeof(nvmem_cfg)); nvmem_cfg.name = "twl-secured-"; nvmem_cfg.type = NVMEM_TYPE_BATTERY_BACKED; - nvmem_cfg.reg_read = twl_nvram_read, - nvmem_cfg.reg_write = twl_nvram_write, + nvmem_cfg.reg_read = twl_nvram_read; + nvmem_cfg.reg_write = twl_nvram_write; nvmem_cfg.word_size = 1; nvmem_cfg.stride = 1; if (twl_class_is_4030()) { -- cgit v1.2.3 From da1531ecf18612572c53878d5ef8e94673bf86ce Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 26 Aug 2024 14:13:20 -0500 Subject: rtc: s35390a: Drop vendorless compatible string from match table There's no need to list "s35390a" in the DT match table. The I2C core will strip any vendor prefix and match against the i2c_device_id table which has an "s35390a" entry. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240826191321.1410668-1-robh@kernel.org Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s35390a.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index 2d6b655a4b25..e3dc18882f41 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -56,7 +56,6 @@ static const struct i2c_device_id s35390a_id[] = { MODULE_DEVICE_TABLE(i2c, s35390a_id); static const __maybe_unused struct of_device_id s35390a_of_match[] = { - { .compatible = "s35390a" }, { .compatible = "sii,s35390a" }, { } }; -- cgit v1.2.3 From 5af858acea22bc5d8a2ebddcad57babca1442dd1 Mon Sep 17 00:00:00 2001 From: Tóth János Date: Fri, 30 Aug 2024 10:36:39 +0200 Subject: rtc: Add driver for SD2405AL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the DFRobot SD2405AL I2C RTC Module. Datasheet: https://image.dfrobot.com/image/data/TOY0021/SD2405AL%20datasheet%20(Angelo%20v0.1).pdf Product: https://www.dfrobot.com/product-1600.html To instantiate (assuming device is connected to I2C-1) as root: echo sd2405al 0x32 > /sys/bus/i2c/devices/i2c-1/new_device as user: echo 'sd2405al 0x32' | sudo tee /sys/class/i2c-adapter/i2c-1/new_device The driver is tested with: + hwclock + tools/testing/selftests/rtc/setdate + tools/testing/selftests/rtc/rtctest Reviewed-by: Csókás Bence Signed-off-by: Tóth János Link: https://lore.kernel.org/r/20240830-rtc-sd2405al-v7-1-2f7102621b1d@gmail.com Signed-off-by: Alexandre Belloni --- MAINTAINERS | 6 ++ drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-sd2405al.c | 227 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 drivers/rtc/rtc-sd2405al.c (limited to 'drivers/rtc') diff --git a/MAINTAINERS b/MAINTAINERS index 42decde38320..7c970944a5f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6474,6 +6474,12 @@ F: include/net/devlink.h F: include/uapi/linux/devlink.h F: net/devlink/ +DFROBOT SD2405AL RTC DRIVER +M: Tóth János +L: linux-rtc@vger.kernel.org +S: Maintained +F: drivers/rtc/rtc-sd2405al.c + DH ELECTRONICS IMX6 DHCOM/DHCOR BOARD SUPPORT M: Christoph Niedermaier L: kernel@dh-electronics.com diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b3469f6986e9..075151c4b786 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -743,6 +743,16 @@ config RTC_DRV_S5M This driver can also be built as a module. If so, the module will be called rtc-s5m. +config RTC_DRV_SD2405AL + tristate "DFRobot SD2405AL" + select REGMAP_I2C + help + If you say yes here you will get support for the + DFRobot SD2405AL I2C RTC Module. + + This driver can also be built as a module. If so, the module + will be called rtc-sd2405al. + config RTC_DRV_SD3078 tristate "ZXW Shenzhen whwave SD3078" select REGMAP_I2C diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 3004e372f25f..3d19feba1e1c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -162,6 +162,7 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o +obj-$(CONFIG_RTC_DRV_SD2405AL) += rtc-sd2405al.o obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o diff --git a/drivers/rtc/rtc-sd2405al.c b/drivers/rtc/rtc-sd2405al.c new file mode 100644 index 000000000000..d2568c3e3876 --- /dev/null +++ b/drivers/rtc/rtc-sd2405al.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC driver for the SD2405AL Real-Time Clock + * + * Datasheet: + * https://image.dfrobot.com/image/data/TOY0021/SD2405AL%20datasheet%20(Angelo%20v0.1).pdf + * + * Copyright (C) 2024 Tóth János + */ + +#include +#include +#include +#include + +/* Real time clock registers */ +#define SD2405AL_REG_T_SEC 0x00 +#define SD2405AL_REG_T_MIN 0x01 +#define SD2405AL_REG_T_HOUR 0x02 +# define SD2405AL_BIT_12H_PM BIT(5) +# define SD2405AL_BIT_24H BIT(7) +#define SD2405AL_REG_T_WEEK 0x03 +#define SD2405AL_REG_T_DAY 0x04 +#define SD2405AL_REG_T_MON 0x05 +#define SD2405AL_REG_T_YEAR 0x06 + +#define SD2405AL_NUM_T_REGS (SD2405AL_REG_T_YEAR - SD2405AL_REG_T_SEC + 1) + +/* Control registers */ +#define SD2405AL_REG_CTR1 0x0F +# define SD2405AL_BIT_WRTC2 BIT(2) +# define SD2405AL_BIT_WRTC3 BIT(7) +#define SD2405AL_REG_CTR2 0x10 +# define SD2405AL_BIT_WRTC1 BIT(7) +#define SD2405AL_REG_CTR3 0x11 +#define SD2405AL_REG_TTF 0x12 +#define SD2405AL_REG_CNTDWN 0x13 + +/* General RAM */ +#define SD2405AL_REG_M_START 0x14 +#define SD2405AL_REG_M_END 0x1F + +struct sd2405al { + struct device *dev; + struct rtc_device *rtc; + struct regmap *regmap; +}; + +static int sd2405al_enable_reg_write(struct sd2405al *sd2405al) +{ + int ret; + + /* order of writes is important */ + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2, + SD2405AL_BIT_WRTC1, SD2405AL_BIT_WRTC1); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3); + if (ret < 0) + return ret; + + return 0; +} + +static int sd2405al_disable_reg_write(struct sd2405al *sd2405al) +{ + int ret; + + /* order of writes is important */ + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3, 0x00); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2, + SD2405AL_BIT_WRTC1, 0x00); + if (ret < 0) + return ret; + + return 0; +} + +static int sd2405al_read_time(struct device *dev, struct rtc_time *time) +{ + u8 data[SD2405AL_NUM_T_REGS] = { 0 }; + struct sd2405al *sd2405al = dev_get_drvdata(dev); + int ret; + + ret = regmap_bulk_read(sd2405al->regmap, SD2405AL_REG_T_SEC, data, + SD2405AL_NUM_T_REGS); + if (ret < 0) + return ret; + + time->tm_sec = bcd2bin(data[SD2405AL_REG_T_SEC] & 0x7F); + time->tm_min = bcd2bin(data[SD2405AL_REG_T_MIN] & 0x7F); + + if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_24H) + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] & 0x3F); + else + if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_12H_PM) + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] + & 0x1F) + 12; + else /* 12 hour mode, AM */ + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] + & 0x1F); + + time->tm_wday = bcd2bin(data[SD2405AL_REG_T_WEEK] & 0x07); + time->tm_mday = bcd2bin(data[SD2405AL_REG_T_DAY] & 0x3F); + time->tm_mon = bcd2bin(data[SD2405AL_REG_T_MON] & 0x1F) - 1; + time->tm_year = bcd2bin(data[SD2405AL_REG_T_YEAR]) + 100; + + dev_dbg(sd2405al->dev, "read time: %ptR (%d)\n", time, time->tm_wday); + + return 0; +} + +static int sd2405al_set_time(struct device *dev, struct rtc_time *time) +{ + u8 data[SD2405AL_NUM_T_REGS]; + struct sd2405al *sd2405al = dev_get_drvdata(dev); + int ret; + + data[SD2405AL_REG_T_SEC] = bin2bcd(time->tm_sec); + data[SD2405AL_REG_T_MIN] = bin2bcd(time->tm_min); + data[SD2405AL_REG_T_HOUR] = bin2bcd(time->tm_hour) | SD2405AL_BIT_24H; + data[SD2405AL_REG_T_DAY] = bin2bcd(time->tm_mday); + data[SD2405AL_REG_T_WEEK] = bin2bcd(time->tm_wday); + data[SD2405AL_REG_T_MON] = bin2bcd(time->tm_mon) + 1; + data[SD2405AL_REG_T_YEAR] = bin2bcd(time->tm_year - 100); + + ret = sd2405al_enable_reg_write(sd2405al); + if (ret < 0) + return ret; + + ret = regmap_bulk_write(sd2405al->regmap, SD2405AL_REG_T_SEC, data, + SD2405AL_NUM_T_REGS); + if (ret < 0) + return ret; + + ret = regmap_write(sd2405al->regmap, SD2405AL_REG_TTF, 0x00); + if (ret < 0) + return ret; + + ret = sd2405al_disable_reg_write(sd2405al); + if (ret < 0) + return ret; + + dev_dbg(sd2405al->dev, "set time: %ptR (%d)\n", time, time->tm_wday); + + return 0; +} + +static const struct rtc_class_ops sd2405al_rtc_ops = { + .read_time = sd2405al_read_time, + .set_time = sd2405al_set_time, +}; + +static const struct regmap_config sd2405al_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SD2405AL_REG_M_END, +}; + +static int sd2405al_probe(struct i2c_client *client) +{ + struct sd2405al *sd2405al; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + sd2405al = devm_kzalloc(&client->dev, sizeof(*sd2405al), GFP_KERNEL); + if (!sd2405al) + return -ENOMEM; + + sd2405al->dev = &client->dev; + + sd2405al->regmap = devm_regmap_init_i2c(client, &sd2405al_regmap_conf); + if (IS_ERR(sd2405al->regmap)) + return PTR_ERR(sd2405al->regmap); + + sd2405al->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(sd2405al->rtc)) + return PTR_ERR(sd2405al->rtc); + + sd2405al->rtc->ops = &sd2405al_rtc_ops; + sd2405al->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + sd2405al->rtc->range_max = RTC_TIMESTAMP_END_2099; + + dev_set_drvdata(&client->dev, sd2405al); + + ret = devm_rtc_register_device(sd2405al->rtc); + if (ret < 0) + return ret; + + return 0; +} + +static const struct i2c_device_id sd2405al_id[] = { + { "sd2405al" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, sd2405al_id); + +static const __maybe_unused struct of_device_id sd2405al_of_match[] = { + { .compatible = "dfrobot,sd2405al" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sd2405al_of_match); + +static struct i2c_driver sd2405al_driver = { + .driver = { + .name = "sd2405al", + .of_match_table = of_match_ptr(sd2405al_of_match), + }, + .probe = sd2405al_probe, + .id_table = sd2405al_id, +}; + +module_i2c_driver(sd2405al_driver); + +MODULE_AUTHOR("Tóth János "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SD2405AL RTC driver"); -- cgit v1.2.3 From 80bf13c07c07c43cc959b253305b64922cb20a92 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 30 Jul 2024 21:49:05 +0200 Subject: rtc: sun6i: disable automatic clock input switching The V3(s) will detect a valid external low frequency clock and if it is not present will automatically switch to the internal one. This might hide bugs and (hardware) configuration errors. It's even worse because the internal RTC runs significantly slower (32.000Hz vs 32.768Hz). Fortunately for us, the V3(s) has an (undocumented) bypass of this switching and the driver already supports it by setting the .has_auto_swt flag. Signed-off-by: Michael Walle Acked-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20240730194905.2587202-1-mwalle@kernel.org Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sun6i.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 8e0c66906103..e681c1745866 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -402,6 +402,7 @@ CLK_OF_DECLARE_DRIVER(sun8i_r40_rtc_clk, "allwinner,sun8i-r40-rtc", static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = { .rc_osc_rate = 32000, .has_out_clk = 1, + .has_auto_swt = 1, }; static void __init sun8i_v3_rtc_clk_init(struct device_node *node) -- cgit v1.2.3 From 73580e2ee6adfb40276bd420da3bb1abae204e10 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 25 Aug 2024 20:31:03 +0200 Subject: rtc: at91sam9: fix OF node leak in probe() error path Driver is leaking an OF node reference obtained from of_parse_phandle_with_fixed_args(). Fixes: 43e112bb3dea ("rtc: at91sam9: make use of syscon/regmap to access GPBR registers") Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240825183103.102904-1-krzysztof.kozlowski@linaro.org Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-at91sam9.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index f93bee96e362..993c0878fb66 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -368,6 +368,7 @@ static int at91_rtc_probe(struct platform_device *pdev) return ret; rtc->gpbr = syscon_node_to_regmap(args.np); + of_node_put(args.np); rtc->gpbr_offset = args.args[0]; if (IS_ERR(rtc->gpbr)) { dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n"); -- cgit v1.2.3 From 60a06efc56d7d336d13fae58f9e90c8a4e21619e Mon Sep 17 00:00:00 2001 From: Abhishek Tamboli Date: Fri, 9 Aug 2024 21:26:31 +0530 Subject: rtc: m48t59: Remove division condition with direct comparison Replace 'year / 100' with a direct comparison 'year >= 100' in m48t59_rtc_set_time() function. Improve the code clarity and eliminate division overhead. Fix the following smatch warning: drivers/rtc/rtc-m48t59.c:135 m48t59_rtc_set_time() warn: replace divide condition 'year / 100' with 'year >= 100' Signed-off-by: Abhishek Tamboli Link: https://lore.kernel.org/r/20240809155631.548044-1-abhishektamboli9@gmail.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m48t59.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index f0f6b9b6daec..cd2ca49805d8 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -132,7 +132,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH); M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR); - if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100)) + if (pdata->type == M48T59RTC_TYPE_M48T59 && (year >= 100)) val = (M48T59_WDAY_CEB | M48T59_WDAY_CB); val |= (bin2bcd(tm->tm_wday) & 0x07); M48T59_WRITE(val, M48T59_WDAY); -- cgit v1.2.3 From b242650dfa17d8591d92e8e151438d1f8e54997a Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 12 Sep 2024 00:48:36 +0200 Subject: rtc: m48t59: set range The m48t59 leap year calculation will fail in 2100 Link: https://lore.kernel.org/r/20240911224836.1571831-1-alexandre.belloni@bootlin.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m48t59.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index cd2ca49805d8..5d30ce8e13ca 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -458,6 +458,8 @@ static int m48t59_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, m48t59); m48t59->rtc->ops = &m48t59_rtc_ops; + m48t59->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + m48t59->rtc->range_max = RTC_TIMESTAMP_END_2099; nvmem_cfg.size = pdata->offset; ret = devm_rtc_nvmem_register(m48t59->rtc, &nvmem_cfg); -- cgit v1.2.3 From 690286214916f32d75de2667ec0fcfa9c3f4eefb Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Wed, 18 Sep 2024 23:21:59 +0200 Subject: rtc: rc5t619: use proper module tables Avoid requiring MODULE_ALIASES by declaring proper device id tables. Signed-off-by: Andreas Kemnade Link: https://lore.kernel.org/r/20240918212159.1191637-1-andreas@kemnade.info Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rc5t619.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-rc5t619.c b/drivers/rtc/rtc-rc5t619.c index e73102a39f1b..711f62eecd79 100644 --- a/drivers/rtc/rtc-rc5t619.c +++ b/drivers/rtc/rtc-rc5t619.c @@ -429,14 +429,23 @@ static int rc5t619_rtc_probe(struct platform_device *pdev) return devm_rtc_register_device(rtc->rtc); } +static const struct platform_device_id rc5t619_rtc_id[] = { + { + .name = "rc5t619-rtc", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, rc5t619_rtc_id); + static struct platform_driver rc5t619_rtc_driver = { .driver = { .name = "rc5t619-rtc", }, .probe = rc5t619_rtc_probe, + .id_table = rc5t619_rtc_id, }; - module_platform_driver(rc5t619_rtc_driver); -MODULE_ALIAS("platform:rc5t619-rtc"); + MODULE_DESCRIPTION("RICOH RC5T619 RTC driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3