* [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs @ 2025-04-03 10:33 Ciprian Costea 2025-04-03 10:33 ` [PATCH v9 1/4] dt-bindings: rtc: add schema for NXP " Ciprian Costea ` (4 more replies) 0 siblings, 5 replies; 19+ messages in thread From: Ciprian Costea @ 2025-04-03 10:33 UTC (permalink / raw) To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet, Ciprian Marian Costea From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> This patch series add support for the NXP RTC hardware module present on S32G2/S32G3 SoCs. RTC tracks clock time during system suspend. It is used as a time-based wakeup source for the S32G2/S32G3 SoCs. RTC is not battery-powered and it is not kept alive during system reset. Following is an example of Suspend to RAM trigger on S32G2/S32G3 SoCs, using userspace tools such as rtcwake: # rtcwake -s 120 -m mem # rtcwake: assuming RTC uses UTC ... # rtcwake: wakeup from "mem" using /dev/rtc0 at Wed Feb 6 06:28:36 2036 # Changes in V9: - Added missing 'bitfield.h' include file - Moved reserved clk mask check earlier to stop execution in case a reserved clk is being used. - Used 'DIV_ROUND_UP' macro when determining the rtc clock frequency. - Fixed some indentation inconsistencies. - Added Frank Li's Reviewed-by tag. Changes in V8: - Added description of uncommon S32G RTC hardware behaviour on resume. Changes in V7: - Changed 'ioread32/iowrite32' calls to more common 'readl/writel'. - Removed calculations performed in 'suspend' and 'resume' routines by using RTC API ('APIVAL') functionality instead of relying on setting 'RTCVAL'. - Simplified 'set_alarm' and 'read_time/read_alarm' by usage of RTC APIVAL functionality and relying on kernel system time. - Added comment about the available RTC hardware frequency divisors in the RTC driver. - Used both available frequency divisors to increase the RTC rollover time. - Removed a redundant error check on 'priv->irq'. Changes in V6: - Fixed a coding style issue regarding kernel doc reported by kernel test robot - Refactored small sections from the S32G RTC driver without impacting functionality - Fixed an error probe path issue on S32G RTC driver. - Added 'rtc' schema in S32G-RTC bindings Changes in V5: - Removed rollover support. - Removed clock switching support between Runtime and Suspend. A clock source which is always available has been used instead. - Enabled 512 value RTC hardware frequency divisor to achieve higher rollover time - Removed unneeded 'remove' callback. - Decreased driver print verbosity on error paths. - Provided 'clock-names' actual names in bindings documentation - Remove __maybe_unused notations. Used the DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() macros to handle the .suspend/.resume callbacks. - Fixed some alignment issues. Changes in V4: - Dropped 'assigned-*' clock management approach. Simplified RTC Runtime and Suspend/Standby clock configuration. - Simplified error paths on probe function - Removed node label from bindings example - Several cosmetic coding style fixes Changes in V3: - Removed 'nxp,s32g3-rtc' compatible string - Change to 'remove' callback from 'remove_new' - Used 'dev.parent' from 'struct rtc_device' instead of defining a specific 'struct device' in driver data - Fixed several errors reported by kernel test robot - Removed 'assigned-clocks', 'assigned-clock-parents' and 'assigned-clock-rates' from required properties in the binding documentation. - Refactored S32G RTC driver such that a default clock source and divisors configuration will be applied in case 'assigned-clocks' and 'assigned-clock-parents' properties are missing. Changes in V2: - Removed 'clksel' support from dts bindings. Used clock parents support from CCF to better illustrate the RTC hardware IP from S32G2/S32G3. - Removed frequency dividers support from dts bindings. Used assigned clock frequencies support from CCF instead. - Reduced the interval while RTC is voluntarily disabled to a simple register write in order to avoid any race condition between a possbile rollover and 'suspend' callback execution flow. - Updated bindings documentation with respect to clocking support. - Fixed a potential unused variable warning reported by kernel test robot. - Updated to usage of 'devm_rtc_allocate_device' and 'devm_rtc_register_device' instead of deprecated 'devm_rtc_device_register'. Ciprian Marian Costea (4): dt-bindings: rtc: add schema for NXP S32G2/S32G3 SoCs rtc: s32g: add NXP S32G2/S32G3 SoC support arm64: defconfig: add S32G RTC module support MAINTAINERS: add NXP S32G RTC driver .../devicetree/bindings/rtc/nxp,s32g-rtc.yaml | 72 ++++ MAINTAINERS | 2 + arch/arm64/configs/defconfig | 1 + drivers/rtc/Kconfig | 11 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-s32g.c | 385 ++++++++++++++++++ 6 files changed, 472 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml create mode 100644 drivers/rtc/rtc-s32g.c -- 2.45.2 ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v9 1/4] dt-bindings: rtc: add schema for NXP S32G2/S32G3 SoCs 2025-04-03 10:33 [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Ciprian Costea @ 2025-04-03 10:33 ` Ciprian Costea 2025-04-03 10:33 ` [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support Ciprian Costea ` (3 subsequent siblings) 4 siblings, 0 replies; 19+ messages in thread From: Ciprian Costea @ 2025-04-03 10:33 UTC (permalink / raw) To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet, Ciprian Marian Costea, Bogdan-Gabriel Roman, Ghennadi Procopciuc From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> RTC tracks clock time during system suspend and it is used as a wakeup source on S32G2/S32G3 architecture. RTC from S32G2/S32G3 is not battery-powered and it is not kept alive during system reset. Co-developed-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> Signed-off-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> Co-developed-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com> Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> Reviewed-by: Rob Herring (Arm) <robh@kernel.org> --- .../devicetree/bindings/rtc/nxp,s32g-rtc.yaml | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml diff --git a/Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml b/Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml new file mode 100644 index 000000000000..40fd2fa298fe --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/nxp,s32g-rtc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP S32G2/S32G3 Real Time Clock (RTC) + +maintainers: + - Bogdan Hamciuc <bogdan.hamciuc@nxp.com> + - Ciprian Marian Costea <ciprianmarian.costea@nxp.com> + +description: + RTC hardware module present on S32G2/S32G3 SoCs is used as a wakeup source. + It is not kept alive during system reset and it is not battery-powered. + +allOf: + - $ref: rtc.yaml# + +properties: + compatible: + oneOf: + - enum: + - nxp,s32g2-rtc + - items: + - const: nxp,s32g3-rtc + - const: nxp,s32g2-rtc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: ipg clock drives the access to the RTC iomapped registers + - description: Clock source for the RTC module. Can be selected between + 4 different clock sources using an integrated hardware mux. + On S32G2/S32G3 SoCs, 'source0' is the SIRC clock (~32KHz) and it is + available during standby and runtime. 'source1' is reserved and cannot + be used. 'source2' is the FIRC clock and it is only available during + runtime providing a better resolution (~48MHz). 'source3' is an external + RTC clock source which can be additionally added in hardware. + + clock-names: + items: + - const: ipg + - enum: [ source0, source1, source2, source3 ] + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + rtc@40060000 { + compatible = "nxp,s32g3-rtc", + "nxp,s32g2-rtc"; + reg = <0x40060000 0x1000>; + interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 54>, <&clks 55>; + clock-names = "ipg", "source0"; + }; -- 2.45.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support 2025-04-03 10:33 [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Ciprian Costea 2025-04-03 10:33 ` [PATCH v9 1/4] dt-bindings: rtc: add schema for NXP " Ciprian Costea @ 2025-04-03 10:33 ` Ciprian Costea 2025-04-09 14:14 ` Enric Balletbo i Serra 2025-04-03 10:33 ` [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support Ciprian Costea ` (2 subsequent siblings) 4 siblings, 1 reply; 19+ messages in thread From: Ciprian Costea @ 2025-04-03 10:33 UTC (permalink / raw) To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet, Ciprian Marian Costea, Bogdan Hamciuc, Ghennadi Procopciuc, Frank Li From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> Add a RTC driver for NXP S32G2/S32G3 SoCs. RTC tracks clock time during system suspend. It can be a wakeup source for the S32G2/S32G3 SoC based boards. The RTC module from S32G2/S32G3 is not battery-powered and it is not kept alive during system reset. Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> --- drivers/rtc/Kconfig | 11 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-s32g.c | 385 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 drivers/rtc/rtc-s32g.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 0bbbf778ecfa..510dc2db745d 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -2103,4 +2103,15 @@ config RTC_DRV_AMLOGIC_A4 This driver can also be built as a module. If so, the module will be called "rtc-amlogic-a4". +config RTC_DRV_S32G + tristate "RTC driver for S32G2/S32G3 SoCs" + depends on ARCH_S32 || COMPILE_TEST + depends on COMMON_CLK + help + Say yes to enable RTC driver for platforms based on the + S32G2/S32G3 SoC family. + + This RTC module can be used as a wakeup source. + Please note that it is not battery-powered. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 489b4ab07068..e4b616ecd5ce 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c new file mode 100644 index 000000000000..3a0818e972eb --- /dev/null +++ b/drivers/rtc/rtc-s32g.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2025 NXP + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#define RTCC_OFFSET 0x4ul +#define RTCS_OFFSET 0x8ul +#define APIVAL_OFFSET 0x10ul + +/* RTCC fields */ +#define RTCC_CNTEN BIT(31) +#define RTCC_APIEN BIT(15) +#define RTCC_APIIE BIT(14) +#define RTCC_CLKSEL_MASK GENMASK(13, 12) +#define RTCC_DIV512EN BIT(11) +#define RTCC_DIV32EN BIT(10) + +/* RTCS fields */ +#define RTCS_INV_API BIT(17) +#define RTCS_APIF BIT(13) + +#define APIVAL_MAX_VAL GENMASK(31, 0) +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) + +/* + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and + * should not be used. + */ +#define RTC_CLK_SRC1_RESERVED BIT(1) + +/* + * S32G RTC module has a 512 value and a 32 value hardware frequency + * divisors (DIV512 and DIV32) which could be used to achieve higher + * counter ranges by lowering the RTC frequency. + */ +enum { + DIV1 = 1, + DIV32 = 32, + DIV512 = 512, + DIV512_32 = 16384 +}; + +static const char *const rtc_clk_src[] = { + "source0", + "source1", + "source2", + "source3" +}; + +struct rtc_priv { + struct rtc_device *rdev; + void __iomem *rtc_base; + struct clk *ipg; + struct clk *clk_src; + const struct rtc_soc_data *rtc_data; + u64 rtc_hz; + time64_t sleep_sec; + int irq; + u32 clk_src_idx; +}; + +struct rtc_soc_data { + u32 clk_div; + u32 reserved_clk_mask; +}; + +static const struct rtc_soc_data rtc_s32g2_data = { + .clk_div = DIV512_32, + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, +}; + +static irqreturn_t s32g_rtc_handler(int irq, void *dev) +{ + struct rtc_priv *priv = platform_get_drvdata(dev); + u32 status; + + status = readl(priv->rtc_base + RTCS_OFFSET); + + if (status & RTCS_APIF) { + writel(0x0, priv->rtc_base + APIVAL_OFFSET); + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); + } + + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +/* + * The function is not really getting time from the RTC since the S32G RTC + * has several limitations. Thus, to setup alarm use system time. + */ +static int s32g_rtc_read_time(struct device *dev, + struct rtc_time *tm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + time64_t sec; + + if (check_add_overflow(ktime_get_real_seconds(), + priv->sleep_sec, &sec)) + return -ERANGE; + + rtc_time64_to_tm(sec, tm); + + return 0; +} + +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc, rtcs; + + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcs = readl(priv->rtc_base + RTCS_OFFSET); + + alrm->enabled = rtcc & RTCC_APIIE; + if (alrm->enabled) + alrm->pending = !(rtcs & RTCS_APIF); + + return 0; +} + +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc; + + /* RTC API functionality is used both for triggering interrupts + * and as a wakeup event. Hence it should always be enabled. + */ + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcc |= RTCC_APIEN | RTCC_APIIE; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + + return 0; +} + +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + unsigned long long cycles; + long long t_offset; + time64_t alrm_time; + u32 rtcs; + int ret; + + alrm_time = rtc_tm_to_time64(&alrm->time); + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; + if (t_offset < 0) + return -ERANGE; + + cycles = t_offset * priv->rtc_hz; + if (cycles > APIVAL_MAX_VAL) + return -ERANGE; + + /* APIVAL could have been reset from the IRQ handler. + * Hence, we wait in case there is a synchronization process. + */ + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); + if (ret) + return ret; + + writel(cycles, priv->rtc_base + APIVAL_OFFSET); + + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); +} + +/* + * Disable the 32-bit free running counter. + * This allows Clock Source and Divisors selection + * to be performed without causing synchronization issues. + */ +static void s32g_rtc_disable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc &= ~RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static void s32g_rtc_enable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc |= RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static int rtc_clk_src_setup(struct rtc_priv *priv) +{ + u32 rtcc; + + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); + + switch (priv->rtc_data->clk_div) { + case DIV512_32: + rtcc |= RTCC_DIV512EN; + rtcc |= RTCC_DIV32EN; + break; + case DIV512: + rtcc |= RTCC_DIV512EN; + break; + case DIV32: + rtcc |= RTCC_DIV32EN; + break; + case DIV1: + break; + default: + return -EINVAL; + } + + rtcc |= RTCC_APIEN | RTCC_APIIE; + /* + * Make sure the CNTEN is 0 before we configure + * the clock source and dividers. + */ + s32g_rtc_disable(priv); + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + s32g_rtc_enable(priv); + + return 0; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = s32g_rtc_read_time, + .read_alarm = s32g_rtc_read_alarm, + .set_alarm = s32g_rtc_set_alarm, + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, +}; + +static int rtc_clk_dts_setup(struct rtc_priv *priv, + struct device *dev) +{ + u32 i; + + priv->ipg = devm_clk_get_enabled(dev, "ipg"); + if (IS_ERR(priv->ipg)) + return dev_err_probe(dev, PTR_ERR(priv->ipg), + "Failed to get 'ipg' clock\n"); + + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { + if (priv->rtc_data->reserved_clk_mask & BIT(i)) + return -EOPNOTSUPP; + + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); + if (!IS_ERR(priv->clk_src)) { + priv->clk_src_idx = i; + break; + } + } + + if (IS_ERR(priv->clk_src)) + return dev_err_probe(dev, PTR_ERR(priv->clk_src), + "Failed to get rtc module clock source\n"); + + return 0; +} + +static int s32g_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtc_priv *priv; + unsigned long rtc_hz; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rtc_data = of_device_get_match_data(dev); + if (!priv->rtc_data) + return -ENODEV; + + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->rtc_base)) + return PTR_ERR(priv->rtc_base); + + device_init_wakeup(dev, true); + + ret = rtc_clk_dts_setup(priv, dev); + if (ret) + return ret; + + priv->rdev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rdev)) + return PTR_ERR(priv->rdev); + + ret = rtc_clk_src_setup(priv); + if (ret) + return ret; + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + ret = priv->irq; + goto disable_rtc; + } + + rtc_hz = clk_get_rate(priv->clk_src); + if (!rtc_hz) { + dev_err(dev, "Failed to get RTC frequency\n"); + ret = -EINVAL; + goto disable_rtc; + } + + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); + + platform_set_drvdata(pdev, priv); + priv->rdev->ops = &rtc_ops; + + ret = devm_request_irq(dev, priv->irq, + s32g_rtc_handler, 0, dev_name(dev), pdev); + if (ret) { + dev_err(dev, "Request interrupt %d failed, error: %d\n", + priv->irq, ret); + goto disable_rtc; + } + + ret = devm_rtc_register_device(priv->rdev); + if (ret) + goto disable_rtc; + + return 0; + +disable_rtc: + s32g_rtc_disable(priv); + return ret; +} + +static int s32g_rtc_suspend(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); + + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), + &priv->sleep_sec)) { + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); + priv->sleep_sec = 0; + } + + return 0; +} + +static int s32g_rtc_resume(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + + /* The transition from resume to run is a reset event. + * This leads to the RTC registers being reset after resume from + * suspend. It is uncommon, but this behaviour has been observed + * on S32G RTC after issuing a Suspend to RAM operation. + * Thus, reconfigure RTC registers on the resume path. + */ + return rtc_clk_src_setup(priv); +} + +static const struct of_device_id rtc_dt_ids[] = { + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, + { /* sentinel */ }, +}; + +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, + s32g_rtc_suspend, s32g_rtc_resume); + +static struct platform_driver s32g_rtc_driver = { + .driver = { + .name = "s32g-rtc", + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), + .of_match_table = rtc_dt_ids, + }, + .probe = s32g_rtc_probe, +}; +module_platform_driver(s32g_rtc_driver); + +MODULE_AUTHOR("NXP"); +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); +MODULE_LICENSE("GPL"); -- 2.45.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support 2025-04-03 10:33 ` [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support Ciprian Costea @ 2025-04-09 14:14 ` Enric Balletbo i Serra 2025-04-09 14:26 ` Ciprian Marian Costea 0 siblings, 1 reply; 19+ messages in thread From: Enric Balletbo i Serra @ 2025-04-09 14:14 UTC (permalink / raw) To: Ciprian Costea Cc: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Eric Chanudet, Bogdan Hamciuc, Ghennadi Procopciuc, Frank Li Hi Ciprian, Many thanks for your patch. On Thu, Apr 3, 2025 at 12:34 PM Ciprian Costea <ciprianmarian.costea@oss.nxp.com> wrote: > > From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > > Add a RTC driver for NXP S32G2/S32G3 SoCs. > > RTC tracks clock time during system suspend. It can be a wakeup source > for the S32G2/S32G3 SoC based boards. > > The RTC module from S32G2/S32G3 is not battery-powered and it is not kept > alive during system reset. > > Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > Reviewed-by: Frank Li <Frank.Li@nxp.com> I went through the code and it looks good to me, so: However, I did a basic rtctest selftest in my S32G-VNP-RDB3 board, and some tests failed (it shows a small deviation), any idea what could be wrong? This is the output of the test: # ./rtctest TAP version 13 1..8 # Starting 8 tests from 1 test cases. # RUN rtc.date_read ... # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:17:49. # OK rtc.date_read ok 1 rtc.date_read # RUN rtc.date_read_loop ... # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s (with 11ms breaks after every read). # rtctest.c:151:date_read_loop:Performed 2638 RTC time reads. # OK rtc.date_read_loop ok 2 rtc.date_read_loop # RUN rtc.uie_read ... # OK rtc.uie_read ok 3 rtc.uie_read # RUN rtc.uie_select ... # OK rtc.uie_select ok 4 rtc.uie_select # RUN rtc.alarm_alm_set ... # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:18:31. # rtctest.c:282:alarm_alm_set:data: 1a0 # OK rtc.alarm_alm_set ok 5 rtc.alarm_alm_set # RUN rtc.alarm_wkalm_set ... # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:18:34. # rtctest.c:353:alarm_wkalm_set:Expected new (1738023515) == secs (1738023514) # alarm_wkalm_set: Test terminated by assertion # FAIL rtc.alarm_wkalm_set not ok 6 rtc.alarm_wkalm_set # RUN rtc.alarm_alm_set_minute ... # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:19:00. # rtctest.c:414:alarm_alm_set_minute:data: 1a0 # rtctest.c:420:alarm_alm_set_minute:Expected new (1738023541) == secs (1738023540) # alarm_alm_set_minute: Test terminated by assertion # FAIL rtc.alarm_alm_set_minute not ok 7 rtc.alarm_alm_set_minute # RUN rtc.alarm_wkalm_set_minute ... # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to 28/01/2025 00:20:00. # rtctest.c:483:alarm_wkalm_set_minute:Expected new (1738023601) == secs (1738023600) # alarm_wkalm_set_minute: Test terminated by assertion # FAIL rtc.alarm_wkalm_set_minute not ok 8 rtc.alarm_wkalm_set_minute # FAILED: 5 / 8 tests passed. # Totals: pass:5 fail:3 xfail:0 xpass:0 skip:0 error:0 Best regards, Enric > --- > drivers/rtc/Kconfig | 11 ++ > drivers/rtc/Makefile | 1 + > drivers/rtc/rtc-s32g.c | 385 +++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 397 insertions(+) > create mode 100644 drivers/rtc/rtc-s32g.c > > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > index 0bbbf778ecfa..510dc2db745d 100644 > --- a/drivers/rtc/Kconfig > +++ b/drivers/rtc/Kconfig > @@ -2103,4 +2103,15 @@ config RTC_DRV_AMLOGIC_A4 > This driver can also be built as a module. If so, the module > will be called "rtc-amlogic-a4". > > +config RTC_DRV_S32G > + tristate "RTC driver for S32G2/S32G3 SoCs" > + depends on ARCH_S32 || COMPILE_TEST > + depends on COMMON_CLK > + help > + Say yes to enable RTC driver for platforms based on the > + S32G2/S32G3 SoC family. > + > + This RTC module can be used as a wakeup source. > + Please note that it is not battery-powered. > + > endif # RTC_CLASS > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > index 489b4ab07068..e4b616ecd5ce 100644 > --- a/drivers/rtc/Makefile > +++ b/drivers/rtc/Makefile > @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o > obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o > obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o > obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o > +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o > obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o > obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o > obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o > diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c > new file mode 100644 > index 000000000000..3a0818e972eb > --- /dev/null > +++ b/drivers/rtc/rtc-s32g.c > @@ -0,0 +1,385 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright 2025 NXP > + */ > + > +#include <linux/bitfield.h> > +#include <linux/clk.h> > +#include <linux/iopoll.h> > +#include <linux/of_irq.h> > +#include <linux/platform_device.h> > +#include <linux/rtc.h> > + > +#define RTCC_OFFSET 0x4ul > +#define RTCS_OFFSET 0x8ul > +#define APIVAL_OFFSET 0x10ul > + > +/* RTCC fields */ > +#define RTCC_CNTEN BIT(31) > +#define RTCC_APIEN BIT(15) > +#define RTCC_APIIE BIT(14) > +#define RTCC_CLKSEL_MASK GENMASK(13, 12) > +#define RTCC_DIV512EN BIT(11) > +#define RTCC_DIV32EN BIT(10) > + > +/* RTCS fields */ > +#define RTCS_INV_API BIT(17) > +#define RTCS_APIF BIT(13) > + > +#define APIVAL_MAX_VAL GENMASK(31, 0) > +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) > + > +/* > + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and > + * should not be used. > + */ > +#define RTC_CLK_SRC1_RESERVED BIT(1) > + > +/* > + * S32G RTC module has a 512 value and a 32 value hardware frequency > + * divisors (DIV512 and DIV32) which could be used to achieve higher > + * counter ranges by lowering the RTC frequency. > + */ > +enum { > + DIV1 = 1, > + DIV32 = 32, > + DIV512 = 512, > + DIV512_32 = 16384 > +}; > + > +static const char *const rtc_clk_src[] = { > + "source0", > + "source1", > + "source2", > + "source3" > +}; > + > +struct rtc_priv { > + struct rtc_device *rdev; > + void __iomem *rtc_base; > + struct clk *ipg; > + struct clk *clk_src; > + const struct rtc_soc_data *rtc_data; > + u64 rtc_hz; > + time64_t sleep_sec; > + int irq; > + u32 clk_src_idx; > +}; > + > +struct rtc_soc_data { > + u32 clk_div; > + u32 reserved_clk_mask; > +}; > + > +static const struct rtc_soc_data rtc_s32g2_data = { > + .clk_div = DIV512_32, > + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, > +}; > + > +static irqreturn_t s32g_rtc_handler(int irq, void *dev) > +{ > + struct rtc_priv *priv = platform_get_drvdata(dev); > + u32 status; > + > + status = readl(priv->rtc_base + RTCS_OFFSET); > + > + if (status & RTCS_APIF) { > + writel(0x0, priv->rtc_base + APIVAL_OFFSET); > + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); > + } > + > + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); > + > + return IRQ_HANDLED; > +} > + > +/* > + * The function is not really getting time from the RTC since the S32G RTC > + * has several limitations. Thus, to setup alarm use system time. > + */ > +static int s32g_rtc_read_time(struct device *dev, > + struct rtc_time *tm) > +{ > + struct rtc_priv *priv = dev_get_drvdata(dev); > + time64_t sec; > + > + if (check_add_overflow(ktime_get_real_seconds(), > + priv->sleep_sec, &sec)) > + return -ERANGE; > + > + rtc_time64_to_tm(sec, tm); > + > + return 0; > +} > + > +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) > +{ > + struct rtc_priv *priv = dev_get_drvdata(dev); > + u32 rtcc, rtcs; > + > + rtcc = readl(priv->rtc_base + RTCC_OFFSET); > + rtcs = readl(priv->rtc_base + RTCS_OFFSET); > + > + alrm->enabled = rtcc & RTCC_APIIE; > + if (alrm->enabled) > + alrm->pending = !(rtcs & RTCS_APIF); > + > + return 0; > +} > + > +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) > +{ > + struct rtc_priv *priv = dev_get_drvdata(dev); > + u32 rtcc; > + > + /* RTC API functionality is used both for triggering interrupts > + * and as a wakeup event. Hence it should always be enabled. > + */ > + rtcc = readl(priv->rtc_base + RTCC_OFFSET); > + rtcc |= RTCC_APIEN | RTCC_APIIE; > + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > + > + return 0; > +} > + > +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) > +{ > + struct rtc_priv *priv = dev_get_drvdata(dev); > + unsigned long long cycles; > + long long t_offset; > + time64_t alrm_time; > + u32 rtcs; > + int ret; > + > + alrm_time = rtc_tm_to_time64(&alrm->time); > + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; > + if (t_offset < 0) > + return -ERANGE; > + > + cycles = t_offset * priv->rtc_hz; > + if (cycles > APIVAL_MAX_VAL) > + return -ERANGE; > + > + /* APIVAL could have been reset from the IRQ handler. > + * Hence, we wait in case there is a synchronization process. > + */ > + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), > + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); > + if (ret) > + return ret; > + > + writel(cycles, priv->rtc_base + APIVAL_OFFSET); > + > + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), > + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); > +} > + > +/* > + * Disable the 32-bit free running counter. > + * This allows Clock Source and Divisors selection > + * to be performed without causing synchronization issues. > + */ > +static void s32g_rtc_disable(struct rtc_priv *priv) > +{ > + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); > + > + rtcc &= ~RTCC_CNTEN; > + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > +} > + > +static void s32g_rtc_enable(struct rtc_priv *priv) > +{ > + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); > + > + rtcc |= RTCC_CNTEN; > + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > +} > + > +static int rtc_clk_src_setup(struct rtc_priv *priv) > +{ > + u32 rtcc; > + > + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); > + > + switch (priv->rtc_data->clk_div) { > + case DIV512_32: > + rtcc |= RTCC_DIV512EN; > + rtcc |= RTCC_DIV32EN; > + break; > + case DIV512: > + rtcc |= RTCC_DIV512EN; > + break; > + case DIV32: > + rtcc |= RTCC_DIV32EN; > + break; > + case DIV1: > + break; > + default: > + return -EINVAL; > + } > + > + rtcc |= RTCC_APIEN | RTCC_APIIE; > + /* > + * Make sure the CNTEN is 0 before we configure > + * the clock source and dividers. > + */ > + s32g_rtc_disable(priv); > + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > + s32g_rtc_enable(priv); > + > + return 0; > +} > + > +static const struct rtc_class_ops rtc_ops = { > + .read_time = s32g_rtc_read_time, > + .read_alarm = s32g_rtc_read_alarm, > + .set_alarm = s32g_rtc_set_alarm, > + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, > +}; > + > +static int rtc_clk_dts_setup(struct rtc_priv *priv, > + struct device *dev) > +{ > + u32 i; > + > + priv->ipg = devm_clk_get_enabled(dev, "ipg"); > + if (IS_ERR(priv->ipg)) > + return dev_err_probe(dev, PTR_ERR(priv->ipg), > + "Failed to get 'ipg' clock\n"); > + > + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { > + if (priv->rtc_data->reserved_clk_mask & BIT(i)) > + return -EOPNOTSUPP; > + > + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); > + if (!IS_ERR(priv->clk_src)) { > + priv->clk_src_idx = i; > + break; > + } > + } > + > + if (IS_ERR(priv->clk_src)) > + return dev_err_probe(dev, PTR_ERR(priv->clk_src), > + "Failed to get rtc module clock source\n"); > + > + return 0; > +} > + > +static int s32g_rtc_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct rtc_priv *priv; > + unsigned long rtc_hz; > + int ret; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->rtc_data = of_device_get_match_data(dev); > + if (!priv->rtc_data) > + return -ENODEV; > + > + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(priv->rtc_base)) > + return PTR_ERR(priv->rtc_base); > + > + device_init_wakeup(dev, true); > + > + ret = rtc_clk_dts_setup(priv, dev); > + if (ret) > + return ret; > + > + priv->rdev = devm_rtc_allocate_device(dev); > + if (IS_ERR(priv->rdev)) > + return PTR_ERR(priv->rdev); > + > + ret = rtc_clk_src_setup(priv); > + if (ret) > + return ret; > + > + priv->irq = platform_get_irq(pdev, 0); > + if (priv->irq < 0) { > + ret = priv->irq; > + goto disable_rtc; > + } > + > + rtc_hz = clk_get_rate(priv->clk_src); > + if (!rtc_hz) { > + dev_err(dev, "Failed to get RTC frequency\n"); > + ret = -EINVAL; > + goto disable_rtc; > + } > + > + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); > + > + platform_set_drvdata(pdev, priv); > + priv->rdev->ops = &rtc_ops; > + > + ret = devm_request_irq(dev, priv->irq, > + s32g_rtc_handler, 0, dev_name(dev), pdev); > + if (ret) { > + dev_err(dev, "Request interrupt %d failed, error: %d\n", > + priv->irq, ret); > + goto disable_rtc; > + } > + > + ret = devm_rtc_register_device(priv->rdev); > + if (ret) > + goto disable_rtc; > + > + return 0; > + > +disable_rtc: > + s32g_rtc_disable(priv); > + return ret; > +} > + > +static int s32g_rtc_suspend(struct device *dev) > +{ > + struct rtc_priv *priv = dev_get_drvdata(dev); > + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); > + > + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), > + &priv->sleep_sec)) { > + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); > + priv->sleep_sec = 0; > + } > + > + return 0; > +} > + > +static int s32g_rtc_resume(struct device *dev) > +{ > + struct rtc_priv *priv = dev_get_drvdata(dev); > + > + /* The transition from resume to run is a reset event. > + * This leads to the RTC registers being reset after resume from > + * suspend. It is uncommon, but this behaviour has been observed > + * on S32G RTC after issuing a Suspend to RAM operation. > + * Thus, reconfigure RTC registers on the resume path. > + */ > + return rtc_clk_src_setup(priv); > +} > + > +static const struct of_device_id rtc_dt_ids[] = { > + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, > + { /* sentinel */ }, > +}; > + > +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, > + s32g_rtc_suspend, s32g_rtc_resume); > + > +static struct platform_driver s32g_rtc_driver = { > + .driver = { > + .name = "s32g-rtc", > + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), > + .of_match_table = rtc_dt_ids, > + }, > + .probe = s32g_rtc_probe, > +}; > +module_platform_driver(s32g_rtc_driver); > + > +MODULE_AUTHOR("NXP"); > +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); > +MODULE_LICENSE("GPL"); > -- > 2.45.2 > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support 2025-04-09 14:14 ` Enric Balletbo i Serra @ 2025-04-09 14:26 ` Ciprian Marian Costea 2025-04-09 14:42 ` Enric Balletbo i Serra 0 siblings, 1 reply; 19+ messages in thread From: Ciprian Marian Costea @ 2025-04-09 14:26 UTC (permalink / raw) To: Enric Balletbo i Serra Cc: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Eric Chanudet, Bogdan Hamciuc, Ghennadi Procopciuc, Frank Li On 4/9/2025 5:14 PM, Enric Balletbo i Serra wrote: > Hi Ciprian, > > Many thanks for your patch. > > On Thu, Apr 3, 2025 at 12:34 PM Ciprian Costea > <ciprianmarian.costea@oss.nxp.com> wrote: >> >> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >> >> Add a RTC driver for NXP S32G2/S32G3 SoCs. >> >> RTC tracks clock time during system suspend. It can be a wakeup source >> for the S32G2/S32G3 SoC based boards. >> >> The RTC module from S32G2/S32G3 is not battery-powered and it is not kept >> alive during system reset. >> >> Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> >> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> >> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> >> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> >> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >> Reviewed-by: Frank Li <Frank.Li@nxp.com> > > I went through the code and it looks good to me, so: > > However, I did a basic rtctest selftest in my S32G-VNP-RDB3 board, > and some tests failed (it shows a small deviation), any idea what > could be wrong? > > This is the output of the test: > > # ./rtctest > TAP version 13 > 1..8 > # Starting 8 tests from 1 test cases. > # RUN rtc.date_read ... > # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:17:49. > # OK rtc.date_read > ok 1 rtc.date_read > # RUN rtc.date_read_loop ... > # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s > (with 11ms breaks after every read). > # rtctest.c:151:date_read_loop:Performed 2638 RTC time reads. > # OK rtc.date_read_loop > ok 2 rtc.date_read_loop > # RUN rtc.uie_read ... > # OK rtc.uie_read > ok 3 rtc.uie_read > # RUN rtc.uie_select ... > # OK rtc.uie_select > ok 4 rtc.uie_select > # RUN rtc.alarm_alm_set ... > # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:18:31. > # rtctest.c:282:alarm_alm_set:data: 1a0 > # OK rtc.alarm_alm_set > ok 5 rtc.alarm_alm_set > # RUN rtc.alarm_wkalm_set ... > # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:18:34. > # rtctest.c:353:alarm_wkalm_set:Expected new (1738023515) == secs (1738023514) > # alarm_wkalm_set: Test terminated by assertion > # FAIL rtc.alarm_wkalm_set > not ok 6 rtc.alarm_wkalm_set > # RUN rtc.alarm_alm_set_minute ... > # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:19:00. > # rtctest.c:414:alarm_alm_set_minute:data: 1a0 > # rtctest.c:420:alarm_alm_set_minute:Expected new (1738023541) == secs > (1738023540) > # alarm_alm_set_minute: Test terminated by assertion > # FAIL rtc.alarm_alm_set_minute > not ok 7 rtc.alarm_alm_set_minute > # RUN rtc.alarm_wkalm_set_minute ... > # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to > 28/01/2025 00:20:00. > # rtctest.c:483:alarm_wkalm_set_minute:Expected new (1738023601) == > secs (1738023600) > # alarm_wkalm_set_minute: Test terminated by assertion > # FAIL rtc.alarm_wkalm_set_minute > not ok 8 rtc.alarm_wkalm_set_minute > # FAILED: 5 / 8 tests passed. > # Totals: pass:5 fail:3 xfail:0 xpass:0 skip:0 error:0 > > Best regards, > Enric > Hello Enric, In order to obtain a better resolution/precision you could try to only use DIV512 hardware divisor instead of both DIV32 and DIV512. Regards, Ciprian >> --- >> drivers/rtc/Kconfig | 11 ++ >> drivers/rtc/Makefile | 1 + >> drivers/rtc/rtc-s32g.c | 385 +++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 397 insertions(+) >> create mode 100644 drivers/rtc/rtc-s32g.c >> >> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig >> index 0bbbf778ecfa..510dc2db745d 100644 >> --- a/drivers/rtc/Kconfig >> +++ b/drivers/rtc/Kconfig >> @@ -2103,4 +2103,15 @@ config RTC_DRV_AMLOGIC_A4 >> This driver can also be built as a module. If so, the module >> will be called "rtc-amlogic-a4". >> >> +config RTC_DRV_S32G >> + tristate "RTC driver for S32G2/S32G3 SoCs" >> + depends on ARCH_S32 || COMPILE_TEST >> + depends on COMMON_CLK >> + help >> + Say yes to enable RTC driver for platforms based on the >> + S32G2/S32G3 SoC family. >> + >> + This RTC module can be used as a wakeup source. >> + Please note that it is not battery-powered. >> + >> endif # RTC_CLASS >> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile >> index 489b4ab07068..e4b616ecd5ce 100644 >> --- a/drivers/rtc/Makefile >> +++ b/drivers/rtc/Makefile >> @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o >> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o >> obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o >> obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o >> +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o >> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o >> obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o >> obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o >> diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c >> new file mode 100644 >> index 000000000000..3a0818e972eb >> --- /dev/null >> +++ b/drivers/rtc/rtc-s32g.c >> @@ -0,0 +1,385 @@ >> +// SPDX-License-Identifier: GPL-2.0-or-later >> +/* >> + * Copyright 2025 NXP >> + */ >> + >> +#include <linux/bitfield.h> >> +#include <linux/clk.h> >> +#include <linux/iopoll.h> >> +#include <linux/of_irq.h> >> +#include <linux/platform_device.h> >> +#include <linux/rtc.h> >> + >> +#define RTCC_OFFSET 0x4ul >> +#define RTCS_OFFSET 0x8ul >> +#define APIVAL_OFFSET 0x10ul >> + >> +/* RTCC fields */ >> +#define RTCC_CNTEN BIT(31) >> +#define RTCC_APIEN BIT(15) >> +#define RTCC_APIIE BIT(14) >> +#define RTCC_CLKSEL_MASK GENMASK(13, 12) >> +#define RTCC_DIV512EN BIT(11) >> +#define RTCC_DIV32EN BIT(10) >> + >> +/* RTCS fields */ >> +#define RTCS_INV_API BIT(17) >> +#define RTCS_APIF BIT(13) >> + >> +#define APIVAL_MAX_VAL GENMASK(31, 0) >> +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) >> + >> +/* >> + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and >> + * should not be used. >> + */ >> +#define RTC_CLK_SRC1_RESERVED BIT(1) >> + >> +/* >> + * S32G RTC module has a 512 value and a 32 value hardware frequency >> + * divisors (DIV512 and DIV32) which could be used to achieve higher >> + * counter ranges by lowering the RTC frequency. >> + */ >> +enum { >> + DIV1 = 1, >> + DIV32 = 32, >> + DIV512 = 512, >> + DIV512_32 = 16384 >> +}; >> + >> +static const char *const rtc_clk_src[] = { >> + "source0", >> + "source1", >> + "source2", >> + "source3" >> +}; >> + >> +struct rtc_priv { >> + struct rtc_device *rdev; >> + void __iomem *rtc_base; >> + struct clk *ipg; >> + struct clk *clk_src; >> + const struct rtc_soc_data *rtc_data; >> + u64 rtc_hz; >> + time64_t sleep_sec; >> + int irq; >> + u32 clk_src_idx; >> +}; >> + >> +struct rtc_soc_data { >> + u32 clk_div; >> + u32 reserved_clk_mask; >> +}; >> + >> +static const struct rtc_soc_data rtc_s32g2_data = { >> + .clk_div = DIV512_32, >> + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, >> +}; >> + >> +static irqreturn_t s32g_rtc_handler(int irq, void *dev) >> +{ >> + struct rtc_priv *priv = platform_get_drvdata(dev); >> + u32 status; >> + >> + status = readl(priv->rtc_base + RTCS_OFFSET); >> + >> + if (status & RTCS_APIF) { >> + writel(0x0, priv->rtc_base + APIVAL_OFFSET); >> + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); >> + } >> + >> + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); >> + >> + return IRQ_HANDLED; >> +} >> + >> +/* >> + * The function is not really getting time from the RTC since the S32G RTC >> + * has several limitations. Thus, to setup alarm use system time. >> + */ >> +static int s32g_rtc_read_time(struct device *dev, >> + struct rtc_time *tm) >> +{ >> + struct rtc_priv *priv = dev_get_drvdata(dev); >> + time64_t sec; >> + >> + if (check_add_overflow(ktime_get_real_seconds(), >> + priv->sleep_sec, &sec)) >> + return -ERANGE; >> + >> + rtc_time64_to_tm(sec, tm); >> + >> + return 0; >> +} >> + >> +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) >> +{ >> + struct rtc_priv *priv = dev_get_drvdata(dev); >> + u32 rtcc, rtcs; >> + >> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); >> + rtcs = readl(priv->rtc_base + RTCS_OFFSET); >> + >> + alrm->enabled = rtcc & RTCC_APIIE; >> + if (alrm->enabled) >> + alrm->pending = !(rtcs & RTCS_APIF); >> + >> + return 0; >> +} >> + >> +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) >> +{ >> + struct rtc_priv *priv = dev_get_drvdata(dev); >> + u32 rtcc; >> + >> + /* RTC API functionality is used both for triggering interrupts >> + * and as a wakeup event. Hence it should always be enabled. >> + */ >> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); >> + rtcc |= RTCC_APIEN | RTCC_APIIE; >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >> + >> + return 0; >> +} >> + >> +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) >> +{ >> + struct rtc_priv *priv = dev_get_drvdata(dev); >> + unsigned long long cycles; >> + long long t_offset; >> + time64_t alrm_time; >> + u32 rtcs; >> + int ret; >> + >> + alrm_time = rtc_tm_to_time64(&alrm->time); >> + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; >> + if (t_offset < 0) >> + return -ERANGE; >> + >> + cycles = t_offset * priv->rtc_hz; >> + if (cycles > APIVAL_MAX_VAL) >> + return -ERANGE; >> + >> + /* APIVAL could have been reset from the IRQ handler. >> + * Hence, we wait in case there is a synchronization process. >> + */ >> + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), >> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); >> + if (ret) >> + return ret; >> + >> + writel(cycles, priv->rtc_base + APIVAL_OFFSET); >> + >> + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), >> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); >> +} >> + >> +/* >> + * Disable the 32-bit free running counter. >> + * This allows Clock Source and Divisors selection >> + * to be performed without causing synchronization issues. >> + */ >> +static void s32g_rtc_disable(struct rtc_priv *priv) >> +{ >> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); >> + >> + rtcc &= ~RTCC_CNTEN; >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >> +} >> + >> +static void s32g_rtc_enable(struct rtc_priv *priv) >> +{ >> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); >> + >> + rtcc |= RTCC_CNTEN; >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >> +} >> + >> +static int rtc_clk_src_setup(struct rtc_priv *priv) >> +{ >> + u32 rtcc; >> + >> + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); >> + >> + switch (priv->rtc_data->clk_div) { >> + case DIV512_32: >> + rtcc |= RTCC_DIV512EN; >> + rtcc |= RTCC_DIV32EN; >> + break; >> + case DIV512: >> + rtcc |= RTCC_DIV512EN; >> + break; >> + case DIV32: >> + rtcc |= RTCC_DIV32EN; >> + break; >> + case DIV1: >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + rtcc |= RTCC_APIEN | RTCC_APIIE; >> + /* >> + * Make sure the CNTEN is 0 before we configure >> + * the clock source and dividers. >> + */ >> + s32g_rtc_disable(priv); >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >> + s32g_rtc_enable(priv); >> + >> + return 0; >> +} >> + >> +static const struct rtc_class_ops rtc_ops = { >> + .read_time = s32g_rtc_read_time, >> + .read_alarm = s32g_rtc_read_alarm, >> + .set_alarm = s32g_rtc_set_alarm, >> + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, >> +}; >> + >> +static int rtc_clk_dts_setup(struct rtc_priv *priv, >> + struct device *dev) >> +{ >> + u32 i; >> + >> + priv->ipg = devm_clk_get_enabled(dev, "ipg"); >> + if (IS_ERR(priv->ipg)) >> + return dev_err_probe(dev, PTR_ERR(priv->ipg), >> + "Failed to get 'ipg' clock\n"); >> + >> + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { >> + if (priv->rtc_data->reserved_clk_mask & BIT(i)) >> + return -EOPNOTSUPP; >> + >> + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); >> + if (!IS_ERR(priv->clk_src)) { >> + priv->clk_src_idx = i; >> + break; >> + } >> + } >> + >> + if (IS_ERR(priv->clk_src)) >> + return dev_err_probe(dev, PTR_ERR(priv->clk_src), >> + "Failed to get rtc module clock source\n"); >> + >> + return 0; >> +} >> + >> +static int s32g_rtc_probe(struct platform_device *pdev) >> +{ >> + struct device *dev = &pdev->dev; >> + struct rtc_priv *priv; >> + unsigned long rtc_hz; >> + int ret; >> + >> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); >> + if (!priv) >> + return -ENOMEM; >> + >> + priv->rtc_data = of_device_get_match_data(dev); >> + if (!priv->rtc_data) >> + return -ENODEV; >> + >> + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); >> + if (IS_ERR(priv->rtc_base)) >> + return PTR_ERR(priv->rtc_base); >> + >> + device_init_wakeup(dev, true); >> + >> + ret = rtc_clk_dts_setup(priv, dev); >> + if (ret) >> + return ret; >> + >> + priv->rdev = devm_rtc_allocate_device(dev); >> + if (IS_ERR(priv->rdev)) >> + return PTR_ERR(priv->rdev); >> + >> + ret = rtc_clk_src_setup(priv); >> + if (ret) >> + return ret; >> + >> + priv->irq = platform_get_irq(pdev, 0); >> + if (priv->irq < 0) { >> + ret = priv->irq; >> + goto disable_rtc; >> + } >> + >> + rtc_hz = clk_get_rate(priv->clk_src); >> + if (!rtc_hz) { >> + dev_err(dev, "Failed to get RTC frequency\n"); >> + ret = -EINVAL; >> + goto disable_rtc; >> + } >> + >> + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); >> + >> + platform_set_drvdata(pdev, priv); >> + priv->rdev->ops = &rtc_ops; >> + >> + ret = devm_request_irq(dev, priv->irq, >> + s32g_rtc_handler, 0, dev_name(dev), pdev); >> + if (ret) { >> + dev_err(dev, "Request interrupt %d failed, error: %d\n", >> + priv->irq, ret); >> + goto disable_rtc; >> + } >> + >> + ret = devm_rtc_register_device(priv->rdev); >> + if (ret) >> + goto disable_rtc; >> + >> + return 0; >> + >> +disable_rtc: >> + s32g_rtc_disable(priv); >> + return ret; >> +} >> + >> +static int s32g_rtc_suspend(struct device *dev) >> +{ >> + struct rtc_priv *priv = dev_get_drvdata(dev); >> + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); >> + >> + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), >> + &priv->sleep_sec)) { >> + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); >> + priv->sleep_sec = 0; >> + } >> + >> + return 0; >> +} >> + >> +static int s32g_rtc_resume(struct device *dev) >> +{ >> + struct rtc_priv *priv = dev_get_drvdata(dev); >> + >> + /* The transition from resume to run is a reset event. >> + * This leads to the RTC registers being reset after resume from >> + * suspend. It is uncommon, but this behaviour has been observed >> + * on S32G RTC after issuing a Suspend to RAM operation. >> + * Thus, reconfigure RTC registers on the resume path. >> + */ >> + return rtc_clk_src_setup(priv); >> +} >> + >> +static const struct of_device_id rtc_dt_ids[] = { >> + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, >> + { /* sentinel */ }, >> +}; >> + >> +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, >> + s32g_rtc_suspend, s32g_rtc_resume); >> + >> +static struct platform_driver s32g_rtc_driver = { >> + .driver = { >> + .name = "s32g-rtc", >> + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), >> + .of_match_table = rtc_dt_ids, >> + }, >> + .probe = s32g_rtc_probe, >> +}; >> +module_platform_driver(s32g_rtc_driver); >> + >> +MODULE_AUTHOR("NXP"); >> +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); >> +MODULE_LICENSE("GPL"); >> -- >> 2.45.2 >> > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support 2025-04-09 14:26 ` Ciprian Marian Costea @ 2025-04-09 14:42 ` Enric Balletbo i Serra 2025-04-09 14:58 ` Ciprian Marian Costea 0 siblings, 1 reply; 19+ messages in thread From: Enric Balletbo i Serra @ 2025-04-09 14:42 UTC (permalink / raw) To: Ciprian Marian Costea Cc: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Eric Chanudet, Bogdan Hamciuc, Ghennadi Procopciuc, Frank Li Hi Ciprian, On Wed, Apr 9, 2025 at 4:26 PM Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> wrote: > > On 4/9/2025 5:14 PM, Enric Balletbo i Serra wrote: > > Hi Ciprian, > > > > Many thanks for your patch. > > > > On Thu, Apr 3, 2025 at 12:34 PM Ciprian Costea > > <ciprianmarian.costea@oss.nxp.com> wrote: > >> > >> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > >> > >> Add a RTC driver for NXP S32G2/S32G3 SoCs. > >> > >> RTC tracks clock time during system suspend. It can be a wakeup source > >> for the S32G2/S32G3 SoC based boards. > >> > >> The RTC module from S32G2/S32G3 is not battery-powered and it is not kept > >> alive during system reset. > >> > >> Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > >> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > >> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > >> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > >> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > >> Reviewed-by: Frank Li <Frank.Li@nxp.com> > > > > I went through the code and it looks good to me, so: > > > > However, I did a basic rtctest selftest in my S32G-VNP-RDB3 board, > > and some tests failed (it shows a small deviation), any idea what > > could be wrong? > > > > This is the output of the test: > > > > # ./rtctest > > TAP version 13 > > 1..8 > > # Starting 8 tests from 1 test cases. > > # RUN rtc.date_read ... > > # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:17:49. > > # OK rtc.date_read > > ok 1 rtc.date_read > > # RUN rtc.date_read_loop ... > > # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s > > (with 11ms breaks after every read). > > # rtctest.c:151:date_read_loop:Performed 2638 RTC time reads. > > # OK rtc.date_read_loop > > ok 2 rtc.date_read_loop > > # RUN rtc.uie_read ... > > # OK rtc.uie_read > > ok 3 rtc.uie_read > > # RUN rtc.uie_select ... > > # OK rtc.uie_select > > ok 4 rtc.uie_select > > # RUN rtc.alarm_alm_set ... > > # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:18:31. > > # rtctest.c:282:alarm_alm_set:data: 1a0 > > # OK rtc.alarm_alm_set > > ok 5 rtc.alarm_alm_set > > # RUN rtc.alarm_wkalm_set ... > > # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:18:34. > > # rtctest.c:353:alarm_wkalm_set:Expected new (1738023515) == secs (1738023514) > > # alarm_wkalm_set: Test terminated by assertion > > # FAIL rtc.alarm_wkalm_set > > not ok 6 rtc.alarm_wkalm_set > > # RUN rtc.alarm_alm_set_minute ... > > # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:19:00. > > # rtctest.c:414:alarm_alm_set_minute:data: 1a0 > > # rtctest.c:420:alarm_alm_set_minute:Expected new (1738023541) == secs > > (1738023540) > > # alarm_alm_set_minute: Test terminated by assertion > > # FAIL rtc.alarm_alm_set_minute > > not ok 7 rtc.alarm_alm_set_minute > > # RUN rtc.alarm_wkalm_set_minute ... > > # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to > > 28/01/2025 00:20:00. > > # rtctest.c:483:alarm_wkalm_set_minute:Expected new (1738023601) == > > secs (1738023600) > > # alarm_wkalm_set_minute: Test terminated by assertion > > # FAIL rtc.alarm_wkalm_set_minute > > not ok 8 rtc.alarm_wkalm_set_minute > > # FAILED: 5 / 8 tests passed. > > # Totals: pass:5 fail:3 xfail:0 xpass:0 skip:0 error:0 > > > > Best regards, > > Enric > > > > Hello Enric, > > In order to obtain a better resolution/precision you could try to only > use DIV512 hardware divisor instead of both DIV32 and DIV512. > That makes the tests pass, so I'm wondering if by default you should set DIV512 in platform data instead. With that change you can add my Tested-by: Enric Balletbo i Serra <eballetbo@kernel.org> # ./rtctest TAP version 13 1..8 # Starting 8 tests from 1 test cases. # RUN rtc.date_read ... # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:00:34. # OK rtc.date_read ok 1 rtc.date_read # RUN rtc.date_read_loop ... # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s (with 11ms breaks after every read). # rtctest.c:151:date_read_loop:Performed 2656 RTC time reads. # OK rtc.date_read_loop ok 2 rtc.date_read_loop # RUN rtc.uie_read ... # OK rtc.uie_read ok 3 rtc.uie_read # RUN rtc.uie_select ... # OK rtc.uie_select ok 4 rtc.uie_select # RUN rtc.alarm_alm_set ... # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:01:14. # rtctest.c:282:alarm_alm_set:data: 1a0 # OK rtc.alarm_alm_set ok 5 rtc.alarm_alm_set # RUN rtc.alarm_wkalm_set ... # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:01:17. # OK rtc.alarm_wkalm_set ok 6 rtc.alarm_wkalm_set # RUN rtc.alarm_alm_set_minute ... # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:02:00. # rtctest.c:414:alarm_alm_set_minute:data: 1a0 # OK rtc.alarm_alm_set_minute ok 7 rtc.alarm_alm_set_minute # RUN rtc.alarm_wkalm_set_minute ... # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to 28/01/2025 00:03:00. # OK rtc.alarm_wkalm_set_minute ok 8 rtc.alarm_wkalm_set_minute # PASSED: 8 / 8 tests passed. # Totals: pass:8 fail:0 xfail:0 xpass:0 skip:0 error:0 Thanks, Enric > Regards, > Ciprian > > >> --- > >> drivers/rtc/Kconfig | 11 ++ > >> drivers/rtc/Makefile | 1 + > >> drivers/rtc/rtc-s32g.c | 385 +++++++++++++++++++++++++++++++++++++++++ > >> 3 files changed, 397 insertions(+) > >> create mode 100644 drivers/rtc/rtc-s32g.c > >> > >> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > >> index 0bbbf778ecfa..510dc2db745d 100644 > >> --- a/drivers/rtc/Kconfig > >> +++ b/drivers/rtc/Kconfig > >> @@ -2103,4 +2103,15 @@ config RTC_DRV_AMLOGIC_A4 > >> This driver can also be built as a module. If so, the module > >> will be called "rtc-amlogic-a4". > >> > >> +config RTC_DRV_S32G > >> + tristate "RTC driver for S32G2/S32G3 SoCs" > >> + depends on ARCH_S32 || COMPILE_TEST > >> + depends on COMMON_CLK > >> + help > >> + Say yes to enable RTC driver for platforms based on the > >> + S32G2/S32G3 SoC family. > >> + > >> + This RTC module can be used as a wakeup source. > >> + Please note that it is not battery-powered. > >> + > >> endif # RTC_CLASS > >> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > >> index 489b4ab07068..e4b616ecd5ce 100644 > >> --- a/drivers/rtc/Makefile > >> +++ b/drivers/rtc/Makefile > >> @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o > >> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o > >> obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o > >> obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o > >> +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o > >> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o > >> obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o > >> obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o > >> diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c > >> new file mode 100644 > >> index 000000000000..3a0818e972eb > >> --- /dev/null > >> +++ b/drivers/rtc/rtc-s32g.c > >> @@ -0,0 +1,385 @@ > >> +// SPDX-License-Identifier: GPL-2.0-or-later > >> +/* > >> + * Copyright 2025 NXP > >> + */ > >> + > >> +#include <linux/bitfield.h> > >> +#include <linux/clk.h> > >> +#include <linux/iopoll.h> > >> +#include <linux/of_irq.h> > >> +#include <linux/platform_device.h> > >> +#include <linux/rtc.h> > >> + > >> +#define RTCC_OFFSET 0x4ul > >> +#define RTCS_OFFSET 0x8ul > >> +#define APIVAL_OFFSET 0x10ul > >> + > >> +/* RTCC fields */ > >> +#define RTCC_CNTEN BIT(31) > >> +#define RTCC_APIEN BIT(15) > >> +#define RTCC_APIIE BIT(14) > >> +#define RTCC_CLKSEL_MASK GENMASK(13, 12) > >> +#define RTCC_DIV512EN BIT(11) > >> +#define RTCC_DIV32EN BIT(10) > >> + > >> +/* RTCS fields */ > >> +#define RTCS_INV_API BIT(17) > >> +#define RTCS_APIF BIT(13) > >> + > >> +#define APIVAL_MAX_VAL GENMASK(31, 0) > >> +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) > >> + > >> +/* > >> + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and > >> + * should not be used. > >> + */ > >> +#define RTC_CLK_SRC1_RESERVED BIT(1) > >> + > >> +/* > >> + * S32G RTC module has a 512 value and a 32 value hardware frequency > >> + * divisors (DIV512 and DIV32) which could be used to achieve higher > >> + * counter ranges by lowering the RTC frequency. > >> + */ > >> +enum { > >> + DIV1 = 1, > >> + DIV32 = 32, > >> + DIV512 = 512, > >> + DIV512_32 = 16384 > >> +}; > >> + > >> +static const char *const rtc_clk_src[] = { > >> + "source0", > >> + "source1", > >> + "source2", > >> + "source3" > >> +}; > >> + > >> +struct rtc_priv { > >> + struct rtc_device *rdev; > >> + void __iomem *rtc_base; > >> + struct clk *ipg; > >> + struct clk *clk_src; > >> + const struct rtc_soc_data *rtc_data; > >> + u64 rtc_hz; > >> + time64_t sleep_sec; > >> + int irq; > >> + u32 clk_src_idx; > >> +}; > >> + > >> +struct rtc_soc_data { > >> + u32 clk_div; > >> + u32 reserved_clk_mask; > >> +}; > >> + > >> +static const struct rtc_soc_data rtc_s32g2_data = { > >> + .clk_div = DIV512_32, > >> + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, > >> +}; > >> + > >> +static irqreturn_t s32g_rtc_handler(int irq, void *dev) > >> +{ > >> + struct rtc_priv *priv = platform_get_drvdata(dev); > >> + u32 status; > >> + > >> + status = readl(priv->rtc_base + RTCS_OFFSET); > >> + > >> + if (status & RTCS_APIF) { > >> + writel(0x0, priv->rtc_base + APIVAL_OFFSET); > >> + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); > >> + } > >> + > >> + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); > >> + > >> + return IRQ_HANDLED; > >> +} > >> + > >> +/* > >> + * The function is not really getting time from the RTC since the S32G RTC > >> + * has several limitations. Thus, to setup alarm use system time. > >> + */ > >> +static int s32g_rtc_read_time(struct device *dev, > >> + struct rtc_time *tm) > >> +{ > >> + struct rtc_priv *priv = dev_get_drvdata(dev); > >> + time64_t sec; > >> + > >> + if (check_add_overflow(ktime_get_real_seconds(), > >> + priv->sleep_sec, &sec)) > >> + return -ERANGE; > >> + > >> + rtc_time64_to_tm(sec, tm); > >> + > >> + return 0; > >> +} > >> + > >> +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) > >> +{ > >> + struct rtc_priv *priv = dev_get_drvdata(dev); > >> + u32 rtcc, rtcs; > >> + > >> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >> + rtcs = readl(priv->rtc_base + RTCS_OFFSET); > >> + > >> + alrm->enabled = rtcc & RTCC_APIIE; > >> + if (alrm->enabled) > >> + alrm->pending = !(rtcs & RTCS_APIF); > >> + > >> + return 0; > >> +} > >> + > >> +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) > >> +{ > >> + struct rtc_priv *priv = dev_get_drvdata(dev); > >> + u32 rtcc; > >> + > >> + /* RTC API functionality is used both for triggering interrupts > >> + * and as a wakeup event. Hence it should always be enabled. > >> + */ > >> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >> + rtcc |= RTCC_APIEN | RTCC_APIIE; > >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >> + > >> + return 0; > >> +} > >> + > >> +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) > >> +{ > >> + struct rtc_priv *priv = dev_get_drvdata(dev); > >> + unsigned long long cycles; > >> + long long t_offset; > >> + time64_t alrm_time; > >> + u32 rtcs; > >> + int ret; > >> + > >> + alrm_time = rtc_tm_to_time64(&alrm->time); > >> + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; > >> + if (t_offset < 0) > >> + return -ERANGE; > >> + > >> + cycles = t_offset * priv->rtc_hz; > >> + if (cycles > APIVAL_MAX_VAL) > >> + return -ERANGE; > >> + > >> + /* APIVAL could have been reset from the IRQ handler. > >> + * Hence, we wait in case there is a synchronization process. > >> + */ > >> + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), > >> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); > >> + if (ret) > >> + return ret; > >> + > >> + writel(cycles, priv->rtc_base + APIVAL_OFFSET); > >> + > >> + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), > >> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); > >> +} > >> + > >> +/* > >> + * Disable the 32-bit free running counter. > >> + * This allows Clock Source and Divisors selection > >> + * to be performed without causing synchronization issues. > >> + */ > >> +static void s32g_rtc_disable(struct rtc_priv *priv) > >> +{ > >> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >> + > >> + rtcc &= ~RTCC_CNTEN; > >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >> +} > >> + > >> +static void s32g_rtc_enable(struct rtc_priv *priv) > >> +{ > >> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >> + > >> + rtcc |= RTCC_CNTEN; > >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >> +} > >> + > >> +static int rtc_clk_src_setup(struct rtc_priv *priv) > >> +{ > >> + u32 rtcc; > >> + > >> + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); > >> + > >> + switch (priv->rtc_data->clk_div) { > >> + case DIV512_32: > >> + rtcc |= RTCC_DIV512EN; > >> + rtcc |= RTCC_DIV32EN; > >> + break; > >> + case DIV512: > >> + rtcc |= RTCC_DIV512EN; > >> + break; > >> + case DIV32: > >> + rtcc |= RTCC_DIV32EN; > >> + break; > >> + case DIV1: > >> + break; > >> + default: > >> + return -EINVAL; > >> + } > >> + > >> + rtcc |= RTCC_APIEN | RTCC_APIIE; > >> + /* > >> + * Make sure the CNTEN is 0 before we configure > >> + * the clock source and dividers. > >> + */ > >> + s32g_rtc_disable(priv); > >> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >> + s32g_rtc_enable(priv); > >> + > >> + return 0; > >> +} > >> + > >> +static const struct rtc_class_ops rtc_ops = { > >> + .read_time = s32g_rtc_read_time, > >> + .read_alarm = s32g_rtc_read_alarm, > >> + .set_alarm = s32g_rtc_set_alarm, > >> + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, > >> +}; > >> + > >> +static int rtc_clk_dts_setup(struct rtc_priv *priv, > >> + struct device *dev) > >> +{ > >> + u32 i; > >> + > >> + priv->ipg = devm_clk_get_enabled(dev, "ipg"); > >> + if (IS_ERR(priv->ipg)) > >> + return dev_err_probe(dev, PTR_ERR(priv->ipg), > >> + "Failed to get 'ipg' clock\n"); > >> + > >> + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { > >> + if (priv->rtc_data->reserved_clk_mask & BIT(i)) > >> + return -EOPNOTSUPP; > >> + > >> + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); > >> + if (!IS_ERR(priv->clk_src)) { > >> + priv->clk_src_idx = i; > >> + break; > >> + } > >> + } > >> + > >> + if (IS_ERR(priv->clk_src)) > >> + return dev_err_probe(dev, PTR_ERR(priv->clk_src), > >> + "Failed to get rtc module clock source\n"); > >> + > >> + return 0; > >> +} > >> + > >> +static int s32g_rtc_probe(struct platform_device *pdev) > >> +{ > >> + struct device *dev = &pdev->dev; > >> + struct rtc_priv *priv; > >> + unsigned long rtc_hz; > >> + int ret; > >> + > >> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > >> + if (!priv) > >> + return -ENOMEM; > >> + > >> + priv->rtc_data = of_device_get_match_data(dev); > >> + if (!priv->rtc_data) > >> + return -ENODEV; > >> + > >> + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); > >> + if (IS_ERR(priv->rtc_base)) > >> + return PTR_ERR(priv->rtc_base); > >> + > >> + device_init_wakeup(dev, true); > >> + > >> + ret = rtc_clk_dts_setup(priv, dev); > >> + if (ret) > >> + return ret; > >> + > >> + priv->rdev = devm_rtc_allocate_device(dev); > >> + if (IS_ERR(priv->rdev)) > >> + return PTR_ERR(priv->rdev); > >> + > >> + ret = rtc_clk_src_setup(priv); > >> + if (ret) > >> + return ret; > >> + > >> + priv->irq = platform_get_irq(pdev, 0); > >> + if (priv->irq < 0) { > >> + ret = priv->irq; > >> + goto disable_rtc; > >> + } > >> + > >> + rtc_hz = clk_get_rate(priv->clk_src); > >> + if (!rtc_hz) { > >> + dev_err(dev, "Failed to get RTC frequency\n"); > >> + ret = -EINVAL; > >> + goto disable_rtc; > >> + } > >> + > >> + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); > >> + > >> + platform_set_drvdata(pdev, priv); > >> + priv->rdev->ops = &rtc_ops; > >> + > >> + ret = devm_request_irq(dev, priv->irq, > >> + s32g_rtc_handler, 0, dev_name(dev), pdev); > >> + if (ret) { > >> + dev_err(dev, "Request interrupt %d failed, error: %d\n", > >> + priv->irq, ret); > >> + goto disable_rtc; > >> + } > >> + > >> + ret = devm_rtc_register_device(priv->rdev); > >> + if (ret) > >> + goto disable_rtc; > >> + > >> + return 0; > >> + > >> +disable_rtc: > >> + s32g_rtc_disable(priv); > >> + return ret; > >> +} > >> + > >> +static int s32g_rtc_suspend(struct device *dev) > >> +{ > >> + struct rtc_priv *priv = dev_get_drvdata(dev); > >> + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); > >> + > >> + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), > >> + &priv->sleep_sec)) { > >> + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); > >> + priv->sleep_sec = 0; > >> + } > >> + > >> + return 0; > >> +} > >> + > >> +static int s32g_rtc_resume(struct device *dev) > >> +{ > >> + struct rtc_priv *priv = dev_get_drvdata(dev); > >> + > >> + /* The transition from resume to run is a reset event. > >> + * This leads to the RTC registers being reset after resume from > >> + * suspend. It is uncommon, but this behaviour has been observed > >> + * on S32G RTC after issuing a Suspend to RAM operation. > >> + * Thus, reconfigure RTC registers on the resume path. > >> + */ > >> + return rtc_clk_src_setup(priv); > >> +} > >> + > >> +static const struct of_device_id rtc_dt_ids[] = { > >> + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, > >> + { /* sentinel */ }, > >> +}; > >> + > >> +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, > >> + s32g_rtc_suspend, s32g_rtc_resume); > >> + > >> +static struct platform_driver s32g_rtc_driver = { > >> + .driver = { > >> + .name = "s32g-rtc", > >> + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), > >> + .of_match_table = rtc_dt_ids, > >> + }, > >> + .probe = s32g_rtc_probe, > >> +}; > >> +module_platform_driver(s32g_rtc_driver); > >> + > >> +MODULE_AUTHOR("NXP"); > >> +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); > >> +MODULE_LICENSE("GPL"); > >> -- > >> 2.45.2 > >> > > > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support 2025-04-09 14:42 ` Enric Balletbo i Serra @ 2025-04-09 14:58 ` Ciprian Marian Costea 2025-04-10 7:08 ` Enric Balletbo i Serra 2025-04-11 8:09 ` Ciprian Marian Costea 0 siblings, 2 replies; 19+ messages in thread From: Ciprian Marian Costea @ 2025-04-09 14:58 UTC (permalink / raw) To: Enric Balletbo i Serra Cc: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Eric Chanudet, Bogdan Hamciuc, Ghennadi Procopciuc, Frank Li On 4/9/2025 5:42 PM, Enric Balletbo i Serra wrote: > Hi Ciprian, > > > On Wed, Apr 9, 2025 at 4:26 PM Ciprian Marian Costea > <ciprianmarian.costea@oss.nxp.com> wrote: >> >> On 4/9/2025 5:14 PM, Enric Balletbo i Serra wrote: >>> Hi Ciprian, >>> >>> Many thanks for your patch. >>> >>> On Thu, Apr 3, 2025 at 12:34 PM Ciprian Costea >>> <ciprianmarian.costea@oss.nxp.com> wrote: >>>> >>>> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >>>> >>>> Add a RTC driver for NXP S32G2/S32G3 SoCs. >>>> >>>> RTC tracks clock time during system suspend. It can be a wakeup source >>>> for the S32G2/S32G3 SoC based boards. >>>> >>>> The RTC module from S32G2/S32G3 is not battery-powered and it is not kept >>>> alive during system reset. >>>> >>>> Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> >>>> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> >>>> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> >>>> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> >>>> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >>>> Reviewed-by: Frank Li <Frank.Li@nxp.com> >>> >>> I went through the code and it looks good to me, so: >>> >>> However, I did a basic rtctest selftest in my S32G-VNP-RDB3 board, >>> and some tests failed (it shows a small deviation), any idea what >>> could be wrong? >>> >>> This is the output of the test: >>> >>> # ./rtctest >>> TAP version 13 >>> 1..8 >>> # Starting 8 tests from 1 test cases. >>> # RUN rtc.date_read ... >>> # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:17:49. >>> # OK rtc.date_read >>> ok 1 rtc.date_read >>> # RUN rtc.date_read_loop ... >>> # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s >>> (with 11ms breaks after every read). >>> # rtctest.c:151:date_read_loop:Performed 2638 RTC time reads. >>> # OK rtc.date_read_loop >>> ok 2 rtc.date_read_loop >>> # RUN rtc.uie_read ... >>> # OK rtc.uie_read >>> ok 3 rtc.uie_read >>> # RUN rtc.uie_select ... >>> # OK rtc.uie_select >>> ok 4 rtc.uie_select >>> # RUN rtc.alarm_alm_set ... >>> # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:18:31. >>> # rtctest.c:282:alarm_alm_set:data: 1a0 >>> # OK rtc.alarm_alm_set >>> ok 5 rtc.alarm_alm_set >>> # RUN rtc.alarm_wkalm_set ... >>> # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:18:34. >>> # rtctest.c:353:alarm_wkalm_set:Expected new (1738023515) == secs (1738023514) >>> # alarm_wkalm_set: Test terminated by assertion >>> # FAIL rtc.alarm_wkalm_set >>> not ok 6 rtc.alarm_wkalm_set >>> # RUN rtc.alarm_alm_set_minute ... >>> # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:19:00. >>> # rtctest.c:414:alarm_alm_set_minute:data: 1a0 >>> # rtctest.c:420:alarm_alm_set_minute:Expected new (1738023541) == secs >>> (1738023540) >>> # alarm_alm_set_minute: Test terminated by assertion >>> # FAIL rtc.alarm_alm_set_minute >>> not ok 7 rtc.alarm_alm_set_minute >>> # RUN rtc.alarm_wkalm_set_minute ... >>> # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to >>> 28/01/2025 00:20:00. >>> # rtctest.c:483:alarm_wkalm_set_minute:Expected new (1738023601) == >>> secs (1738023600) >>> # alarm_wkalm_set_minute: Test terminated by assertion >>> # FAIL rtc.alarm_wkalm_set_minute >>> not ok 8 rtc.alarm_wkalm_set_minute >>> # FAILED: 5 / 8 tests passed. >>> # Totals: pass:5 fail:3 xfail:0 xpass:0 skip:0 error:0 >>> >>> Best regards, >>> Enric >>> >> >> Hello Enric, >> >> In order to obtain a better resolution/precision you could try to only >> use DIV512 hardware divisor instead of both DIV32 and DIV512. >> > > That makes the tests pass, so I'm wondering if by default you should > set DIV512 in platform data instead. With that change you can add my > > Tested-by: Enric Balletbo i Serra <eballetbo@kernel.org> > This change would decrease the RTC rollover time. Initially indeed only DIV512 was enabled, but during review [1] I've agreed enabling both hardware divisors to increase the RTC rollover time. Personally, I am ok with the current 1 second resolution capability. Alexandre Belloni, are you ok with the current version of this driver ? Or should I send a V10 patch in which I enable back only the DIV512 divisor in order to increase the RTC precision at the downside of losing rollover time. [1] https://lore.kernel.org/all/9f4fc9d8-ec7f-43b4-8bdd-01a4ba4855c5@oss.nxp.com/ Regards, Ciprian > # ./rtctest > TAP version 13 > 1..8 > # Starting 8 tests from 1 test cases. > # RUN rtc.date_read ... > # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:00:34. > # OK rtc.date_read > ok 1 rtc.date_read > # RUN rtc.date_read_loop ... > # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s > (with 11ms breaks after every read). > # rtctest.c:151:date_read_loop:Performed 2656 RTC time reads. > # OK rtc.date_read_loop > ok 2 rtc.date_read_loop > # RUN rtc.uie_read ... > # OK rtc.uie_read > ok 3 rtc.uie_read > # RUN rtc.uie_select ... > # OK rtc.uie_select > ok 4 rtc.uie_select > # RUN rtc.alarm_alm_set ... > # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:01:14. > # rtctest.c:282:alarm_alm_set:data: 1a0 > # OK rtc.alarm_alm_set > ok 5 rtc.alarm_alm_set > # RUN rtc.alarm_wkalm_set ... > # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:01:17. > # OK rtc.alarm_wkalm_set > ok 6 rtc.alarm_wkalm_set > # RUN rtc.alarm_alm_set_minute ... > # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:02:00. > # rtctest.c:414:alarm_alm_set_minute:data: 1a0 > # OK rtc.alarm_alm_set_minute > ok 7 rtc.alarm_alm_set_minute > # RUN rtc.alarm_wkalm_set_minute ... > # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to > 28/01/2025 00:03:00. > # OK rtc.alarm_wkalm_set_minute > ok 8 rtc.alarm_wkalm_set_minute > # PASSED: 8 / 8 tests passed. > # Totals: pass:8 fail:0 xfail:0 xpass:0 skip:0 error:0 > > > Thanks, > Enric > >> Regards, >> Ciprian >> >>>> --- >>>> drivers/rtc/Kconfig | 11 ++ >>>> drivers/rtc/Makefile | 1 + >>>> drivers/rtc/rtc-s32g.c | 385 +++++++++++++++++++++++++++++++++++++++++ >>>> 3 files changed, 397 insertions(+) >>>> create mode 100644 drivers/rtc/rtc-s32g.c >>>> >>>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig >>>> index 0bbbf778ecfa..510dc2db745d 100644 >>>> --- a/drivers/rtc/Kconfig >>>> +++ b/drivers/rtc/Kconfig >>>> @@ -2103,4 +2103,15 @@ config RTC_DRV_AMLOGIC_A4 >>>> This driver can also be built as a module. If so, the module >>>> will be called "rtc-amlogic-a4". >>>> >>>> +config RTC_DRV_S32G >>>> + tristate "RTC driver for S32G2/S32G3 SoCs" >>>> + depends on ARCH_S32 || COMPILE_TEST >>>> + depends on COMMON_CLK >>>> + help >>>> + Say yes to enable RTC driver for platforms based on the >>>> + S32G2/S32G3 SoC family. >>>> + >>>> + This RTC module can be used as a wakeup source. >>>> + Please note that it is not battery-powered. >>>> + >>>> endif # RTC_CLASS >>>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile >>>> index 489b4ab07068..e4b616ecd5ce 100644 >>>> --- a/drivers/rtc/Makefile >>>> +++ b/drivers/rtc/Makefile >>>> @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o >>>> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o >>>> obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o >>>> obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o >>>> +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o >>>> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o >>>> obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o >>>> obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o >>>> diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c >>>> new file mode 100644 >>>> index 000000000000..3a0818e972eb >>>> --- /dev/null >>>> +++ b/drivers/rtc/rtc-s32g.c >>>> @@ -0,0 +1,385 @@ >>>> +// SPDX-License-Identifier: GPL-2.0-or-later >>>> +/* >>>> + * Copyright 2025 NXP >>>> + */ >>>> + >>>> +#include <linux/bitfield.h> >>>> +#include <linux/clk.h> >>>> +#include <linux/iopoll.h> >>>> +#include <linux/of_irq.h> >>>> +#include <linux/platform_device.h> >>>> +#include <linux/rtc.h> >>>> + >>>> +#define RTCC_OFFSET 0x4ul >>>> +#define RTCS_OFFSET 0x8ul >>>> +#define APIVAL_OFFSET 0x10ul >>>> + >>>> +/* RTCC fields */ >>>> +#define RTCC_CNTEN BIT(31) >>>> +#define RTCC_APIEN BIT(15) >>>> +#define RTCC_APIIE BIT(14) >>>> +#define RTCC_CLKSEL_MASK GENMASK(13, 12) >>>> +#define RTCC_DIV512EN BIT(11) >>>> +#define RTCC_DIV32EN BIT(10) >>>> + >>>> +/* RTCS fields */ >>>> +#define RTCS_INV_API BIT(17) >>>> +#define RTCS_APIF BIT(13) >>>> + >>>> +#define APIVAL_MAX_VAL GENMASK(31, 0) >>>> +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) >>>> + >>>> +/* >>>> + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and >>>> + * should not be used. >>>> + */ >>>> +#define RTC_CLK_SRC1_RESERVED BIT(1) >>>> + >>>> +/* >>>> + * S32G RTC module has a 512 value and a 32 value hardware frequency >>>> + * divisors (DIV512 and DIV32) which could be used to achieve higher >>>> + * counter ranges by lowering the RTC frequency. >>>> + */ >>>> +enum { >>>> + DIV1 = 1, >>>> + DIV32 = 32, >>>> + DIV512 = 512, >>>> + DIV512_32 = 16384 >>>> +}; >>>> + >>>> +static const char *const rtc_clk_src[] = { >>>> + "source0", >>>> + "source1", >>>> + "source2", >>>> + "source3" >>>> +}; >>>> + >>>> +struct rtc_priv { >>>> + struct rtc_device *rdev; >>>> + void __iomem *rtc_base; >>>> + struct clk *ipg; >>>> + struct clk *clk_src; >>>> + const struct rtc_soc_data *rtc_data; >>>> + u64 rtc_hz; >>>> + time64_t sleep_sec; >>>> + int irq; >>>> + u32 clk_src_idx; >>>> +}; >>>> + >>>> +struct rtc_soc_data { >>>> + u32 clk_div; >>>> + u32 reserved_clk_mask; >>>> +}; >>>> + >>>> +static const struct rtc_soc_data rtc_s32g2_data = { >>>> + .clk_div = DIV512_32, >>>> + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, >>>> +}; >>>> + >>>> +static irqreturn_t s32g_rtc_handler(int irq, void *dev) >>>> +{ >>>> + struct rtc_priv *priv = platform_get_drvdata(dev); >>>> + u32 status; >>>> + >>>> + status = readl(priv->rtc_base + RTCS_OFFSET); >>>> + >>>> + if (status & RTCS_APIF) { >>>> + writel(0x0, priv->rtc_base + APIVAL_OFFSET); >>>> + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); >>>> + } >>>> + >>>> + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); >>>> + >>>> + return IRQ_HANDLED; >>>> +} >>>> + >>>> +/* >>>> + * The function is not really getting time from the RTC since the S32G RTC >>>> + * has several limitations. Thus, to setup alarm use system time. >>>> + */ >>>> +static int s32g_rtc_read_time(struct device *dev, >>>> + struct rtc_time *tm) >>>> +{ >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>> + time64_t sec; >>>> + >>>> + if (check_add_overflow(ktime_get_real_seconds(), >>>> + priv->sleep_sec, &sec)) >>>> + return -ERANGE; >>>> + >>>> + rtc_time64_to_tm(sec, tm); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) >>>> +{ >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>> + u32 rtcc, rtcs; >>>> + >>>> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>> + rtcs = readl(priv->rtc_base + RTCS_OFFSET); >>>> + >>>> + alrm->enabled = rtcc & RTCC_APIIE; >>>> + if (alrm->enabled) >>>> + alrm->pending = !(rtcs & RTCS_APIF); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) >>>> +{ >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>> + u32 rtcc; >>>> + >>>> + /* RTC API functionality is used both for triggering interrupts >>>> + * and as a wakeup event. Hence it should always be enabled. >>>> + */ >>>> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>> + rtcc |= RTCC_APIEN | RTCC_APIIE; >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) >>>> +{ >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>> + unsigned long long cycles; >>>> + long long t_offset; >>>> + time64_t alrm_time; >>>> + u32 rtcs; >>>> + int ret; >>>> + >>>> + alrm_time = rtc_tm_to_time64(&alrm->time); >>>> + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; >>>> + if (t_offset < 0) >>>> + return -ERANGE; >>>> + >>>> + cycles = t_offset * priv->rtc_hz; >>>> + if (cycles > APIVAL_MAX_VAL) >>>> + return -ERANGE; >>>> + >>>> + /* APIVAL could have been reset from the IRQ handler. >>>> + * Hence, we wait in case there is a synchronization process. >>>> + */ >>>> + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), >>>> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + writel(cycles, priv->rtc_base + APIVAL_OFFSET); >>>> + >>>> + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), >>>> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); >>>> +} >>>> + >>>> +/* >>>> + * Disable the 32-bit free running counter. >>>> + * This allows Clock Source and Divisors selection >>>> + * to be performed without causing synchronization issues. >>>> + */ >>>> +static void s32g_rtc_disable(struct rtc_priv *priv) >>>> +{ >>>> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>> + >>>> + rtcc &= ~RTCC_CNTEN; >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>> +} >>>> + >>>> +static void s32g_rtc_enable(struct rtc_priv *priv) >>>> +{ >>>> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>> + >>>> + rtcc |= RTCC_CNTEN; >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>> +} >>>> + >>>> +static int rtc_clk_src_setup(struct rtc_priv *priv) >>>> +{ >>>> + u32 rtcc; >>>> + >>>> + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); >>>> + >>>> + switch (priv->rtc_data->clk_div) { >>>> + case DIV512_32: >>>> + rtcc |= RTCC_DIV512EN; >>>> + rtcc |= RTCC_DIV32EN; >>>> + break; >>>> + case DIV512: >>>> + rtcc |= RTCC_DIV512EN; >>>> + break; >>>> + case DIV32: >>>> + rtcc |= RTCC_DIV32EN; >>>> + break; >>>> + case DIV1: >>>> + break; >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> + >>>> + rtcc |= RTCC_APIEN | RTCC_APIIE; >>>> + /* >>>> + * Make sure the CNTEN is 0 before we configure >>>> + * the clock source and dividers. >>>> + */ >>>> + s32g_rtc_disable(priv); >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>> + s32g_rtc_enable(priv); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static const struct rtc_class_ops rtc_ops = { >>>> + .read_time = s32g_rtc_read_time, >>>> + .read_alarm = s32g_rtc_read_alarm, >>>> + .set_alarm = s32g_rtc_set_alarm, >>>> + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, >>>> +}; >>>> + >>>> +static int rtc_clk_dts_setup(struct rtc_priv *priv, >>>> + struct device *dev) >>>> +{ >>>> + u32 i; >>>> + >>>> + priv->ipg = devm_clk_get_enabled(dev, "ipg"); >>>> + if (IS_ERR(priv->ipg)) >>>> + return dev_err_probe(dev, PTR_ERR(priv->ipg), >>>> + "Failed to get 'ipg' clock\n"); >>>> + >>>> + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { >>>> + if (priv->rtc_data->reserved_clk_mask & BIT(i)) >>>> + return -EOPNOTSUPP; >>>> + >>>> + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); >>>> + if (!IS_ERR(priv->clk_src)) { >>>> + priv->clk_src_idx = i; >>>> + break; >>>> + } >>>> + } >>>> + >>>> + if (IS_ERR(priv->clk_src)) >>>> + return dev_err_probe(dev, PTR_ERR(priv->clk_src), >>>> + "Failed to get rtc module clock source\n"); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int s32g_rtc_probe(struct platform_device *pdev) >>>> +{ >>>> + struct device *dev = &pdev->dev; >>>> + struct rtc_priv *priv; >>>> + unsigned long rtc_hz; >>>> + int ret; >>>> + >>>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); >>>> + if (!priv) >>>> + return -ENOMEM; >>>> + >>>> + priv->rtc_data = of_device_get_match_data(dev); >>>> + if (!priv->rtc_data) >>>> + return -ENODEV; >>>> + >>>> + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); >>>> + if (IS_ERR(priv->rtc_base)) >>>> + return PTR_ERR(priv->rtc_base); >>>> + >>>> + device_init_wakeup(dev, true); >>>> + >>>> + ret = rtc_clk_dts_setup(priv, dev); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + priv->rdev = devm_rtc_allocate_device(dev); >>>> + if (IS_ERR(priv->rdev)) >>>> + return PTR_ERR(priv->rdev); >>>> + >>>> + ret = rtc_clk_src_setup(priv); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + priv->irq = platform_get_irq(pdev, 0); >>>> + if (priv->irq < 0) { >>>> + ret = priv->irq; >>>> + goto disable_rtc; >>>> + } >>>> + >>>> + rtc_hz = clk_get_rate(priv->clk_src); >>>> + if (!rtc_hz) { >>>> + dev_err(dev, "Failed to get RTC frequency\n"); >>>> + ret = -EINVAL; >>>> + goto disable_rtc; >>>> + } >>>> + >>>> + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); >>>> + >>>> + platform_set_drvdata(pdev, priv); >>>> + priv->rdev->ops = &rtc_ops; >>>> + >>>> + ret = devm_request_irq(dev, priv->irq, >>>> + s32g_rtc_handler, 0, dev_name(dev), pdev); >>>> + if (ret) { >>>> + dev_err(dev, "Request interrupt %d failed, error: %d\n", >>>> + priv->irq, ret); >>>> + goto disable_rtc; >>>> + } >>>> + >>>> + ret = devm_rtc_register_device(priv->rdev); >>>> + if (ret) >>>> + goto disable_rtc; >>>> + >>>> + return 0; >>>> + >>>> +disable_rtc: >>>> + s32g_rtc_disable(priv); >>>> + return ret; >>>> +} >>>> + >>>> +static int s32g_rtc_suspend(struct device *dev) >>>> +{ >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>> + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); >>>> + >>>> + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), >>>> + &priv->sleep_sec)) { >>>> + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); >>>> + priv->sleep_sec = 0; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int s32g_rtc_resume(struct device *dev) >>>> +{ >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>> + >>>> + /* The transition from resume to run is a reset event. >>>> + * This leads to the RTC registers being reset after resume from >>>> + * suspend. It is uncommon, but this behaviour has been observed >>>> + * on S32G RTC after issuing a Suspend to RAM operation. >>>> + * Thus, reconfigure RTC registers on the resume path. >>>> + */ >>>> + return rtc_clk_src_setup(priv); >>>> +} >>>> + >>>> +static const struct of_device_id rtc_dt_ids[] = { >>>> + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, >>>> + { /* sentinel */ }, >>>> +}; >>>> + >>>> +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, >>>> + s32g_rtc_suspend, s32g_rtc_resume); >>>> + >>>> +static struct platform_driver s32g_rtc_driver = { >>>> + .driver = { >>>> + .name = "s32g-rtc", >>>> + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), >>>> + .of_match_table = rtc_dt_ids, >>>> + }, >>>> + .probe = s32g_rtc_probe, >>>> +}; >>>> +module_platform_driver(s32g_rtc_driver); >>>> + >>>> +MODULE_AUTHOR("NXP"); >>>> +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); >>>> +MODULE_LICENSE("GPL"); >>>> -- >>>> 2.45.2 >>>> >>> >> > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support 2025-04-09 14:58 ` Ciprian Marian Costea @ 2025-04-10 7:08 ` Enric Balletbo i Serra 2025-04-11 8:09 ` Ciprian Marian Costea 1 sibling, 0 replies; 19+ messages in thread From: Enric Balletbo i Serra @ 2025-04-10 7:08 UTC (permalink / raw) To: Ciprian Marian Costea Cc: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Eric Chanudet, Bogdan Hamciuc, Ghennadi Procopciuc, Frank Li Hi Ciprian, On Wed, Apr 9, 2025 at 4:58 PM Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> wrote: > > On 4/9/2025 5:42 PM, Enric Balletbo i Serra wrote: > > Hi Ciprian, > > > > > > On Wed, Apr 9, 2025 at 4:26 PM Ciprian Marian Costea > > <ciprianmarian.costea@oss.nxp.com> wrote: > >> > >> On 4/9/2025 5:14 PM, Enric Balletbo i Serra wrote: > >>> Hi Ciprian, > >>> > >>> Many thanks for your patch. > >>> > >>> On Thu, Apr 3, 2025 at 12:34 PM Ciprian Costea > >>> <ciprianmarian.costea@oss.nxp.com> wrote: > >>>> > >>>> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > >>>> > >>>> Add a RTC driver for NXP S32G2/S32G3 SoCs. > >>>> > >>>> RTC tracks clock time during system suspend. It can be a wakeup source > >>>> for the S32G2/S32G3 SoC based boards. > >>>> > >>>> The RTC module from S32G2/S32G3 is not battery-powered and it is not kept > >>>> alive during system reset. > >>>> > >>>> Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > >>>> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > >>>> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > >>>> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > >>>> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > >>>> Reviewed-by: Frank Li <Frank.Li@nxp.com> > >>> > >>> I went through the code and it looks good to me, so: > >>> > >>> However, I did a basic rtctest selftest in my S32G-VNP-RDB3 board, > >>> and some tests failed (it shows a small deviation), any idea what > >>> could be wrong? > >>> > >>> This is the output of the test: > >>> > >>> # ./rtctest > >>> TAP version 13 > >>> 1..8 > >>> # Starting 8 tests from 1 test cases. > >>> # RUN rtc.date_read ... > >>> # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:17:49. > >>> # OK rtc.date_read > >>> ok 1 rtc.date_read > >>> # RUN rtc.date_read_loop ... > >>> # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s > >>> (with 11ms breaks after every read). > >>> # rtctest.c:151:date_read_loop:Performed 2638 RTC time reads. > >>> # OK rtc.date_read_loop > >>> ok 2 rtc.date_read_loop > >>> # RUN rtc.uie_read ... > >>> # OK rtc.uie_read > >>> ok 3 rtc.uie_read > >>> # RUN rtc.uie_select ... > >>> # OK rtc.uie_select > >>> ok 4 rtc.uie_select > >>> # RUN rtc.alarm_alm_set ... > >>> # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:18:31. > >>> # rtctest.c:282:alarm_alm_set:data: 1a0 > >>> # OK rtc.alarm_alm_set > >>> ok 5 rtc.alarm_alm_set > >>> # RUN rtc.alarm_wkalm_set ... > >>> # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:18:34. > >>> # rtctest.c:353:alarm_wkalm_set:Expected new (1738023515) == secs (1738023514) > >>> # alarm_wkalm_set: Test terminated by assertion > >>> # FAIL rtc.alarm_wkalm_set > >>> not ok 6 rtc.alarm_wkalm_set > >>> # RUN rtc.alarm_alm_set_minute ... > >>> # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:19:00. > >>> # rtctest.c:414:alarm_alm_set_minute:data: 1a0 > >>> # rtctest.c:420:alarm_alm_set_minute:Expected new (1738023541) == secs > >>> (1738023540) > >>> # alarm_alm_set_minute: Test terminated by assertion > >>> # FAIL rtc.alarm_alm_set_minute > >>> not ok 7 rtc.alarm_alm_set_minute > >>> # RUN rtc.alarm_wkalm_set_minute ... > >>> # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to > >>> 28/01/2025 00:20:00. > >>> # rtctest.c:483:alarm_wkalm_set_minute:Expected new (1738023601) == > >>> secs (1738023600) > >>> # alarm_wkalm_set_minute: Test terminated by assertion > >>> # FAIL rtc.alarm_wkalm_set_minute > >>> not ok 8 rtc.alarm_wkalm_set_minute > >>> # FAILED: 5 / 8 tests passed. > >>> # Totals: pass:5 fail:3 xfail:0 xpass:0 skip:0 error:0 > >>> > >>> Best regards, > >>> Enric > >>> > >> > >> Hello Enric, > >> > >> In order to obtain a better resolution/precision you could try to only > >> use DIV512 hardware divisor instead of both DIV32 and DIV512. > >> > > > > That makes the tests pass, so I'm wondering if by default you should > > set DIV512 in platform data instead. With that change you can add my > > > > Tested-by: Enric Balletbo i Serra <eballetbo@kernel.org> > > > > This change would decrease the RTC rollover time. Initially indeed only > DIV512 was enabled, but during review [1] I've agreed enabling both > hardware divisors to increase the RTC rollover time. Personally, I am ok > with the current 1 second resolution capability. > I'd say that having rtc selftests failing will add a bit of headache to those that run integration tests, as the test will need to be marked as an exception. Another option would be to make the tests a bit more flexible, but I'm not sure this is the right way. I guess we can make this user configurable via a module parameter so the user has the final decision. Just my two cents. > Alexandre Belloni, are you ok with the current version of this driver ? > Or should I send a V10 patch in which I enable back only the DIV512 > divisor in order to increase the RTC precision at the downside of losing > rollover time. > Agree, up to the maintainer to decide. Regards, Enric > [1] > https://lore.kernel.org/all/9f4fc9d8-ec7f-43b4-8bdd-01a4ba4855c5@oss.nxp.com/ > > Regards, > Ciprian > > > # ./rtctest > > TAP version 13 > > 1..8 > > # Starting 8 tests from 1 test cases. > > # RUN rtc.date_read ... > > # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:00:34. > > # OK rtc.date_read > > ok 1 rtc.date_read > > # RUN rtc.date_read_loop ... > > # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s > > (with 11ms breaks after every read). > > # rtctest.c:151:date_read_loop:Performed 2656 RTC time reads. > > # OK rtc.date_read_loop > > ok 2 rtc.date_read_loop > > # RUN rtc.uie_read ... > > # OK rtc.uie_read > > ok 3 rtc.uie_read > > # RUN rtc.uie_select ... > > # OK rtc.uie_select > > ok 4 rtc.uie_select > > # RUN rtc.alarm_alm_set ... > > # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:01:14. > > # rtctest.c:282:alarm_alm_set:data: 1a0 > > # OK rtc.alarm_alm_set > > ok 5 rtc.alarm_alm_set > > # RUN rtc.alarm_wkalm_set ... > > # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 00:01:17. > > # OK rtc.alarm_wkalm_set > > ok 6 rtc.alarm_wkalm_set > > # RUN rtc.alarm_alm_set_minute ... > > # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:02:00. > > # rtctest.c:414:alarm_alm_set_minute:data: 1a0 > > # OK rtc.alarm_alm_set_minute > > ok 7 rtc.alarm_alm_set_minute > > # RUN rtc.alarm_wkalm_set_minute ... > > # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to > > 28/01/2025 00:03:00. > > # OK rtc.alarm_wkalm_set_minute > > ok 8 rtc.alarm_wkalm_set_minute > > # PASSED: 8 / 8 tests passed. > > # Totals: pass:8 fail:0 xfail:0 xpass:0 skip:0 error:0 > > > > > > Thanks, > > Enric > > > >> Regards, > >> Ciprian > >> > >>>> --- > >>>> drivers/rtc/Kconfig | 11 ++ > >>>> drivers/rtc/Makefile | 1 + > >>>> drivers/rtc/rtc-s32g.c | 385 +++++++++++++++++++++++++++++++++++++++++ > >>>> 3 files changed, 397 insertions(+) > >>>> create mode 100644 drivers/rtc/rtc-s32g.c > >>>> > >>>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > >>>> index 0bbbf778ecfa..510dc2db745d 100644 > >>>> --- a/drivers/rtc/Kconfig > >>>> +++ b/drivers/rtc/Kconfig > >>>> @@ -2103,4 +2103,15 @@ config RTC_DRV_AMLOGIC_A4 > >>>> This driver can also be built as a module. If so, the module > >>>> will be called "rtc-amlogic-a4". > >>>> > >>>> +config RTC_DRV_S32G > >>>> + tristate "RTC driver for S32G2/S32G3 SoCs" > >>>> + depends on ARCH_S32 || COMPILE_TEST > >>>> + depends on COMMON_CLK > >>>> + help > >>>> + Say yes to enable RTC driver for platforms based on the > >>>> + S32G2/S32G3 SoC family. > >>>> + > >>>> + This RTC module can be used as a wakeup source. > >>>> + Please note that it is not battery-powered. > >>>> + > >>>> endif # RTC_CLASS > >>>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > >>>> index 489b4ab07068..e4b616ecd5ce 100644 > >>>> --- a/drivers/rtc/Makefile > >>>> +++ b/drivers/rtc/Makefile > >>>> @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o > >>>> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o > >>>> obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o > >>>> obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o > >>>> +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o > >>>> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o > >>>> obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o > >>>> obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o > >>>> diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c > >>>> new file mode 100644 > >>>> index 000000000000..3a0818e972eb > >>>> --- /dev/null > >>>> +++ b/drivers/rtc/rtc-s32g.c > >>>> @@ -0,0 +1,385 @@ > >>>> +// SPDX-License-Identifier: GPL-2.0-or-later > >>>> +/* > >>>> + * Copyright 2025 NXP > >>>> + */ > >>>> + > >>>> +#include <linux/bitfield.h> > >>>> +#include <linux/clk.h> > >>>> +#include <linux/iopoll.h> > >>>> +#include <linux/of_irq.h> > >>>> +#include <linux/platform_device.h> > >>>> +#include <linux/rtc.h> > >>>> + > >>>> +#define RTCC_OFFSET 0x4ul > >>>> +#define RTCS_OFFSET 0x8ul > >>>> +#define APIVAL_OFFSET 0x10ul > >>>> + > >>>> +/* RTCC fields */ > >>>> +#define RTCC_CNTEN BIT(31) > >>>> +#define RTCC_APIEN BIT(15) > >>>> +#define RTCC_APIIE BIT(14) > >>>> +#define RTCC_CLKSEL_MASK GENMASK(13, 12) > >>>> +#define RTCC_DIV512EN BIT(11) > >>>> +#define RTCC_DIV32EN BIT(10) > >>>> + > >>>> +/* RTCS fields */ > >>>> +#define RTCS_INV_API BIT(17) > >>>> +#define RTCS_APIF BIT(13) > >>>> + > >>>> +#define APIVAL_MAX_VAL GENMASK(31, 0) > >>>> +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) > >>>> + > >>>> +/* > >>>> + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and > >>>> + * should not be used. > >>>> + */ > >>>> +#define RTC_CLK_SRC1_RESERVED BIT(1) > >>>> + > >>>> +/* > >>>> + * S32G RTC module has a 512 value and a 32 value hardware frequency > >>>> + * divisors (DIV512 and DIV32) which could be used to achieve higher > >>>> + * counter ranges by lowering the RTC frequency. > >>>> + */ > >>>> +enum { > >>>> + DIV1 = 1, > >>>> + DIV32 = 32, > >>>> + DIV512 = 512, > >>>> + DIV512_32 = 16384 > >>>> +}; > >>>> + > >>>> +static const char *const rtc_clk_src[] = { > >>>> + "source0", > >>>> + "source1", > >>>> + "source2", > >>>> + "source3" > >>>> +}; > >>>> + > >>>> +struct rtc_priv { > >>>> + struct rtc_device *rdev; > >>>> + void __iomem *rtc_base; > >>>> + struct clk *ipg; > >>>> + struct clk *clk_src; > >>>> + const struct rtc_soc_data *rtc_data; > >>>> + u64 rtc_hz; > >>>> + time64_t sleep_sec; > >>>> + int irq; > >>>> + u32 clk_src_idx; > >>>> +}; > >>>> + > >>>> +struct rtc_soc_data { > >>>> + u32 clk_div; > >>>> + u32 reserved_clk_mask; > >>>> +}; > >>>> + > >>>> +static const struct rtc_soc_data rtc_s32g2_data = { > >>>> + .clk_div = DIV512_32, > >>>> + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, > >>>> +}; > >>>> + > >>>> +static irqreturn_t s32g_rtc_handler(int irq, void *dev) > >>>> +{ > >>>> + struct rtc_priv *priv = platform_get_drvdata(dev); > >>>> + u32 status; > >>>> + > >>>> + status = readl(priv->rtc_base + RTCS_OFFSET); > >>>> + > >>>> + if (status & RTCS_APIF) { > >>>> + writel(0x0, priv->rtc_base + APIVAL_OFFSET); > >>>> + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); > >>>> + } > >>>> + > >>>> + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); > >>>> + > >>>> + return IRQ_HANDLED; > >>>> +} > >>>> + > >>>> +/* > >>>> + * The function is not really getting time from the RTC since the S32G RTC > >>>> + * has several limitations. Thus, to setup alarm use system time. > >>>> + */ > >>>> +static int s32g_rtc_read_time(struct device *dev, > >>>> + struct rtc_time *tm) > >>>> +{ > >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); > >>>> + time64_t sec; > >>>> + > >>>> + if (check_add_overflow(ktime_get_real_seconds(), > >>>> + priv->sleep_sec, &sec)) > >>>> + return -ERANGE; > >>>> + > >>>> + rtc_time64_to_tm(sec, tm); > >>>> + > >>>> + return 0; > >>>> +} > >>>> + > >>>> +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) > >>>> +{ > >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); > >>>> + u32 rtcc, rtcs; > >>>> + > >>>> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >>>> + rtcs = readl(priv->rtc_base + RTCS_OFFSET); > >>>> + > >>>> + alrm->enabled = rtcc & RTCC_APIIE; > >>>> + if (alrm->enabled) > >>>> + alrm->pending = !(rtcs & RTCS_APIF); > >>>> + > >>>> + return 0; > >>>> +} > >>>> + > >>>> +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) > >>>> +{ > >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); > >>>> + u32 rtcc; > >>>> + > >>>> + /* RTC API functionality is used both for triggering interrupts > >>>> + * and as a wakeup event. Hence it should always be enabled. > >>>> + */ > >>>> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >>>> + rtcc |= RTCC_APIEN | RTCC_APIIE; > >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >>>> + > >>>> + return 0; > >>>> +} > >>>> + > >>>> +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) > >>>> +{ > >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); > >>>> + unsigned long long cycles; > >>>> + long long t_offset; > >>>> + time64_t alrm_time; > >>>> + u32 rtcs; > >>>> + int ret; > >>>> + > >>>> + alrm_time = rtc_tm_to_time64(&alrm->time); > >>>> + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; > >>>> + if (t_offset < 0) > >>>> + return -ERANGE; > >>>> + > >>>> + cycles = t_offset * priv->rtc_hz; > >>>> + if (cycles > APIVAL_MAX_VAL) > >>>> + return -ERANGE; > >>>> + > >>>> + /* APIVAL could have been reset from the IRQ handler. > >>>> + * Hence, we wait in case there is a synchronization process. > >>>> + */ > >>>> + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), > >>>> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); > >>>> + if (ret) > >>>> + return ret; > >>>> + > >>>> + writel(cycles, priv->rtc_base + APIVAL_OFFSET); > >>>> + > >>>> + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), > >>>> + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); > >>>> +} > >>>> + > >>>> +/* > >>>> + * Disable the 32-bit free running counter. > >>>> + * This allows Clock Source and Divisors selection > >>>> + * to be performed without causing synchronization issues. > >>>> + */ > >>>> +static void s32g_rtc_disable(struct rtc_priv *priv) > >>>> +{ > >>>> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >>>> + > >>>> + rtcc &= ~RTCC_CNTEN; > >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >>>> +} > >>>> + > >>>> +static void s32g_rtc_enable(struct rtc_priv *priv) > >>>> +{ > >>>> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); > >>>> + > >>>> + rtcc |= RTCC_CNTEN; > >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >>>> +} > >>>> + > >>>> +static int rtc_clk_src_setup(struct rtc_priv *priv) > >>>> +{ > >>>> + u32 rtcc; > >>>> + > >>>> + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); > >>>> + > >>>> + switch (priv->rtc_data->clk_div) { > >>>> + case DIV512_32: > >>>> + rtcc |= RTCC_DIV512EN; > >>>> + rtcc |= RTCC_DIV32EN; > >>>> + break; > >>>> + case DIV512: > >>>> + rtcc |= RTCC_DIV512EN; > >>>> + break; > >>>> + case DIV32: > >>>> + rtcc |= RTCC_DIV32EN; > >>>> + break; > >>>> + case DIV1: > >>>> + break; > >>>> + default: > >>>> + return -EINVAL; > >>>> + } > >>>> + > >>>> + rtcc |= RTCC_APIEN | RTCC_APIIE; > >>>> + /* > >>>> + * Make sure the CNTEN is 0 before we configure > >>>> + * the clock source and dividers. > >>>> + */ > >>>> + s32g_rtc_disable(priv); > >>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); > >>>> + s32g_rtc_enable(priv); > >>>> + > >>>> + return 0; > >>>> +} > >>>> + > >>>> +static const struct rtc_class_ops rtc_ops = { > >>>> + .read_time = s32g_rtc_read_time, > >>>> + .read_alarm = s32g_rtc_read_alarm, > >>>> + .set_alarm = s32g_rtc_set_alarm, > >>>> + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, > >>>> +}; > >>>> + > >>>> +static int rtc_clk_dts_setup(struct rtc_priv *priv, > >>>> + struct device *dev) > >>>> +{ > >>>> + u32 i; > >>>> + > >>>> + priv->ipg = devm_clk_get_enabled(dev, "ipg"); > >>>> + if (IS_ERR(priv->ipg)) > >>>> + return dev_err_probe(dev, PTR_ERR(priv->ipg), > >>>> + "Failed to get 'ipg' clock\n"); > >>>> + > >>>> + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { > >>>> + if (priv->rtc_data->reserved_clk_mask & BIT(i)) > >>>> + return -EOPNOTSUPP; > >>>> + > >>>> + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); > >>>> + if (!IS_ERR(priv->clk_src)) { > >>>> + priv->clk_src_idx = i; > >>>> + break; > >>>> + } > >>>> + } > >>>> + > >>>> + if (IS_ERR(priv->clk_src)) > >>>> + return dev_err_probe(dev, PTR_ERR(priv->clk_src), > >>>> + "Failed to get rtc module clock source\n"); > >>>> + > >>>> + return 0; > >>>> +} > >>>> + > >>>> +static int s32g_rtc_probe(struct platform_device *pdev) > >>>> +{ > >>>> + struct device *dev = &pdev->dev; > >>>> + struct rtc_priv *priv; > >>>> + unsigned long rtc_hz; > >>>> + int ret; > >>>> + > >>>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > >>>> + if (!priv) > >>>> + return -ENOMEM; > >>>> + > >>>> + priv->rtc_data = of_device_get_match_data(dev); > >>>> + if (!priv->rtc_data) > >>>> + return -ENODEV; > >>>> + > >>>> + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); > >>>> + if (IS_ERR(priv->rtc_base)) > >>>> + return PTR_ERR(priv->rtc_base); > >>>> + > >>>> + device_init_wakeup(dev, true); > >>>> + > >>>> + ret = rtc_clk_dts_setup(priv, dev); > >>>> + if (ret) > >>>> + return ret; > >>>> + > >>>> + priv->rdev = devm_rtc_allocate_device(dev); > >>>> + if (IS_ERR(priv->rdev)) > >>>> + return PTR_ERR(priv->rdev); > >>>> + > >>>> + ret = rtc_clk_src_setup(priv); > >>>> + if (ret) > >>>> + return ret; > >>>> + > >>>> + priv->irq = platform_get_irq(pdev, 0); > >>>> + if (priv->irq < 0) { > >>>> + ret = priv->irq; > >>>> + goto disable_rtc; > >>>> + } > >>>> + > >>>> + rtc_hz = clk_get_rate(priv->clk_src); > >>>> + if (!rtc_hz) { > >>>> + dev_err(dev, "Failed to get RTC frequency\n"); > >>>> + ret = -EINVAL; > >>>> + goto disable_rtc; > >>>> + } > >>>> + > >>>> + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); > >>>> + > >>>> + platform_set_drvdata(pdev, priv); > >>>> + priv->rdev->ops = &rtc_ops; > >>>> + > >>>> + ret = devm_request_irq(dev, priv->irq, > >>>> + s32g_rtc_handler, 0, dev_name(dev), pdev); > >>>> + if (ret) { > >>>> + dev_err(dev, "Request interrupt %d failed, error: %d\n", > >>>> + priv->irq, ret); > >>>> + goto disable_rtc; > >>>> + } > >>>> + > >>>> + ret = devm_rtc_register_device(priv->rdev); > >>>> + if (ret) > >>>> + goto disable_rtc; > >>>> + > >>>> + return 0; > >>>> + > >>>> +disable_rtc: > >>>> + s32g_rtc_disable(priv); > >>>> + return ret; > >>>> +} > >>>> + > >>>> +static int s32g_rtc_suspend(struct device *dev) > >>>> +{ > >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); > >>>> + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); > >>>> + > >>>> + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), > >>>> + &priv->sleep_sec)) { > >>>> + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); > >>>> + priv->sleep_sec = 0; > >>>> + } > >>>> + > >>>> + return 0; > >>>> +} > >>>> + > >>>> +static int s32g_rtc_resume(struct device *dev) > >>>> +{ > >>>> + struct rtc_priv *priv = dev_get_drvdata(dev); > >>>> + > >>>> + /* The transition from resume to run is a reset event. > >>>> + * This leads to the RTC registers being reset after resume from > >>>> + * suspend. It is uncommon, but this behaviour has been observed > >>>> + * on S32G RTC after issuing a Suspend to RAM operation. > >>>> + * Thus, reconfigure RTC registers on the resume path. > >>>> + */ > >>>> + return rtc_clk_src_setup(priv); > >>>> +} > >>>> + > >>>> +static const struct of_device_id rtc_dt_ids[] = { > >>>> + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, > >>>> + { /* sentinel */ }, > >>>> +}; > >>>> + > >>>> +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, > >>>> + s32g_rtc_suspend, s32g_rtc_resume); > >>>> + > >>>> +static struct platform_driver s32g_rtc_driver = { > >>>> + .driver = { > >>>> + .name = "s32g-rtc", > >>>> + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), > >>>> + .of_match_table = rtc_dt_ids, > >>>> + }, > >>>> + .probe = s32g_rtc_probe, > >>>> +}; > >>>> +module_platform_driver(s32g_rtc_driver); > >>>> + > >>>> +MODULE_AUTHOR("NXP"); > >>>> +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); > >>>> +MODULE_LICENSE("GPL"); > >>>> -- > >>>> 2.45.2 > >>>> > >>> > >> > > > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support 2025-04-09 14:58 ` Ciprian Marian Costea 2025-04-10 7:08 ` Enric Balletbo i Serra @ 2025-04-11 8:09 ` Ciprian Marian Costea 1 sibling, 0 replies; 19+ messages in thread From: Ciprian Marian Costea @ 2025-04-11 8:09 UTC (permalink / raw) To: Alexandre Belloni Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Eric Chanudet, Bogdan Hamciuc, Ghennadi Procopciuc, Frank Li, Enric Balletbo On 4/9/2025 5:58 PM, Ciprian Marian Costea wrote: > On 4/9/2025 5:42 PM, Enric Balletbo i Serra wrote: >> Hi Ciprian, >> >> >> On Wed, Apr 9, 2025 at 4:26 PM Ciprian Marian Costea >> <ciprianmarian.costea@oss.nxp.com> wrote: >>> >>> On 4/9/2025 5:14 PM, Enric Balletbo i Serra wrote: >>>> Hi Ciprian, >>>> >>>> Many thanks for your patch. >>>> >>>> On Thu, Apr 3, 2025 at 12:34 PM Ciprian Costea >>>> <ciprianmarian.costea@oss.nxp.com> wrote: >>>>> >>>>> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >>>>> >>>>> Add a RTC driver for NXP S32G2/S32G3 SoCs. >>>>> >>>>> RTC tracks clock time during system suspend. It can be a wakeup source >>>>> for the S32G2/S32G3 SoC based boards. >>>>> >>>>> The RTC module from S32G2/S32G3 is not battery-powered and it is >>>>> not kept >>>>> alive during system reset. >>>>> >>>>> Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> >>>>> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> >>>>> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> >>>>> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> >>>>> Signed-off-by: Ciprian Marian Costea >>>>> <ciprianmarian.costea@oss.nxp.com> >>>>> Reviewed-by: Frank Li <Frank.Li@nxp.com> >>>> >>>> I went through the code and it looks good to me, so: >>>> >>>> However, I did a basic rtctest selftest in my S32G-VNP-RDB3 board, >>>> and some tests failed (it shows a small deviation), any idea what >>>> could be wrong? >>>> >>>> This is the output of the test: >>>> >>>> # ./rtctest >>>> TAP version 13 >>>> 1..8 >>>> # Starting 8 tests from 1 test cases. >>>> # RUN rtc.date_read ... >>>> # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:17:49. >>>> # OK rtc.date_read >>>> ok 1 rtc.date_read >>>> # RUN rtc.date_read_loop ... >>>> # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s >>>> (with 11ms breaks after every read). >>>> # rtctest.c:151:date_read_loop:Performed 2638 RTC time reads. >>>> # OK rtc.date_read_loop >>>> ok 2 rtc.date_read_loop >>>> # RUN rtc.uie_read ... >>>> # OK rtc.uie_read >>>> ok 3 rtc.uie_read >>>> # RUN rtc.uie_select ... >>>> # OK rtc.uie_select >>>> ok 4 rtc.uie_select >>>> # RUN rtc.alarm_alm_set ... >>>> # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:18:31. >>>> # rtctest.c:282:alarm_alm_set:data: 1a0 >>>> # OK rtc.alarm_alm_set >>>> ok 5 rtc.alarm_alm_set >>>> # RUN rtc.alarm_wkalm_set ... >>>> # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 >>>> 00:18:34. >>>> # rtctest.c:353:alarm_wkalm_set:Expected new (1738023515) == secs >>>> (1738023514) >>>> # alarm_wkalm_set: Test terminated by assertion >>>> # FAIL rtc.alarm_wkalm_set >>>> not ok 6 rtc.alarm_wkalm_set >>>> # RUN rtc.alarm_alm_set_minute ... >>>> # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:19:00. >>>> # rtctest.c:414:alarm_alm_set_minute:data: 1a0 >>>> # rtctest.c:420:alarm_alm_set_minute:Expected new (1738023541) == secs >>>> (1738023540) >>>> # alarm_alm_set_minute: Test terminated by assertion >>>> # FAIL rtc.alarm_alm_set_minute >>>> not ok 7 rtc.alarm_alm_set_minute >>>> # RUN rtc.alarm_wkalm_set_minute ... >>>> # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to >>>> 28/01/2025 00:20:00. >>>> # rtctest.c:483:alarm_wkalm_set_minute:Expected new (1738023601) == >>>> secs (1738023600) >>>> # alarm_wkalm_set_minute: Test terminated by assertion >>>> # FAIL rtc.alarm_wkalm_set_minute >>>> not ok 8 rtc.alarm_wkalm_set_minute >>>> # FAILED: 5 / 8 tests passed. >>>> # Totals: pass:5 fail:3 xfail:0 xpass:0 skip:0 error:0 >>>> >>>> Best regards, >>>> Enric >>>> >>> >>> Hello Enric, >>> >>> In order to obtain a better resolution/precision you could try to only >>> use DIV512 hardware divisor instead of both DIV32 and DIV512. >>> >> >> That makes the tests pass, so I'm wondering if by default you should >> set DIV512 in platform data instead. With that change you can add my >> >> Tested-by: Enric Balletbo i Serra <eballetbo@kernel.org> >> > > This change would decrease the RTC rollover time. Initially indeed only > DIV512 was enabled, but during review [1] I've agreed enabling both > hardware divisors to increase the RTC rollover time. Personally, I am ok > with the current 1 second resolution capability. > > Alexandre Belloni, are you ok with the current version of this driver ? > Or should I send a V10 patch in which I enable back only the DIV512 > divisor in order to increase the RTC precision at the downside of losing > rollover time. > > [1] > https://lore.kernel.org/all/9f4fc9d8-ec7f-43b4-8bdd-01a4ba4855c5@oss.nxp.com/ > > Regards, > Ciprian > Hello Alexandre, I just want to make sure this email reaches you with my above question, since you were just in CC previously. Sorry for the inconvenience, I just do not want all the effort invested in this driver (development + review) to get lost. Regards, Ciprian >> # ./rtctest >> TAP version 13 >> 1..8 >> # Starting 8 tests from 1 test cases. >> # RUN rtc.date_read ... >> # rtctest.c:59:date_read:Current RTC date/time is 28/01/2025 00:00:34. >> # OK rtc.date_read >> ok 1 rtc.date_read >> # RUN rtc.date_read_loop ... >> # rtctest.c:124:date_read_loop:Continuously reading RTC time for 30s >> (with 11ms breaks after every read). >> # rtctest.c:151:date_read_loop:Performed 2656 RTC time reads. >> # OK rtc.date_read_loop >> ok 2 rtc.date_read_loop >> # RUN rtc.uie_read ... >> # OK rtc.uie_read >> ok 3 rtc.uie_read >> # RUN rtc.uie_select ... >> # OK rtc.uie_select >> ok 4 rtc.uie_select >> # RUN rtc.alarm_alm_set ... >> # rtctest.c:262:alarm_alm_set:Alarm time now set to 00:01:14. >> # rtctest.c:282:alarm_alm_set:data: 1a0 >> # OK rtc.alarm_alm_set >> ok 5 rtc.alarm_alm_set >> # RUN rtc.alarm_wkalm_set ... >> # rtctest.c:334:alarm_wkalm_set:Alarm time now set to 28/01/2025 >> 00:01:17. >> # OK rtc.alarm_wkalm_set >> ok 6 rtc.alarm_wkalm_set >> # RUN rtc.alarm_alm_set_minute ... >> # rtctest.c:394:alarm_alm_set_minute:Alarm time now set to 00:02:00. >> # rtctest.c:414:alarm_alm_set_minute:data: 1a0 >> # OK rtc.alarm_alm_set_minute >> ok 7 rtc.alarm_alm_set_minute >> # RUN rtc.alarm_wkalm_set_minute ... >> # rtctest.c:464:alarm_wkalm_set_minute:Alarm time now set to >> 28/01/2025 00:03:00. >> # OK rtc.alarm_wkalm_set_minute >> ok 8 rtc.alarm_wkalm_set_minute >> # PASSED: 8 / 8 tests passed. >> # Totals: pass:8 fail:0 xfail:0 xpass:0 skip:0 error:0 >> >> >> Thanks, >> Enric >> >>> Regards, >>> Ciprian >>> >>>>> --- >>>>> drivers/rtc/Kconfig | 11 ++ >>>>> drivers/rtc/Makefile | 1 + >>>>> drivers/rtc/rtc-s32g.c | 385 >>>>> +++++++++++++++++++++++++++++++++++++++++ >>>>> 3 files changed, 397 insertions(+) >>>>> create mode 100644 drivers/rtc/rtc-s32g.c >>>>> >>>>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig >>>>> index 0bbbf778ecfa..510dc2db745d 100644 >>>>> --- a/drivers/rtc/Kconfig >>>>> +++ b/drivers/rtc/Kconfig >>>>> @@ -2103,4 +2103,15 @@ config RTC_DRV_AMLOGIC_A4 >>>>> This driver can also be built as a module. If so, the >>>>> module >>>>> will be called "rtc-amlogic-a4". >>>>> >>>>> +config RTC_DRV_S32G >>>>> + tristate "RTC driver for S32G2/S32G3 SoCs" >>>>> + depends on ARCH_S32 || COMPILE_TEST >>>>> + depends on COMMON_CLK >>>>> + help >>>>> + Say yes to enable RTC driver for platforms based on the >>>>> + S32G2/S32G3 SoC family. >>>>> + >>>>> + This RTC module can be used as a wakeup source. >>>>> + Please note that it is not battery-powered. >>>>> + >>>>> endif # RTC_CLASS >>>>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile >>>>> index 489b4ab07068..e4b616ecd5ce 100644 >>>>> --- a/drivers/rtc/Makefile >>>>> +++ b/drivers/rtc/Makefile >>>>> @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += >>>>> rtc-rx8111.o >>>>> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o >>>>> obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o >>>>> obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o >>>>> +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o >>>>> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o >>>>> obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o >>>>> obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o >>>>> diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c >>>>> new file mode 100644 >>>>> index 000000000000..3a0818e972eb >>>>> --- /dev/null >>>>> +++ b/drivers/rtc/rtc-s32g.c >>>>> @@ -0,0 +1,385 @@ >>>>> +// SPDX-License-Identifier: GPL-2.0-or-later >>>>> +/* >>>>> + * Copyright 2025 NXP >>>>> + */ >>>>> + >>>>> +#include <linux/bitfield.h> >>>>> +#include <linux/clk.h> >>>>> +#include <linux/iopoll.h> >>>>> +#include <linux/of_irq.h> >>>>> +#include <linux/platform_device.h> >>>>> +#include <linux/rtc.h> >>>>> + >>>>> +#define RTCC_OFFSET 0x4ul >>>>> +#define RTCS_OFFSET 0x8ul >>>>> +#define APIVAL_OFFSET 0x10ul >>>>> + >>>>> +/* RTCC fields */ >>>>> +#define RTCC_CNTEN BIT(31) >>>>> +#define RTCC_APIEN BIT(15) >>>>> +#define RTCC_APIIE BIT(14) >>>>> +#define RTCC_CLKSEL_MASK GENMASK(13, 12) >>>>> +#define RTCC_DIV512EN BIT(11) >>>>> +#define RTCC_DIV32EN BIT(10) >>>>> + >>>>> +/* RTCS fields */ >>>>> +#define RTCS_INV_API BIT(17) >>>>> +#define RTCS_APIF BIT(13) >>>>> + >>>>> +#define APIVAL_MAX_VAL GENMASK(31, 0) >>>>> +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) >>>>> + >>>>> +/* >>>>> + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and >>>>> + * should not be used. >>>>> + */ >>>>> +#define RTC_CLK_SRC1_RESERVED BIT(1) >>>>> + >>>>> +/* >>>>> + * S32G RTC module has a 512 value and a 32 value hardware frequency >>>>> + * divisors (DIV512 and DIV32) which could be used to achieve higher >>>>> + * counter ranges by lowering the RTC frequency. >>>>> + */ >>>>> +enum { >>>>> + DIV1 = 1, >>>>> + DIV32 = 32, >>>>> + DIV512 = 512, >>>>> + DIV512_32 = 16384 >>>>> +}; >>>>> + >>>>> +static const char *const rtc_clk_src[] = { >>>>> + "source0", >>>>> + "source1", >>>>> + "source2", >>>>> + "source3" >>>>> +}; >>>>> + >>>>> +struct rtc_priv { >>>>> + struct rtc_device *rdev; >>>>> + void __iomem *rtc_base; >>>>> + struct clk *ipg; >>>>> + struct clk *clk_src; >>>>> + const struct rtc_soc_data *rtc_data; >>>>> + u64 rtc_hz; >>>>> + time64_t sleep_sec; >>>>> + int irq; >>>>> + u32 clk_src_idx; >>>>> +}; >>>>> + >>>>> +struct rtc_soc_data { >>>>> + u32 clk_div; >>>>> + u32 reserved_clk_mask; >>>>> +}; >>>>> + >>>>> +static const struct rtc_soc_data rtc_s32g2_data = { >>>>> + .clk_div = DIV512_32, >>>>> + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, >>>>> +}; >>>>> + >>>>> +static irqreturn_t s32g_rtc_handler(int irq, void *dev) >>>>> +{ >>>>> + struct rtc_priv *priv = platform_get_drvdata(dev); >>>>> + u32 status; >>>>> + >>>>> + status = readl(priv->rtc_base + RTCS_OFFSET); >>>>> + >>>>> + if (status & RTCS_APIF) { >>>>> + writel(0x0, priv->rtc_base + APIVAL_OFFSET); >>>>> + writel(status | RTCS_APIF, priv->rtc_base + >>>>> RTCS_OFFSET); >>>>> + } >>>>> + >>>>> + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); >>>>> + >>>>> + return IRQ_HANDLED; >>>>> +} >>>>> + >>>>> +/* >>>>> + * The function is not really getting time from the RTC since the >>>>> S32G RTC >>>>> + * has several limitations. Thus, to setup alarm use system time. >>>>> + */ >>>>> +static int s32g_rtc_read_time(struct device *dev, >>>>> + struct rtc_time *tm) >>>>> +{ >>>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>>> + time64_t sec; >>>>> + >>>>> + if (check_add_overflow(ktime_get_real_seconds(), >>>>> + priv->sleep_sec, &sec)) >>>>> + return -ERANGE; >>>>> + >>>>> + rtc_time64_to_tm(sec, tm); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static int s32g_rtc_read_alarm(struct device *dev, struct >>>>> rtc_wkalrm *alrm) >>>>> +{ >>>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>>> + u32 rtcc, rtcs; >>>>> + >>>>> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>>> + rtcs = readl(priv->rtc_base + RTCS_OFFSET); >>>>> + >>>>> + alrm->enabled = rtcc & RTCC_APIIE; >>>>> + if (alrm->enabled) >>>>> + alrm->pending = !(rtcs & RTCS_APIF); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned >>>>> int enabled) >>>>> +{ >>>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>>> + u32 rtcc; >>>>> + >>>>> + /* RTC API functionality is used both for triggering >>>>> interrupts >>>>> + * and as a wakeup event. Hence it should always be enabled. >>>>> + */ >>>>> + rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>>> + rtcc |= RTCC_APIEN | RTCC_APIIE; >>>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static int s32g_rtc_set_alarm(struct device *dev, struct >>>>> rtc_wkalrm *alrm) >>>>> +{ >>>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>>> + unsigned long long cycles; >>>>> + long long t_offset; >>>>> + time64_t alrm_time; >>>>> + u32 rtcs; >>>>> + int ret; >>>>> + >>>>> + alrm_time = rtc_tm_to_time64(&alrm->time); >>>>> + t_offset = alrm_time - ktime_get_real_seconds() - >>>>> priv->sleep_sec; >>>>> + if (t_offset < 0) >>>>> + return -ERANGE; >>>>> + >>>>> + cycles = t_offset * priv->rtc_hz; >>>>> + if (cycles > APIVAL_MAX_VAL) >>>>> + return -ERANGE; >>>>> + >>>>> + /* APIVAL could have been reset from the IRQ handler. >>>>> + * Hence, we wait in case there is a synchronization process. >>>>> + */ >>>>> + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), >>>>> + 0, RTC_SYNCH_TIMEOUT, false, >>>>> priv->rtc_base + RTCS_OFFSET); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + writel(cycles, priv->rtc_base + APIVAL_OFFSET); >>>>> + >>>>> + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), >>>>> + 0, RTC_SYNCH_TIMEOUT, false, >>>>> priv->rtc_base + RTCS_OFFSET); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Disable the 32-bit free running counter. >>>>> + * This allows Clock Source and Divisors selection >>>>> + * to be performed without causing synchronization issues. >>>>> + */ >>>>> +static void s32g_rtc_disable(struct rtc_priv *priv) >>>>> +{ >>>>> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>>> + >>>>> + rtcc &= ~RTCC_CNTEN; >>>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>>> +} >>>>> + >>>>> +static void s32g_rtc_enable(struct rtc_priv *priv) >>>>> +{ >>>>> + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); >>>>> + >>>>> + rtcc |= RTCC_CNTEN; >>>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>>> +} >>>>> + >>>>> +static int rtc_clk_src_setup(struct rtc_priv *priv) >>>>> +{ >>>>> + u32 rtcc; >>>>> + >>>>> + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); >>>>> + >>>>> + switch (priv->rtc_data->clk_div) { >>>>> + case DIV512_32: >>>>> + rtcc |= RTCC_DIV512EN; >>>>> + rtcc |= RTCC_DIV32EN; >>>>> + break; >>>>> + case DIV512: >>>>> + rtcc |= RTCC_DIV512EN; >>>>> + break; >>>>> + case DIV32: >>>>> + rtcc |= RTCC_DIV32EN; >>>>> + break; >>>>> + case DIV1: >>>>> + break; >>>>> + default: >>>>> + return -EINVAL; >>>>> + } >>>>> + >>>>> + rtcc |= RTCC_APIEN | RTCC_APIIE; >>>>> + /* >>>>> + * Make sure the CNTEN is 0 before we configure >>>>> + * the clock source and dividers. >>>>> + */ >>>>> + s32g_rtc_disable(priv); >>>>> + writel(rtcc, priv->rtc_base + RTCC_OFFSET); >>>>> + s32g_rtc_enable(priv); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static const struct rtc_class_ops rtc_ops = { >>>>> + .read_time = s32g_rtc_read_time, >>>>> + .read_alarm = s32g_rtc_read_alarm, >>>>> + .set_alarm = s32g_rtc_set_alarm, >>>>> + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, >>>>> +}; >>>>> + >>>>> +static int rtc_clk_dts_setup(struct rtc_priv *priv, >>>>> + struct device *dev) >>>>> +{ >>>>> + u32 i; >>>>> + >>>>> + priv->ipg = devm_clk_get_enabled(dev, "ipg"); >>>>> + if (IS_ERR(priv->ipg)) >>>>> + return dev_err_probe(dev, PTR_ERR(priv->ipg), >>>>> + "Failed to get 'ipg' clock\n"); >>>>> + >>>>> + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { >>>>> + if (priv->rtc_data->reserved_clk_mask & BIT(i)) >>>>> + return -EOPNOTSUPP; >>>>> + >>>>> + priv->clk_src = devm_clk_get_enabled(dev, >>>>> rtc_clk_src[i]); >>>>> + if (!IS_ERR(priv->clk_src)) { >>>>> + priv->clk_src_idx = i; >>>>> + break; >>>>> + } >>>>> + } >>>>> + >>>>> + if (IS_ERR(priv->clk_src)) >>>>> + return dev_err_probe(dev, PTR_ERR(priv->clk_src), >>>>> + "Failed to get rtc module clock >>>>> source\n"); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static int s32g_rtc_probe(struct platform_device *pdev) >>>>> +{ >>>>> + struct device *dev = &pdev->dev; >>>>> + struct rtc_priv *priv; >>>>> + unsigned long rtc_hz; >>>>> + int ret; >>>>> + >>>>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); >>>>> + if (!priv) >>>>> + return -ENOMEM; >>>>> + >>>>> + priv->rtc_data = of_device_get_match_data(dev); >>>>> + if (!priv->rtc_data) >>>>> + return -ENODEV; >>>>> + >>>>> + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); >>>>> + if (IS_ERR(priv->rtc_base)) >>>>> + return PTR_ERR(priv->rtc_base); >>>>> + >>>>> + device_init_wakeup(dev, true); >>>>> + >>>>> + ret = rtc_clk_dts_setup(priv, dev); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + priv->rdev = devm_rtc_allocate_device(dev); >>>>> + if (IS_ERR(priv->rdev)) >>>>> + return PTR_ERR(priv->rdev); >>>>> + >>>>> + ret = rtc_clk_src_setup(priv); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + priv->irq = platform_get_irq(pdev, 0); >>>>> + if (priv->irq < 0) { >>>>> + ret = priv->irq; >>>>> + goto disable_rtc; >>>>> + } >>>>> + >>>>> + rtc_hz = clk_get_rate(priv->clk_src); >>>>> + if (!rtc_hz) { >>>>> + dev_err(dev, "Failed to get RTC frequency\n"); >>>>> + ret = -EINVAL; >>>>> + goto disable_rtc; >>>>> + } >>>>> + >>>>> + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); >>>>> + >>>>> + platform_set_drvdata(pdev, priv); >>>>> + priv->rdev->ops = &rtc_ops; >>>>> + >>>>> + ret = devm_request_irq(dev, priv->irq, >>>>> + s32g_rtc_handler, 0, dev_name(dev), >>>>> pdev); >>>>> + if (ret) { >>>>> + dev_err(dev, "Request interrupt %d failed, error: >>>>> %d\n", >>>>> + priv->irq, ret); >>>>> + goto disable_rtc; >>>>> + } >>>>> + >>>>> + ret = devm_rtc_register_device(priv->rdev); >>>>> + if (ret) >>>>> + goto disable_rtc; >>>>> + >>>>> + return 0; >>>>> + >>>>> +disable_rtc: >>>>> + s32g_rtc_disable(priv); >>>>> + return ret; >>>>> +} >>>>> + >>>>> +static int s32g_rtc_suspend(struct device *dev) >>>>> +{ >>>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>>> + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); >>>>> + >>>>> + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, >>>>> priv->rtc_hz), >>>>> + &priv->sleep_sec)) { >>>>> + dev_warn(dev, "Overflow on sleep cycles occurred. >>>>> Resetting to 0.\n"); >>>>> + priv->sleep_sec = 0; >>>>> + } >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static int s32g_rtc_resume(struct device *dev) >>>>> +{ >>>>> + struct rtc_priv *priv = dev_get_drvdata(dev); >>>>> + >>>>> + /* The transition from resume to run is a reset event. >>>>> + * This leads to the RTC registers being reset after resume >>>>> from >>>>> + * suspend. It is uncommon, but this behaviour has been >>>>> observed >>>>> + * on S32G RTC after issuing a Suspend to RAM operation. >>>>> + * Thus, reconfigure RTC registers on the resume path. >>>>> + */ >>>>> + return rtc_clk_src_setup(priv); >>>>> +} >>>>> + >>>>> +static const struct of_device_id rtc_dt_ids[] = { >>>>> + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, >>>>> + { /* sentinel */ }, >>>>> +}; >>>>> + >>>>> +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, >>>>> + s32g_rtc_suspend, s32g_rtc_resume); >>>>> + >>>>> +static struct platform_driver s32g_rtc_driver = { >>>>> + .driver = { >>>>> + .name = "s32g-rtc", >>>>> + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), >>>>> + .of_match_table = rtc_dt_ids, >>>>> + }, >>>>> + .probe = s32g_rtc_probe, >>>>> +}; >>>>> +module_platform_driver(s32g_rtc_driver); >>>>> + >>>>> +MODULE_AUTHOR("NXP"); >>>>> +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); >>>>> +MODULE_LICENSE("GPL"); >>>>> -- >>>>> 2.45.2 >>>>> >>>> >>> >> > ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-03 10:33 [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Ciprian Costea 2025-04-03 10:33 ` [PATCH v9 1/4] dt-bindings: rtc: add schema for NXP " Ciprian Costea 2025-04-03 10:33 ` [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support Ciprian Costea @ 2025-04-03 10:33 ` Ciprian Costea 2025-04-04 6:17 ` Krzysztof Kozlowski 2025-04-03 10:33 ` [PATCH v9 4/4] MAINTAINERS: add NXP S32G RTC driver Ciprian Costea 2025-05-25 21:33 ` (subset) [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Alexandre Belloni 4 siblings, 1 reply; 19+ messages in thread From: Ciprian Costea @ 2025-04-03 10:33 UTC (permalink / raw) To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet, Ciprian Marian Costea From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> The RTC hardware module present on S32G based SoCs tracks clock time during system suspend and it is used as a wakeup source on S32G2/S32G3 architecture. Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index cb7da4415599..c24b88b0206e 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1227,6 +1227,7 @@ CONFIG_RTC_DRV_DA9063=m CONFIG_RTC_DRV_EFI=y CONFIG_RTC_DRV_CROS_EC=y CONFIG_RTC_DRV_FSL_FTM_ALARM=m +CONFIG_RTC_DRV_S32G=m CONFIG_RTC_DRV_S3C=y CONFIG_RTC_DRV_PL031=y CONFIG_RTC_DRV_SUN6I=y -- 2.45.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-03 10:33 ` [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support Ciprian Costea @ 2025-04-04 6:17 ` Krzysztof Kozlowski 2025-04-04 6:24 ` Ciprian Marian Costea 0 siblings, 1 reply; 19+ messages in thread From: Krzysztof Kozlowski @ 2025-04-04 6:17 UTC (permalink / raw) To: Ciprian Costea, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On 03/04/2025 12:33, Ciprian Costea wrote: > From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > > The RTC hardware module present on S32G based SoCs tracks clock time > during system suspend and it is used as a wakeup source on S32G2/S32G3 > architecture. Which boards are using it? I don't see any DTS (nowhere), so I do not see single reason for this patch. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-04 6:17 ` Krzysztof Kozlowski @ 2025-04-04 6:24 ` Ciprian Marian Costea 2025-04-04 7:15 ` Krzysztof Kozlowski 0 siblings, 1 reply; 19+ messages in thread From: Ciprian Marian Costea @ 2025-04-04 6:24 UTC (permalink / raw) To: Krzysztof Kozlowski, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On 4/4/2025 9:17 AM, Krzysztof Kozlowski wrote: > On 03/04/2025 12:33, Ciprian Costea wrote: >> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >> >> The RTC hardware module present on S32G based SoCs tracks clock time >> during system suspend and it is used as a wakeup source on S32G2/S32G3 >> architecture. > Which boards are using it? I don't see any DTS (nowhere), so I do not > see single reason for this patch. > > Best regards, > Krzysztof The RTC module is used by all the currently supported S32G2/S32G3 boards, so currently they are: S32G274A-EVB, S32G274A-RDB2, S32G399A-RDB3. I do see your point in the fact that this driver should be enabled as module only after platforms are actually using it. So, would it be better for me to send a V10 in this series with the DTS patch added ? Best Regards, Ciprian ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-04 6:24 ` Ciprian Marian Costea @ 2025-04-04 7:15 ` Krzysztof Kozlowski 2025-04-04 7:19 ` Ciprian Marian Costea 0 siblings, 1 reply; 19+ messages in thread From: Krzysztof Kozlowski @ 2025-04-04 7:15 UTC (permalink / raw) To: Ciprian Marian Costea, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On 04/04/2025 08:24, Ciprian Marian Costea wrote: > On 4/4/2025 9:17 AM, Krzysztof Kozlowski wrote: >> On 03/04/2025 12:33, Ciprian Costea wrote: >>> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >>> >>> The RTC hardware module present on S32G based SoCs tracks clock time >>> during system suspend and it is used as a wakeup source on S32G2/S32G3 >>> architecture. >> Which boards are using it? I don't see any DTS (nowhere), so I do not >> see single reason for this patch. >> >> Best regards, >> Krzysztof > > The RTC module is used by all the currently supported S32G2/S32G3 > boards, so currently they are: S32G274A-EVB, S32G274A-RDB2, S32G399A-RDB3. I don't think so. I looked at these DTS and there is no RTC. > I do see your point in the fact that this driver should be enabled as > module only after platforms are actually using it. No, post the user. I don't see the point of sending defconfig patch with RTC patchset anyway. That's different subsystem. > > So, would it be better for me to send a V10 in this series with the DTS > patch added ? No, separate patchsets. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-04 7:15 ` Krzysztof Kozlowski @ 2025-04-04 7:19 ` Ciprian Marian Costea 2025-04-04 8:53 ` Alexandre Belloni 0 siblings, 1 reply; 19+ messages in thread From: Ciprian Marian Costea @ 2025-04-04 7:19 UTC (permalink / raw) To: Krzysztof Kozlowski, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On 4/4/2025 10:15 AM, Krzysztof Kozlowski wrote: > On 04/04/2025 08:24, Ciprian Marian Costea wrote: >> On 4/4/2025 9:17 AM, Krzysztof Kozlowski wrote: >>> On 03/04/2025 12:33, Ciprian Costea wrote: >>>> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >>>> >>>> The RTC hardware module present on S32G based SoCs tracks clock time >>>> during system suspend and it is used as a wakeup source on S32G2/S32G3 >>>> architecture. >>> Which boards are using it? I don't see any DTS (nowhere), so I do not >>> see single reason for this patch. >>> >>> Best regards, >>> Krzysztof >> >> The RTC module is used by all the currently supported S32G2/S32G3 >> boards, so currently they are: S32G274A-EVB, S32G274A-RDB2, S32G399A-RDB3. > > I don't think so. I looked at these DTS and there is no RTC. > >> I do see your point in the fact that this driver should be enabled as >> module only after platforms are actually using it. > > No, post the user. I don't see the point of sending defconfig patch with > RTC patchset anyway. That's different subsystem. > >> >> So, would it be better for me to send a V10 in this series with the DTS >> patch added ? > > No, separate patchsets. Ok. I will send out a V10 in which I will drop this current patch from the patchset. Also, I will send the DTS patch which adds S32G274A-EVB, S32G274-RDB2 and S32G399A-RDB3 usage of the RTC after this patchset gets accepted. Best Regards, Ciprian > > Best regards, > Krzysztof ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-04 7:19 ` Ciprian Marian Costea @ 2025-04-04 8:53 ` Alexandre Belloni 2025-04-04 11:36 ` Ciprian Marian Costea 2025-05-20 9:01 ` Ciprian Marian Costea 0 siblings, 2 replies; 19+ messages in thread From: Alexandre Belloni @ 2025-04-04 8:53 UTC (permalink / raw) To: Ciprian Marian Costea Cc: Krzysztof Kozlowski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On 04/04/2025 10:19:56+0300, Ciprian Marian Costea wrote: > On 4/4/2025 10:15 AM, Krzysztof Kozlowski wrote: > > On 04/04/2025 08:24, Ciprian Marian Costea wrote: > > > On 4/4/2025 9:17 AM, Krzysztof Kozlowski wrote: > > > > On 03/04/2025 12:33, Ciprian Costea wrote: > > > > > From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > > > > > > > > > > The RTC hardware module present on S32G based SoCs tracks clock time > > > > > during system suspend and it is used as a wakeup source on S32G2/S32G3 > > > > > architecture. > > > > Which boards are using it? I don't see any DTS (nowhere), so I do not > > > > see single reason for this patch. > > > > > > > > Best regards, > > > > Krzysztof > > > > > > The RTC module is used by all the currently supported S32G2/S32G3 > > > boards, so currently they are: S32G274A-EVB, S32G274A-RDB2, S32G399A-RDB3. > > > > I don't think so. I looked at these DTS and there is no RTC. > > > > > I do see your point in the fact that this driver should be enabled as > > > module only after platforms are actually using it. > > > > No, post the user. I don't see the point of sending defconfig patch with > > RTC patchset anyway. That's different subsystem. > > > > > > > > So, would it be better for me to send a V10 in this series with the DTS > > > patch added ? > > > > No, separate patchsets. > > Ok. I will send out a V10 in which I will drop this current patch from the > patchset. Also, I will send the DTS patch which adds S32G274A-EVB, > S32G274-RDB2 and S32G399A-RDB3 usage of the RTC after this patchset gets > accepted. > I don't need V10, I can apply V9 without 3/4 and 4/4 > Best Regards, > Ciprian > > > > > Best regards, > > Krzysztof > -- Alexandre Belloni, co-owner and COO, Bootlin Embedded Linux and Kernel engineering https://bootlin.com ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-04 8:53 ` Alexandre Belloni @ 2025-04-04 11:36 ` Ciprian Marian Costea 2025-05-20 9:01 ` Ciprian Marian Costea 1 sibling, 0 replies; 19+ messages in thread From: Ciprian Marian Costea @ 2025-04-04 11:36 UTC (permalink / raw) To: Alexandre Belloni Cc: Krzysztof Kozlowski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On 4/4/2025 11:53 AM, Alexandre Belloni wrote: > On 04/04/2025 10:19:56+0300, Ciprian Marian Costea wrote: >> On 4/4/2025 10:15 AM, Krzysztof Kozlowski wrote: >>> On 04/04/2025 08:24, Ciprian Marian Costea wrote: >>>> On 4/4/2025 9:17 AM, Krzysztof Kozlowski wrote: >>>>> On 03/04/2025 12:33, Ciprian Costea wrote: >>>>>> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >>>>>> >>>>>> The RTC hardware module present on S32G based SoCs tracks clock time >>>>>> during system suspend and it is used as a wakeup source on S32G2/S32G3 >>>>>> architecture. >>>>> Which boards are using it? I don't see any DTS (nowhere), so I do not >>>>> see single reason for this patch. >>>>> >>>>> Best regards, >>>>> Krzysztof >>>> >>>> The RTC module is used by all the currently supported S32G2/S32G3 >>>> boards, so currently they are: S32G274A-EVB, S32G274A-RDB2, S32G399A-RDB3. >>> >>> I don't think so. I looked at these DTS and there is no RTC. >>> >>>> I do see your point in the fact that this driver should be enabled as >>>> module only after platforms are actually using it. >>> >>> No, post the user. I don't see the point of sending defconfig patch with >>> RTC patchset anyway. That's different subsystem. >>> >>>> >>>> So, would it be better for me to send a V10 in this series with the DTS >>>> patch added ? >>> >>> No, separate patchsets. >> >> Ok. I will send out a V10 in which I will drop this current patch from the >> patchset. Also, I will send the DTS patch which adds S32G274A-EVB, >> S32G274-RDB2 and S32G399A-RDB3 usage of the RTC after this patchset gets >> accepted. >> > > I don't need V10, I can apply V9 without 3/4 and 4/4 > Ok. Thanks for letting me know. Ciprian >> Best Regards, >> Ciprian >> >>> >>> Best regards, >>> Krzysztof >> > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support 2025-04-04 8:53 ` Alexandre Belloni 2025-04-04 11:36 ` Ciprian Marian Costea @ 2025-05-20 9:01 ` Ciprian Marian Costea 1 sibling, 0 replies; 19+ messages in thread From: Ciprian Marian Costea @ 2025-05-20 9:01 UTC (permalink / raw) To: Alexandre Belloni Cc: Krzysztof Kozlowski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On 4/4/2025 11:53 AM, Alexandre Belloni wrote: [ ... ] >> >> Ok. I will send out a V10 in which I will drop this current patch from the >> patchset. Also, I will send the DTS patch which adds S32G274A-EVB, >> S32G274-RDB2 and S32G399A-RDB3 usage of the RTC after this patchset gets >> accepted. >> > > I don't need V10, I can apply V9 without 3/4 and 4/4 > >> Best Regards, >> Ciprian >> Hello Alexandre, Sorry for bothering. I am writing to ask about the status of this patchset since it has been hanging for a while. Are there any more changes required from your point of view ? Regards, Ciprian ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v9 4/4] MAINTAINERS: add NXP S32G RTC driver 2025-04-03 10:33 [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Ciprian Costea ` (2 preceding siblings ...) 2025-04-03 10:33 ` [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support Ciprian Costea @ 2025-04-03 10:33 ` Ciprian Costea 2025-05-25 21:33 ` (subset) [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Alexandre Belloni 4 siblings, 0 replies; 19+ messages in thread From: Ciprian Costea @ 2025-04-03 10:33 UTC (permalink / raw) To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet, Ciprian Marian Costea From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> Add the NXP S32G RTC driver as maintained so further patches on this driver can be reviewed under this architecture. Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 873aa2cce4d7..515ed8115922 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2873,8 +2873,10 @@ R: Ghennadi Procopciuc <ghennadi.procopciuc@oss.nxp.com> R: NXP S32 Linux Team <s32@nxp.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml F: arch/arm64/boot/dts/freescale/s32g*.dts* F: drivers/pinctrl/nxp/ +F: drivers/rtc/rtc-s32g.c ARM/NXP S32G/S32R DWMAC ETHERNET DRIVER M: Jan Petrous <jan.petrous@oss.nxp.com> -- 2.45.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: (subset) [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs 2025-04-03 10:33 [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Ciprian Costea ` (3 preceding siblings ...) 2025-04-03 10:33 ` [PATCH v9 4/4] MAINTAINERS: add NXP S32G RTC driver Ciprian Costea @ 2025-05-25 21:33 ` Alexandre Belloni 4 siblings, 0 replies; 19+ messages in thread From: Alexandre Belloni @ 2025-05-25 21:33 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon, Ciprian Costea Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel, NXP S32 Linux, imx, Christophe Lizzi, Alberto Ruiz, Enric Balletbo, Eric Chanudet On Thu, 03 Apr 2025 13:33:42 +0300, Ciprian Costea wrote: > From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > > This patch series add support for the NXP RTC hardware module present on > S32G2/S32G3 SoCs. > > RTC tracks clock time during system suspend. It is used as a time-based > wakeup source for the S32G2/S32G3 SoCs. > > [...] Applied, thanks! [1/4] dt-bindings: rtc: add schema for NXP S32G2/S32G3 SoCs https://git.kernel.org/abelloni/c/8568d8b4daec [2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support https://git.kernel.org/abelloni/c/818b6709e1cb Best regards, -- Alexandre Belloni, co-owner and COO, Bootlin Embedded Linux and Kernel engineering https://bootlin.com ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2025-05-25 21:36 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-04-03 10:33 [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Ciprian Costea 2025-04-03 10:33 ` [PATCH v9 1/4] dt-bindings: rtc: add schema for NXP " Ciprian Costea 2025-04-03 10:33 ` [PATCH v9 2/4] rtc: s32g: add NXP S32G2/S32G3 SoC support Ciprian Costea 2025-04-09 14:14 ` Enric Balletbo i Serra 2025-04-09 14:26 ` Ciprian Marian Costea 2025-04-09 14:42 ` Enric Balletbo i Serra 2025-04-09 14:58 ` Ciprian Marian Costea 2025-04-10 7:08 ` Enric Balletbo i Serra 2025-04-11 8:09 ` Ciprian Marian Costea 2025-04-03 10:33 ` [PATCH v9 3/4] arm64: defconfig: add S32G RTC module support Ciprian Costea 2025-04-04 6:17 ` Krzysztof Kozlowski 2025-04-04 6:24 ` Ciprian Marian Costea 2025-04-04 7:15 ` Krzysztof Kozlowski 2025-04-04 7:19 ` Ciprian Marian Costea 2025-04-04 8:53 ` Alexandre Belloni 2025-04-04 11:36 ` Ciprian Marian Costea 2025-05-20 9:01 ` Ciprian Marian Costea 2025-04-03 10:33 ` [PATCH v9 4/4] MAINTAINERS: add NXP S32G RTC driver Ciprian Costea 2025-05-25 21:33 ` (subset) [PATCH v9 0/4] add NXP RTC driver support for S32G2/S32G3 SoCs Alexandre Belloni
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).