* [PATCH 1/5] dt-bindings: mfd: add bindings for NVIDIA VRS PSEQ
2025-04-16 12:06 [PATCH 0/5] Add NVIDIA VRS PSEQ support Shubhi Garg
@ 2025-04-16 12:06 ` Shubhi Garg
2025-04-21 22:02 ` Rob Herring
2025-04-16 12:06 ` [PATCH 2/5] arm64: tegra: Add device-tree node for NVVRS PSEQ Shubhi Garg
` (3 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Shubhi Garg @ 2025-04-16 12:06 UTC (permalink / raw)
To: lee, alexandre.belloni, thierry.reding, jonathanh, devicetree,
linux-kernel, linux-tegra
Cc: shgarg
Add bindings for NVIDIA VRS (Voltage Regulator Specification) power
sequencer device. NVIDIA VRS PSEQ controls ON/OFF and suspend/resume
power sequencing of system power rails on Tegra234 SoC. This device
also provides 32kHz RTC support with backup battery for system timing.
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
.../bindings/mfd/nvidia,vrs-pseq.yaml | 61 +++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
diff --git a/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
new file mode 100644
index 000000000000..d4c5984930e9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/nvidia,vrs-pseq.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Voltage Regulator Specification Power Sequencer
+
+maintainers:
+ - Shubhi Garg <shgarg@nvidia.com>
+
+description:
+ NVIDIA Voltage Regulator Specification Power Sequencer device controls ON/OFF
+ and suspend/resume power sequencing of system power rails for NVIDIA
+ SoCs. It provides 32kHz RTC clock support with backup battery for
+ system timing.
+
+properties:
+ compatible:
+ const: nvidia,vrs-pseq
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 2
+ description:
+ The first cell is the IRQ number, the second cell is the trigger type.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-controller
+ - "#interrupt-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ vrs@3c {
+ compatible = "nvidia,vrs-pseq";
+ reg = <0x3c>;
+ interrupt-parent = <&pmc>;
+ interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+ };
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] dt-bindings: mfd: add bindings for NVIDIA VRS PSEQ
2025-04-16 12:06 ` [PATCH 1/5] dt-bindings: mfd: add bindings for NVIDIA VRS PSEQ Shubhi Garg
@ 2025-04-21 22:02 ` Rob Herring
2025-04-21 22:03 ` Rob Herring
0 siblings, 1 reply; 12+ messages in thread
From: Rob Herring @ 2025-04-21 22:02 UTC (permalink / raw)
To: Shubhi Garg
Cc: lee, alexandre.belloni, thierry.reding, jonathanh, devicetree,
linux-kernel, linux-tegra
On Wed, Apr 16, 2025 at 12:06:15PM +0000, Shubhi Garg wrote:
> Add bindings for NVIDIA VRS (Voltage Regulator Specification) power
> sequencer device. NVIDIA VRS PSEQ controls ON/OFF and suspend/resume
> power sequencing of system power rails on Tegra234 SoC. This device
> also provides 32kHz RTC support with backup battery for system timing.
>
> Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
> ---
> .../bindings/mfd/nvidia,vrs-pseq.yaml | 61 +++++++++++++++++++
> 1 file changed, 61 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
>
> diff --git a/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
> new file mode 100644
> index 000000000000..d4c5984930e9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
> @@ -0,0 +1,61 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
First I've seen this...
According to this[1], you shouldn't have 'Copyright (c)'. But better
check with your lawyers.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/nvidia,vrs-pseq.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NVIDIA Voltage Regulator Specification Power Sequencer
> +
> +maintainers:
> + - Shubhi Garg <shgarg@nvidia.com>
> +
> +description:
> + NVIDIA Voltage Regulator Specification Power Sequencer device controls ON/OFF
Wrap at 80 char.
> + and suspend/resume power sequencing of system power rails for NVIDIA
> + SoCs. It provides 32kHz RTC clock support with backup battery for
> + system timing.
Nothing in this description indicates it's also an interrupt controller.
> +
> +properties:
> + compatible:
> + const: nvidia,vrs-pseq
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + interrupt-controller: true
> +
> + '#interrupt-cells':
> + const: 2
> + description:
> + The first cell is the IRQ number, the second cell is the trigger type.
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - interrupt-controller
> + - "#interrupt-cells"
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/irq.h>
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + vrs@3c {
> + compatible = "nvidia,vrs-pseq";
> + reg = <0x3c>;
> + interrupt-parent = <&pmc>;
> + interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + status = "disabled";
Examples should be enabled or removed.
> + };
> + };
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] dt-bindings: mfd: add bindings for NVIDIA VRS PSEQ
2025-04-21 22:02 ` Rob Herring
@ 2025-04-21 22:03 ` Rob Herring
0 siblings, 0 replies; 12+ messages in thread
From: Rob Herring @ 2025-04-21 22:03 UTC (permalink / raw)
To: Shubhi Garg
Cc: lee, alexandre.belloni, thierry.reding, jonathanh, devicetree,
linux-kernel, linux-tegra
On Mon, Apr 21, 2025 at 5:02 PM Rob Herring <robh@kernel.org> wrote:
>
> On Wed, Apr 16, 2025 at 12:06:15PM +0000, Shubhi Garg wrote:
> > Add bindings for NVIDIA VRS (Voltage Regulator Specification) power
> > sequencer device. NVIDIA VRS PSEQ controls ON/OFF and suspend/resume
> > power sequencing of system power rails on Tegra234 SoC. This device
> > also provides 32kHz RTC support with backup battery for system timing.
> >
> > Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
> > ---
> > .../bindings/mfd/nvidia,vrs-pseq.yaml | 61 +++++++++++++++++++
> > 1 file changed, 61 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
> > new file mode 100644
> > index 000000000000..d4c5984930e9
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
> > @@ -0,0 +1,61 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
>
> First I've seen this...
>
> According to this[1], you shouldn't have 'Copyright (c)'. But better
> check with your lawyers.
With the link now:
[1] https://reuse.software/faq/
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/5] arm64: tegra: Add device-tree node for NVVRS PSEQ
2025-04-16 12:06 [PATCH 0/5] Add NVIDIA VRS PSEQ support Shubhi Garg
2025-04-16 12:06 ` [PATCH 1/5] dt-bindings: mfd: add bindings for NVIDIA VRS PSEQ Shubhi Garg
@ 2025-04-16 12:06 ` Shubhi Garg
2025-04-17 5:25 ` Krzysztof Kozlowski
2025-04-16 12:06 ` [PATCH 3/5] mfd: nvvrs: add NVVRS PSEQ MFD driver Shubhi Garg
` (2 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Shubhi Garg @ 2025-04-16 12:06 UTC (permalink / raw)
To: lee, alexandre.belloni, thierry.reding, jonathanh, devicetree,
linux-kernel, linux-tegra
Cc: shgarg
Add NVIDIA VRS Power Sequencer device tree node for Tegra234 P3701 and
P3767 platforms.
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi | 11 +++++++++++
arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi | 17 +++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
index 9086a0d010e5..d3c1f13b2b99 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
@@ -170,6 +170,17 @@ bpmp {
i2c {
status = "okay";
+ vrs@3c {
+ compatible = "nvidia,vrs-pseq";
+ reg = <0x3c>;
+ interrupt-parent = <&pmc>;
+ /* VRS Wake ID is 24 */
+ interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "okay";
+ };
+
thermal-sensor@4c {
compatible = "ti,tmp451";
status = "okay";
diff --git a/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi b/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi
index 84db7132e8fc..93a787c57eba 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi
@@ -121,6 +121,23 @@ pmc@c360000 {
};
};
+ bpmp {
+ i2c {
+ status = "okay";
+
+ vrs@3c {
+ compatible = "nvidia,vrs-pseq";
+ reg = <0x3c>;
+ interrupt-parent = <&pmc>;
+ /* VRS Wake ID is 24 */
+ interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "okay";
+ };
+ };
+ };
+
vdd_5v0_sys: regulator-vdd-5v0-sys {
compatible = "regulator-fixed";
regulator-name = "VDD_5V0_SYS";
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] arm64: tegra: Add device-tree node for NVVRS PSEQ
2025-04-16 12:06 ` [PATCH 2/5] arm64: tegra: Add device-tree node for NVVRS PSEQ Shubhi Garg
@ 2025-04-17 5:25 ` Krzysztof Kozlowski
0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-04-17 5:25 UTC (permalink / raw)
To: Shubhi Garg, lee, alexandre.belloni, thierry.reding, jonathanh,
devicetree, linux-kernel, linux-tegra
On 16/04/2025 14:06, Shubhi Garg wrote:
> Add NVIDIA VRS Power Sequencer device tree node for Tegra234 P3701 and
> P3767 platforms.
>
> Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
> ---
> arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi | 11 +++++++++++
> arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi | 17 +++++++++++++++++
> 2 files changed, 28 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
> index 9086a0d010e5..d3c1f13b2b99 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
> @@ -170,6 +170,17 @@ bpmp {
> i2c {
> status = "okay";
>
> + vrs@3c {
> + compatible = "nvidia,vrs-pseq";
> + reg = <0x3c>;
> + interrupt-parent = <&pmc>;
> + /* VRS Wake ID is 24 */
> + interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + status = "okay";
Where was it disabled?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/5] mfd: nvvrs: add NVVRS PSEQ MFD driver
2025-04-16 12:06 [PATCH 0/5] Add NVIDIA VRS PSEQ support Shubhi Garg
2025-04-16 12:06 ` [PATCH 1/5] dt-bindings: mfd: add bindings for NVIDIA VRS PSEQ Shubhi Garg
2025-04-16 12:06 ` [PATCH 2/5] arm64: tegra: Add device-tree node for NVVRS PSEQ Shubhi Garg
@ 2025-04-16 12:06 ` Shubhi Garg
2025-04-17 5:26 ` Krzysztof Kozlowski
2025-04-16 12:06 ` [PATCH 4/5] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver Shubhi Garg
2025-04-16 12:06 ` [PATCH 5/5] arm64: defconfig: enable NVIDIA VRS PSEQ Shubhi Garg
4 siblings, 1 reply; 12+ messages in thread
From: Shubhi Garg @ 2025-04-16 12:06 UTC (permalink / raw)
To: lee, alexandre.belloni, thierry.reding, jonathanh, devicetree,
linux-kernel, linux-tegra
Cc: shgarg
Add support for NVIDIA VRS (Voltage Regulator Specification) power
sequencer device driver. This driver manages ON/OFF and suspend/resume
power sequencing of system power rails for NVIDIA Tegra234 SoC. It also
provides 32kHz RTC clock support with backup battery for system timing.
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
drivers/mfd/Kconfig | 12 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/nvidia-vrs-pseq.c | 279 ++++++++++++++++++++++++++++
include/linux/mfd/nvidia-vrs-pseq.h | 127 +++++++++++++
4 files changed, 419 insertions(+)
create mode 100644 drivers/mfd/nvidia-vrs-pseq.c
create mode 100644 include/linux/mfd/nvidia-vrs-pseq.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 22b936310039..b7357ff398f2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1396,6 +1396,18 @@ config MFD_SC27XX_PMIC
This driver provides common support for accessing the SC27xx PMICs,
and it also adds the irq_chip parts for handling the PMIC chip events.
+config MFD_NVVRS_PSEQ
+ tristate "NVIDIA Voltage Regulator Specification Power Sequencer"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say Y here to add support for NVIDIA Voltage Regulator Specification
+ Power Sequencer. NVVRS_PSEQ supports ON/OFF, suspend/resume sequence of
+ system power rails. It provides 32kHz RTC clock support with backup
+ battery for system timing.
+
config RZ_MTU3
tristate "Renesas RZ/G2L MTU3a core driver"
depends on (ARCH_RZG2L && OF) || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 948cbdf42a18..93bb2c34e341 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -182,6 +182,7 @@ obj-$(CONFIG_MFD_MT6360) += mt6360-core.o
obj-$(CONFIG_MFD_MT6370) += mt6370.o
mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o
obj-$(CONFIG_MFD_MT6397) += mt6397.o
+obj-$(CONFIG_MFD_NVVRS_PSEQ) += nvidia-vrs-pseq.o
obj-$(CONFIG_RZ_MTU3) += rz-mtu3.o
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
diff --git a/drivers/mfd/nvidia-vrs-pseq.c b/drivers/mfd/nvidia-vrs-pseq.c
new file mode 100644
index 000000000000..c93730168dcb
--- /dev/null
+++ b/drivers/mfd/nvidia-vrs-pseq.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// NVIDIA VRS Power Sequencer driver.
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/nvidia-vrs-pseq.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+static const struct resource rtc_resources[] = {
+ DEFINE_RES_IRQ(NVVRS_PSEQ_INT_SRC1_RTC),
+};
+
+static const struct regmap_irq nvvrs_pseq_irqs[] = {
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_RSTIRQ, 0, NVVRS_PSEQ_INT_SRC1_RSTIRQ_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_OSC, 0, NVVRS_PSEQ_INT_SRC1_OSC_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_EN, 0, NVVRS_PSEQ_INT_SRC1_EN_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_RTC, 0, NVVRS_PSEQ_INT_SRC1_RTC_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_PEC, 0, NVVRS_PSEQ_INT_SRC1_PEC_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_WDT, 0, NVVRS_PSEQ_INT_SRC1_WDT_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_EM_PD, 0, NVVRS_PSEQ_INT_SRC1_EM_PD_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC1_INTERNAL, 0, NVVRS_PSEQ_INT_SRC1_INTERNAL_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC2_PBSP, 1, NVVRS_PSEQ_INT_SRC2_PBSP_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC2_ECC_DED, 1, NVVRS_PSEQ_INT_SRC2_ECC_DED_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC2_TSD, 1, NVVRS_PSEQ_INT_SRC2_TSD_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC2_LDO, 1, NVVRS_PSEQ_INT_SRC2_LDO_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC2_BIST, 1, NVVRS_PSEQ_INT_SRC2_BIST_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC2_RT_CRC, 1, NVVRS_PSEQ_INT_SRC2_RT_CRC_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_SRC2_VENDOR, 1, NVVRS_PSEQ_INT_SRC2_VENDOR_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR0, 2, NVVRS_PSEQ_INT_VENDOR0_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR1, 2, NVVRS_PSEQ_INT_VENDOR1_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR2, 2, NVVRS_PSEQ_INT_VENDOR2_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR3, 2, NVVRS_PSEQ_INT_VENDOR3_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR4, 2, NVVRS_PSEQ_INT_VENDOR4_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR5, 2, NVVRS_PSEQ_INT_VENDOR5_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR6, 2, NVVRS_PSEQ_INT_VENDOR6_MASK),
+ REGMAP_IRQ_REG(NVVRS_PSEQ_INT_VENDOR7, 2, NVVRS_PSEQ_INT_VENDOR7_MASK),
+};
+
+static const struct mfd_cell nvvrs_pseq_children[] = {
+ {
+ .name = "nvvrs-pseq-rtc",
+ .resources = rtc_resources,
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ },
+};
+
+static const struct regmap_range nvvrs_pseq_readable_ranges[] = {
+ regmap_reg_range(NVVRS_PSEQ_REG_VENDOR_ID, NVVRS_PSEQ_REG_MODEL_REV),
+ regmap_reg_range(NVVRS_PSEQ_REG_INT_SRC1, NVVRS_PSEQ_REG_LAST_RST),
+ regmap_reg_range(NVVRS_PSEQ_REG_EN_ALT_F, NVVRS_PSEQ_REG_IEN_VENDOR),
+ regmap_reg_range(NVVRS_PSEQ_REG_RTC_T3, NVVRS_PSEQ_REG_RTC_A0),
+ regmap_reg_range(NVVRS_PSEQ_REG_WDT_CFG, NVVRS_PSEQ_REG_WDTKEY),
+};
+
+static const struct regmap_access_table nvvrs_pseq_readable_table = {
+ .yes_ranges = nvvrs_pseq_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(nvvrs_pseq_readable_ranges),
+};
+
+static const struct regmap_range nvvrs_pseq_writable_ranges[] = {
+ regmap_reg_range(NVVRS_PSEQ_REG_INT_SRC1, NVVRS_PSEQ_REG_INT_VENDOR),
+ regmap_reg_range(NVVRS_PSEQ_REG_GP_OUT, NVVRS_PSEQ_REG_IEN_VENDOR),
+ regmap_reg_range(NVVRS_PSEQ_REG_RTC_T3, NVVRS_PSEQ_REG_RTC_A0),
+ regmap_reg_range(NVVRS_PSEQ_REG_WDT_CFG, NVVRS_PSEQ_REG_WDTKEY),
+};
+
+static const struct regmap_access_table nvvrs_pseq_writable_table = {
+ .yes_ranges = nvvrs_pseq_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(nvvrs_pseq_writable_ranges),
+};
+
+static const struct regmap_config nvvrs_pseq_regmap_config = {
+ .name = "nvvrs-pseq",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = NVVRS_PSEQ_REG_WDTKEY + 1,
+ .cache_type = REGCACHE_RBTREE,
+ .rd_table = &nvvrs_pseq_readable_table,
+ .wr_table = &nvvrs_pseq_writable_table,
+};
+
+static int nvvrs_pseq_irq_clear(void *irq_drv_data)
+{
+ struct nvvrs_pseq_chip *chip = (struct nvvrs_pseq_chip *)irq_drv_data;
+ struct i2c_client *client = chip->client;
+ u8 reg, val;
+ unsigned int i;
+ int ret = 0;
+
+ /* Write 1 to clear the interrupt bit in the Interrupt
+ * Source Register, writing 0 has no effect, writing 1 to a bit
+ * which is already at 0 has no effect
+ */
+
+ for (i = 0; i < chip->irq_chip->num_regs; i++) {
+ reg = (u8)(chip->irq_chip->status_base + i);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read interrupt register: 0x%02x, ret=%d\n",
+ reg, ret);
+ return ret;
+ } else if (ret > 0) {
+ val = (u8)ret;
+ dev_dbg(chip->dev, "Clearing interrupts! Interrupt status reg 0x%02x = 0x%02x\n",
+ reg, val);
+
+ /* Clear interrupt */
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to write interrupt register: 0x%02x, ret= %d\n",
+ reg, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct regmap_irq_chip nvvrs_pseq_irq_chip = {
+ .name = "nvvrs-pseq-irq",
+ .irqs = nvvrs_pseq_irqs,
+ .num_irqs = ARRAY_SIZE(nvvrs_pseq_irqs),
+ .num_regs = 3,
+ .status_base = NVVRS_PSEQ_REG_INT_SRC1,
+ .handle_post_irq = nvvrs_pseq_irq_clear,
+};
+
+static int nvvrs_pseq_vendor_info(struct nvvrs_pseq_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ u8 vendor_id, model_rev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_VENDOR_ID);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read Vendor ID: %d\n", ret);
+ return ret;
+ }
+
+ vendor_id = (u8)ret;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_MODEL_REV);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read Model Rev: %d\n", ret);
+ return ret;
+ }
+
+ model_rev = (u8)ret;
+
+ if (model_rev < 0x40) {
+ dev_err(chip->dev, "Chip revision 0x%02x is not supported!\n",
+ model_rev);
+ return -ENODEV;
+ }
+
+ dev_info(chip->dev, "NVVRS Vendor ID: 0x%02x, Model Rev: 0x%02x\n",
+ vendor_id, model_rev);
+
+ return 0;
+}
+
+static int nvvrs_pseq_probe(struct i2c_client *client)
+{
+ const struct regmap_config *rmap_config;
+ struct nvvrs_pseq_chip *nvvrs_chip;
+ const struct mfd_cell *mfd_cells;
+ int n_mfd_cells;
+ int ret;
+
+ nvvrs_chip = devm_kzalloc(&client->dev, sizeof(*nvvrs_chip), GFP_KERNEL);
+ if (!nvvrs_chip)
+ return -ENOMEM;
+
+ /* Set PEC flag for SMBUS transfer with PEC enabled */
+ client->flags |= I2C_CLIENT_PEC;
+
+ i2c_set_clientdata(client, nvvrs_chip);
+ nvvrs_chip->client = client;
+ nvvrs_chip->dev = &client->dev;
+ nvvrs_chip->chip_irq = client->irq;
+ mfd_cells = nvvrs_pseq_children;
+ n_mfd_cells = ARRAY_SIZE(nvvrs_pseq_children);
+ rmap_config = &nvvrs_pseq_regmap_config;
+ nvvrs_chip->irq_chip = &nvvrs_pseq_irq_chip;
+
+ nvvrs_chip->rmap = devm_regmap_init_i2c(client, rmap_config);
+ if (IS_ERR(nvvrs_chip->rmap)) {
+ ret = PTR_ERR(nvvrs_chip->rmap);
+ dev_err(nvvrs_chip->dev, "Failed to initialise regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = nvvrs_pseq_vendor_info(nvvrs_chip);
+ if (ret < 0)
+ return ret;
+
+ nvvrs_chip->irq_drv_data = nvvrs_chip;
+ ret = devm_regmap_add_irq_chip(nvvrs_chip->dev, nvvrs_chip->rmap, client->irq,
+ IRQF_ONESHOT | IRQF_SHARED, 0,
+ &nvvrs_pseq_irq_chip,
+ &nvvrs_chip->irq_data);
+ if (ret < 0) {
+ dev_err(nvvrs_chip->dev, "Failed to add regmap irq: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_mfd_add_devices(nvvrs_chip->dev, PLATFORM_DEVID_NONE,
+ mfd_cells, n_mfd_cells, NULL, 0,
+ regmap_irq_get_domain(nvvrs_chip->irq_data));
+ if (ret < 0) {
+ dev_err(nvvrs_chip->dev, "Failed to add MFD children: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(nvvrs_chip->dev, "NVVRS PSEQ probe successful");
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nvvrs_pseq_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ /*
+ * IRQ must be disabled during suspend because if it happens
+ * while suspended it will be handled before resuming I2C.
+ *
+ * When device is woken up from suspend (e.g. by RTC wake alarm),
+ * an interrupt occurs before resuming I2C bus controller.
+ * Interrupt handler tries to read registers but this read
+ * will fail because I2C is still suspended.
+ */
+ disable_irq(client->irq);
+
+ return 0;
+}
+
+static int nvvrs_pseq_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ enable_irq(client->irq);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops nvvrs_pseq_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(nvvrs_pseq_i2c_suspend, nvvrs_pseq_i2c_resume)
+};
+
+static const struct of_device_id nvvrs_dt_match[] = {
+ { .compatible = "nvidia,vrs-pseq" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nvvrs_dt_match);
+
+static struct i2c_driver nvvrs_pseq_driver = {
+ .driver = {
+ .name = "nvvrs_pseq",
+ .pm = &nvvrs_pseq_pm_ops,
+ .of_match_table = of_match_ptr(nvvrs_dt_match),
+ },
+ .probe = nvvrs_pseq_probe,
+};
+
+module_i2c_driver(nvvrs_pseq_driver);
+
+MODULE_AUTHOR("Shubhi Garg <shgarg@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Voltage Regulator Specification Power Sequencer Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/nvidia-vrs-pseq.h b/include/linux/mfd/nvidia-vrs-pseq.h
new file mode 100644
index 000000000000..7e6f3aa940e7
--- /dev/null
+++ b/include/linux/mfd/nvidia-vrs-pseq.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
+
+#ifndef _MFD_NVIDIA_VRS_PSEQ_H_
+#define _MFD_NVIDIA_VRS_PSEQ_H_
+
+#include <linux/types.h>
+
+/* Vendor ID */
+#define NVVRS_PSEQ_REG_VENDOR_ID 0x00
+#define NVVRS_PSEQ_REG_MODEL_REV 0x01
+
+/* Interrupts and Status registers */
+#define NVVRS_PSEQ_REG_INT_SRC1 0x10
+#define NVVRS_PSEQ_REG_INT_SRC2 0x11
+#define NVVRS_PSEQ_REG_INT_VENDOR 0x12
+#define NVVRS_PSEQ_REG_CTL_STAT 0x13
+#define NVVRS_PSEQ_REG_EN_STDR1 0x14
+#define NVVRS_PSEQ_REG_EN_STDR2 0x15
+#define NVVRS_PSEQ_REG_EN_STRD1 0x16
+#define NVVRS_PSEQ_REG_EN_STRD2 0x17
+#define NVVRS_PSEQ_REG_WDT_STAT 0x18
+#define NVVRS_PSEQ_REG_TEST_STAT 0x19
+#define NVVRS_PSEQ_REG_LAST_RST 0x1A
+
+/* Configuration Registers */
+#define NVVRS_PSEQ_REG_EN_ALT_F 0x20
+#define NVVRS_PSEQ_REG_AF_IN_OUT 0x21
+#define NVVRS_PSEQ_REG_EN_CFG1 0x22
+#define NVVRS_PSEQ_REG_EN_CFG2 0x23
+#define NVVRS_PSEQ_REG_CLK_CFG 0x24
+#define NVVRS_PSEQ_REG_GP_OUT 0x25
+#define NVVRS_PSEQ_REG_DEB_IN 0x26
+#define NVVRS_PSEQ_REG_LP_TTSHLD 0x27
+#define NVVRS_PSEQ_REG_CTL_1 0x28
+#define NVVRS_PSEQ_REG_CTL_2 0x29
+#define NVVRS_PSEQ_REG_TEST_CFG 0x2A
+#define NVVRS_PSEQ_REG_IEN_VENDOR 0x2B
+
+/* RTC */
+#define NVVRS_PSEQ_REG_RTC_T3 0x70
+#define NVVRS_PSEQ_REG_RTC_T2 0x71
+#define NVVRS_PSEQ_REG_RTC_T1 0x72
+#define NVVRS_PSEQ_REG_RTC_T0 0x73
+#define NVVRS_PSEQ_REG_RTC_A3 0x74
+#define NVVRS_PSEQ_REG_RTC_A2 0x75
+#define NVVRS_PSEQ_REG_RTC_A1 0x76
+#define NVVRS_PSEQ_REG_RTC_A0 0x77
+
+/* WDT */
+#define NVVRS_PSEQ_REG_WDT_CFG 0x80
+#define NVVRS_PSEQ_REG_WDT_CLOSE 0x81
+#define NVVRS_PSEQ_REG_WDT_OPEN 0x82
+#define NVVRS_PSEQ_REG_WDTKEY 0x83
+
+/* Interrupt Mask */
+#define NVVRS_PSEQ_INT_SRC1_RSTIRQ_MASK BIT(0)
+#define NVVRS_PSEQ_INT_SRC1_OSC_MASK BIT(1)
+#define NVVRS_PSEQ_INT_SRC1_EN_MASK BIT(2)
+#define NVVRS_PSEQ_INT_SRC1_RTC_MASK BIT(3)
+#define NVVRS_PSEQ_INT_SRC1_PEC_MASK BIT(4)
+#define NVVRS_PSEQ_INT_SRC1_WDT_MASK BIT(5)
+#define NVVRS_PSEQ_INT_SRC1_EM_PD_MASK BIT(6)
+#define NVVRS_PSEQ_INT_SRC1_INTERNAL_MASK BIT(7)
+#define NVVRS_PSEQ_INT_SRC2_PBSP_MASK BIT(0)
+#define NVVRS_PSEQ_INT_SRC2_ECC_DED_MASK BIT(1)
+#define NVVRS_PSEQ_INT_SRC2_TSD_MASK BIT(2)
+#define NVVRS_PSEQ_INT_SRC2_LDO_MASK BIT(3)
+#define NVVRS_PSEQ_INT_SRC2_BIST_MASK BIT(4)
+#define NVVRS_PSEQ_INT_SRC2_RT_CRC_MASK BIT(5)
+#define NVVRS_PSEQ_INT_SRC2_VENDOR_MASK BIT(7)
+#define NVVRS_PSEQ_INT_VENDOR0_MASK BIT(0)
+#define NVVRS_PSEQ_INT_VENDOR1_MASK BIT(1)
+#define NVVRS_PSEQ_INT_VENDOR2_MASK BIT(2)
+#define NVVRS_PSEQ_INT_VENDOR3_MASK BIT(3)
+#define NVVRS_PSEQ_INT_VENDOR4_MASK BIT(4)
+#define NVVRS_PSEQ_INT_VENDOR5_MASK BIT(5)
+#define NVVRS_PSEQ_INT_VENDOR6_MASK BIT(6)
+#define NVVRS_PSEQ_INT_VENDOR7_MASK BIT(7)
+
+/* Controller Register Mask */
+#define NVVRS_PSEQ_REG_CTL_1_FORCE_SHDN (BIT(0) | BIT(1))
+#define NVVRS_PSEQ_REG_CTL_1_FORCE_ACT BIT(2)
+#define NVVRS_PSEQ_REG_CTL_1_FORCE_INT BIT(3)
+#define NVVRS_PSEQ_REG_CTL_2_EN_PEC BIT(0)
+#define NVVRS_PSEQ_REG_CTL_2_REQ_PEC BIT(1)
+#define NVVRS_PSEQ_REG_CTL_2_RTC_PU BIT(2)
+#define NVVRS_PSEQ_REG_CTL_2_RTC_WAKE BIT(3)
+#define NVVRS_PSEQ_REG_CTL_2_RST_DLY 0xF0
+
+enum {
+ NVVRS_PSEQ_INT_SRC1_RSTIRQ, /* Reset or Interrupt Pin Fault */
+ NVVRS_PSEQ_INT_SRC1_OSC, /* Crystal Oscillator Fault */
+ NVVRS_PSEQ_INT_SRC1_EN, /* Enable Output Pin Fault */
+ NVVRS_PSEQ_INT_SRC1_RTC, /* RTC Alarm */
+ NVVRS_PSEQ_INT_SRC1_PEC, /* Packet Error Checking */
+ NVVRS_PSEQ_INT_SRC1_WDT, /* Watchdog Violation */
+ NVVRS_PSEQ_INT_SRC1_EM_PD, /* Emergency Power Down */
+ NVVRS_PSEQ_INT_SRC1_INTERNAL, /* Internal Fault*/
+ NVVRS_PSEQ_INT_SRC2_PBSP, /* PWR_BTN Short Pulse Detection */
+ NVVRS_PSEQ_INT_SRC2_ECC_DED, /* ECC Double-Error Detection */
+ NVVRS_PSEQ_INT_SRC2_TSD, /* Thermal Shutdown */
+ NVVRS_PSEQ_INT_SRC2_LDO, /* LDO Fault */
+ NVVRS_PSEQ_INT_SRC2_BIST, /* Built-In Self Test Fault */
+ NVVRS_PSEQ_INT_SRC2_RT_CRC, /* Runtime Register CRC Fault */
+ NVVRS_PSEQ_INT_SRC2_VENDOR, /* Vendor Specific Internal Fault */
+ NVVRS_PSEQ_INT_VENDOR0, /* Vendor Internal Fault Bit 0 */
+ NVVRS_PSEQ_INT_VENDOR1, /* Vendor Internal Fault Bit 1 */
+ NVVRS_PSEQ_INT_VENDOR2, /* Vendor Internal Fault Bit 2 */
+ NVVRS_PSEQ_INT_VENDOR3, /* Vendor Internal Fault Bit 3 */
+ NVVRS_PSEQ_INT_VENDOR4, /* Vendor Internal Fault Bit 4 */
+ NVVRS_PSEQ_INT_VENDOR5, /* Vendor Internal Fault Bit 5 */
+ NVVRS_PSEQ_INT_VENDOR6, /* Vendor Internal Fault Bit 6 */
+ NVVRS_PSEQ_INT_VENDOR7, /* Vendor Internal Fault Bit 7 */
+};
+
+struct nvvrs_pseq_chip {
+ struct device *dev;
+ struct regmap *rmap;
+ int chip_irq;
+ struct i2c_client *client;
+ struct regmap_irq_chip_data *irq_data;
+ const struct regmap_irq_chip *irq_chip;
+ void *irq_drv_data;
+};
+
+#endif /* _MFD_NVIDIA_VRS_PSEQ_H_ */
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 3/5] mfd: nvvrs: add NVVRS PSEQ MFD driver
2025-04-16 12:06 ` [PATCH 3/5] mfd: nvvrs: add NVVRS PSEQ MFD driver Shubhi Garg
@ 2025-04-17 5:26 ` Krzysztof Kozlowski
0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-04-17 5:26 UTC (permalink / raw)
To: Shubhi Garg, lee, alexandre.belloni, thierry.reding, jonathanh,
devicetree, linux-kernel, linux-tegra
On 16/04/2025 14:06, Shubhi Garg wrote:
> +
> + if (model_rev < 0x40) {
> + dev_err(chip->dev, "Chip revision 0x%02x is not supported!\n",
> + model_rev);
> + return -ENODEV;
> + }
> +
> + dev_info(chip->dev, "NVVRS Vendor ID: 0x%02x, Model Rev: 0x%02x\n",
> + vendor_id, model_rev);
dev_dbg
> +
> + return 0;
> +}
> +
> +static int nvvrs_pseq_probe(struct i2c_client *client)
> +{
> + const struct regmap_config *rmap_config;
> + struct nvvrs_pseq_chip *nvvrs_chip;
> + const struct mfd_cell *mfd_cells;
> + int n_mfd_cells;
> + int ret;
> +
> + nvvrs_chip = devm_kzalloc(&client->dev, sizeof(*nvvrs_chip), GFP_KERNEL);
> + if (!nvvrs_chip)
> + return -ENOMEM;
> +
> + /* Set PEC flag for SMBUS transfer with PEC enabled */
> + client->flags |= I2C_CLIENT_PEC;
> +
> + i2c_set_clientdata(client, nvvrs_chip);
> + nvvrs_chip->client = client;
> + nvvrs_chip->dev = &client->dev;
> + nvvrs_chip->chip_irq = client->irq;
> + mfd_cells = nvvrs_pseq_children;
> + n_mfd_cells = ARRAY_SIZE(nvvrs_pseq_children);
> + rmap_config = &nvvrs_pseq_regmap_config;
> + nvvrs_chip->irq_chip = &nvvrs_pseq_irq_chip;
> +
> + nvvrs_chip->rmap = devm_regmap_init_i2c(client, rmap_config);
> + if (IS_ERR(nvvrs_chip->rmap)) {
> + ret = PTR_ERR(nvvrs_chip->rmap);
> + dev_err(nvvrs_chip->dev, "Failed to initialise regmap: %d\n", ret);
> + return ret;
Syntax is: return dev_err_probe
> + }
> +
> + ret = nvvrs_pseq_vendor_info(nvvrs_chip);
> + if (ret < 0)
> + return ret;
> +
> + nvvrs_chip->irq_drv_data = nvvrs_chip;
> + ret = devm_regmap_add_irq_chip(nvvrs_chip->dev, nvvrs_chip->rmap, client->irq,
> + IRQF_ONESHOT | IRQF_SHARED, 0,
> + &nvvrs_pseq_irq_chip,
> + &nvvrs_chip->irq_data);
> + if (ret < 0) {
> + dev_err(nvvrs_chip->dev, "Failed to add regmap irq: %d\n", ret);
> + return ret;
> + }
> +
> + ret = devm_mfd_add_devices(nvvrs_chip->dev, PLATFORM_DEVID_NONE,
> + mfd_cells, n_mfd_cells, NULL, 0,
> + regmap_irq_get_domain(nvvrs_chip->irq_data));
> + if (ret < 0) {
> + dev_err(nvvrs_chip->dev, "Failed to add MFD children: %d\n", ret);
> + return ret;
> + }
> +
> + dev_info(nvvrs_chip->dev, "NVVRS PSEQ probe successful");
Drop, useless message.
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int nvvrs_pseq_i2c_suspend(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> +
> + /*
> + * IRQ must be disabled during suspend because if it happens
> + * while suspended it will be handled before resuming I2C.
> + *
> + * When device is woken up from suspend (e.g. by RTC wake alarm),
> + * an interrupt occurs before resuming I2C bus controller.
> + * Interrupt handler tries to read registers but this read
> + * will fail because I2C is still suspended.
> + */
> + disable_irq(client->irq);
> +
> + return 0;
> +}
> +
> +static int nvvrs_pseq_i2c_resume(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> +
> + enable_irq(client->irq);
> + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops nvvrs_pseq_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(nvvrs_pseq_i2c_suspend, nvvrs_pseq_i2c_resume)
> +};
> +
> +static const struct of_device_id nvvrs_dt_match[] = {
> + { .compatible = "nvidia,vrs-pseq" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, nvvrs_dt_match);
> +
> +static struct i2c_driver nvvrs_pseq_driver = {
> + .driver = {
> + .name = "nvvrs_pseq",
> + .pm = &nvvrs_pseq_pm_ops,
> + .of_match_table = of_match_ptr(nvvrs_dt_match),
Drop of_mathc_ptr, you have warnings here.
> + },
> + .probe = nvvrs_pseq_probe,
> +};
> +
> +module_i2c_driver(nvvrs_pseq_driver);
> +
> +MODULE_AUTHOR("Shubhi Garg <shgarg@nvidia.com>");
> +MODULE_DESCRIPTION("NVIDIA Voltage Regulator Specification Power Sequencer Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/nvidia-vrs-pseq.h b/include/linux/mfd/nvidia-vrs-pseq.h
> new file mode 100644
> index 000000000000..7e6f3aa940e7
> --- /dev/null
> +++ b/include/linux/mfd/nvidia-vrs-pseq.h
> @@ -0,0 +1,127 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
> +
> +#ifndef _MFD_NVIDIA_VRS_PSEQ_H_
> +#define _MFD_NVIDIA_VRS_PSEQ_H_
> +
> +#include <linux/types.h>
> +
> +/* Vendor ID */
> +#define NVVRS_PSEQ_REG_VENDOR_ID 0x00
> +#define NVVRS_PSEQ_REG_MODEL_REV 0x01
> +
> +/* Interrupts and Status registers */
> +#define NVVRS_PSEQ_REG_INT_SRC1 0x10
> +#define NVVRS_PSEQ_REG_INT_SRC2 0x11
> +#define NVVRS_PSEQ_REG_INT_VENDOR 0x12
> +#define NVVRS_PSEQ_REG_CTL_STAT 0x13
> +#define NVVRS_PSEQ_REG_EN_STDR1 0x14
> +#define NVVRS_PSEQ_REG_EN_STDR2 0x15
> +#define NVVRS_PSEQ_REG_EN_STRD1 0x16
> +#define NVVRS_PSEQ_REG_EN_STRD2 0x17
> +#define NVVRS_PSEQ_REG_WDT_STAT 0x18
> +#define NVVRS_PSEQ_REG_TEST_STAT 0x19
> +#define NVVRS_PSEQ_REG_LAST_RST 0x1A
> +
> +/* Configuration Registers */
> +#define NVVRS_PSEQ_REG_EN_ALT_F 0x20
> +#define NVVRS_PSEQ_REG_AF_IN_OUT 0x21
> +#define NVVRS_PSEQ_REG_EN_CFG1 0x22
> +#define NVVRS_PSEQ_REG_EN_CFG2 0x23
> +#define NVVRS_PSEQ_REG_CLK_CFG 0x24
> +#define NVVRS_PSEQ_REG_GP_OUT 0x25
> +#define NVVRS_PSEQ_REG_DEB_IN 0x26
> +#define NVVRS_PSEQ_REG_LP_TTSHLD 0x27
> +#define NVVRS_PSEQ_REG_CTL_1 0x28
> +#define NVVRS_PSEQ_REG_CTL_2 0x29
> +#define NVVRS_PSEQ_REG_TEST_CFG 0x2A
> +#define NVVRS_PSEQ_REG_IEN_VENDOR 0x2B
> +
> +/* RTC */
> +#define NVVRS_PSEQ_REG_RTC_T3 0x70
> +#define NVVRS_PSEQ_REG_RTC_T2 0x71
> +#define NVVRS_PSEQ_REG_RTC_T1 0x72
> +#define NVVRS_PSEQ_REG_RTC_T0 0x73
> +#define NVVRS_PSEQ_REG_RTC_A3 0x74
> +#define NVVRS_PSEQ_REG_RTC_A2 0x75
> +#define NVVRS_PSEQ_REG_RTC_A1 0x76
> +#define NVVRS_PSEQ_REG_RTC_A0 0x77
> +
> +/* WDT */
> +#define NVVRS_PSEQ_REG_WDT_CFG 0x80
> +#define NVVRS_PSEQ_REG_WDT_CLOSE 0x81
> +#define NVVRS_PSEQ_REG_WDT_OPEN 0x82
> +#define NVVRS_PSEQ_REG_WDTKEY 0x83
> +
> +/* Interrupt Mask */
> +#define NVVRS_PSEQ_INT_SRC1_RSTIRQ_MASK BIT(0)
> +#define NVVRS_PSEQ_INT_SRC1_OSC_MASK BIT(1)
> +#define NVVRS_PSEQ_INT_SRC1_EN_MASK BIT(2)
> +#define NVVRS_PSEQ_INT_SRC1_RTC_MASK BIT(3)
> +#define NVVRS_PSEQ_INT_SRC1_PEC_MASK BIT(4)
> +#define NVVRS_PSEQ_INT_SRC1_WDT_MASK BIT(5)
> +#define NVVRS_PSEQ_INT_SRC1_EM_PD_MASK BIT(6)
> +#define NVVRS_PSEQ_INT_SRC1_INTERNAL_MASK BIT(7)
> +#define NVVRS_PSEQ_INT_SRC2_PBSP_MASK BIT(0)
> +#define NVVRS_PSEQ_INT_SRC2_ECC_DED_MASK BIT(1)
> +#define NVVRS_PSEQ_INT_SRC2_TSD_MASK BIT(2)
> +#define NVVRS_PSEQ_INT_SRC2_LDO_MASK BIT(3)
> +#define NVVRS_PSEQ_INT_SRC2_BIST_MASK BIT(4)
> +#define NVVRS_PSEQ_INT_SRC2_RT_CRC_MASK BIT(5)
> +#define NVVRS_PSEQ_INT_SRC2_VENDOR_MASK BIT(7)
> +#define NVVRS_PSEQ_INT_VENDOR0_MASK BIT(0)
> +#define NVVRS_PSEQ_INT_VENDOR1_MASK BIT(1)
> +#define NVVRS_PSEQ_INT_VENDOR2_MASK BIT(2)
> +#define NVVRS_PSEQ_INT_VENDOR3_MASK BIT(3)
> +#define NVVRS_PSEQ_INT_VENDOR4_MASK BIT(4)
> +#define NVVRS_PSEQ_INT_VENDOR5_MASK BIT(5)
> +#define NVVRS_PSEQ_INT_VENDOR6_MASK BIT(6)
> +#define NVVRS_PSEQ_INT_VENDOR7_MASK BIT(7)
> +
> +/* Controller Register Mask */
> +#define NVVRS_PSEQ_REG_CTL_1_FORCE_SHDN (BIT(0) | BIT(1))
> +#define NVVRS_PSEQ_REG_CTL_1_FORCE_ACT BIT(2)
> +#define NVVRS_PSEQ_REG_CTL_1_FORCE_INT BIT(3)
> +#define NVVRS_PSEQ_REG_CTL_2_EN_PEC BIT(0)
> +#define NVVRS_PSEQ_REG_CTL_2_REQ_PEC BIT(1)
> +#define NVVRS_PSEQ_REG_CTL_2_RTC_PU BIT(2)
> +#define NVVRS_PSEQ_REG_CTL_2_RTC_WAKE BIT(3)
> +#define NVVRS_PSEQ_REG_CTL_2_RST_DLY 0xF0
> +
> +enum {
> + NVVRS_PSEQ_INT_SRC1_RSTIRQ, /* Reset or Interrupt Pin Fault */
> + NVVRS_PSEQ_INT_SRC1_OSC, /* Crystal Oscillator Fault */
> + NVVRS_PSEQ_INT_SRC1_EN, /* Enable Output Pin Fault */
> + NVVRS_PSEQ_INT_SRC1_RTC, /* RTC Alarm */
> + NVVRS_PSEQ_INT_SRC1_PEC, /* Packet Error Checking */
> + NVVRS_PSEQ_INT_SRC1_WDT, /* Watchdog Violation */
> + NVVRS_PSEQ_INT_SRC1_EM_PD, /* Emergency Power Down */
> + NVVRS_PSEQ_INT_SRC1_INTERNAL, /* Internal Fault*/
> + NVVRS_PSEQ_INT_SRC2_PBSP, /* PWR_BTN Short Pulse Detection */
> + NVVRS_PSEQ_INT_SRC2_ECC_DED, /* ECC Double-Error Detection */
> + NVVRS_PSEQ_INT_SRC2_TSD, /* Thermal Shutdown */
> + NVVRS_PSEQ_INT_SRC2_LDO, /* LDO Fault */
> + NVVRS_PSEQ_INT_SRC2_BIST, /* Built-In Self Test Fault */
> + NVVRS_PSEQ_INT_SRC2_RT_CRC, /* Runtime Register CRC Fault */
> + NVVRS_PSEQ_INT_SRC2_VENDOR, /* Vendor Specific Internal Fault */
> + NVVRS_PSEQ_INT_VENDOR0, /* Vendor Internal Fault Bit 0 */
> + NVVRS_PSEQ_INT_VENDOR1, /* Vendor Internal Fault Bit 1 */
> + NVVRS_PSEQ_INT_VENDOR2, /* Vendor Internal Fault Bit 2 */
> + NVVRS_PSEQ_INT_VENDOR3, /* Vendor Internal Fault Bit 3 */
> + NVVRS_PSEQ_INT_VENDOR4, /* Vendor Internal Fault Bit 4 */
> + NVVRS_PSEQ_INT_VENDOR5, /* Vendor Internal Fault Bit 5 */
> + NVVRS_PSEQ_INT_VENDOR6, /* Vendor Internal Fault Bit 6 */
> + NVVRS_PSEQ_INT_VENDOR7, /* Vendor Internal Fault Bit 7 */
> +};
> +
> +struct nvvrs_pseq_chip {
> + struct device *dev;
> + struct regmap *rmap;
> + int chip_irq;
> + struct i2c_client *client;
> + struct regmap_irq_chip_data *irq_data;
> + const struct regmap_irq_chip *irq_chip;
> + void *irq_drv_data;
> +};
> +
> +#endif /* _MFD_NVIDIA_VRS_PSEQ_H_ */
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 4/5] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver
2025-04-16 12:06 [PATCH 0/5] Add NVIDIA VRS PSEQ support Shubhi Garg
` (2 preceding siblings ...)
2025-04-16 12:06 ` [PATCH 3/5] mfd: nvvrs: add NVVRS PSEQ MFD driver Shubhi Garg
@ 2025-04-16 12:06 ` Shubhi Garg
2025-04-16 14:20 ` Alexandre Belloni
2025-04-16 12:06 ` [PATCH 5/5] arm64: defconfig: enable NVIDIA VRS PSEQ Shubhi Garg
4 siblings, 1 reply; 12+ messages in thread
From: Shubhi Garg @ 2025-04-16 12:06 UTC (permalink / raw)
To: lee, alexandre.belloni, thierry.reding, jonathanh, devicetree,
linux-kernel, linux-tegra
Cc: shgarg
Add support for NVIDIA VRS (Voltage Regulator Specification) Power
Sequencer RTC device driver. This RTC can be used to get/set system
time, retain system time across boot, wake system from suspend and
shutdown state.
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
drivers/rtc/Kconfig | 10 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-nvidia-vrs-pseq.c | 559 ++++++++++++++++++++++++++++++
3 files changed, 570 insertions(+)
create mode 100644 drivers/rtc/rtc-nvidia-vrs-pseq.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 838bdc138ffe..3b6dc27a50af 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -406,6 +406,16 @@ config RTC_DRV_MAX77686
This driver can also be built as a module. If so, the module
will be called rtc-max77686.
+config RTC_DRV_NVVRS_PSEQ
+ tristate "NVIDIA VRS Power Sequencer RTC device"
+ depends on MFD_NVVRS_PSEQ
+ help
+ If you say yes here you will get support for the battery backed RTC device
+ of NVIDIA VRS Power Sequencer. The RTC is connected via I2C interface and
+ supports alarm functionality.
+ This driver can also be built as a module. If so, the module will be called
+ rtc-nvidia-vrs-pseq.
+
config RTC_DRV_NCT3018Y
tristate "Nuvoton NCT3018Y"
depends on OF
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 31473b3276d9..543c5a9fe851 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -119,6 +119,7 @@ obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
+obj-$(CONFIG_RTC_DRV_NVVRS_PSEQ)+= rtc-nvidia-vrs-pseq.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o
diff --git a/drivers/rtc/rtc-nvidia-vrs-pseq.c b/drivers/rtc/rtc-nvidia-vrs-pseq.c
new file mode 100644
index 000000000000..1a4194e4edf4
--- /dev/null
+++ b/drivers/rtc/rtc-nvidia-vrs-pseq.c
@@ -0,0 +1,559 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ *
+ * RTC driver for NVIDIA Voltage Regulator Power Sequencer
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/mfd/nvidia-vrs-pseq.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+#include <linux/bits.h>
+
+#define ALARM_RESET_VAL 0xFFFFFFFF /* Alarm reset/disable value */
+#define NVVRS_INT_RTC_INDEX 0 /* Only one RTC interrupt register */
+#define REG_LEN_IN_BYTES 4
+
+struct nvvrs_rtc_info {
+ struct device *dev;
+ struct i2c_client *client;
+ struct rtc_device *rtc_dev;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *rtc_irq_data;
+ /* Mutex to protect RTC operations */
+ struct mutex lock;
+ unsigned int rtc_irq;
+ /* Registers offset to I2C addresses map */
+ const u8 *map;
+ /* RTC IRQ CHIP */
+ const struct regmap_irq_chip *rtc_irq_chip;
+};
+
+/* RTC Registers offsets */
+enum nvvrs_rtc_reg_offset {
+ RTC_T3 = 0,
+ RTC_T2,
+ RTC_T1,
+ RTC_T0,
+ RTC_A3,
+ RTC_A2,
+ RTC_A1,
+ RTC_A0,
+ CTL1_REG,
+ CTL2_REG,
+ RTC_END,
+};
+
+static const struct regmap_irq nvvrs_rtc_irq[] = {
+ REGMAP_IRQ_REG(NVVRS_INT_RTC_INDEX, 0, NVVRS_PSEQ_INT_SRC1_RTC_MASK),
+};
+
+static const struct regmap_irq_chip nvvrs_rtc_irq_chip = {
+ .name = "nvvrs-rtc",
+ .status_base = NVVRS_PSEQ_REG_INT_SRC1,
+ .num_regs = 1,
+ .irqs = nvvrs_rtc_irq,
+ .num_irqs = ARRAY_SIZE(nvvrs_rtc_irq),
+};
+
+static const u8 rtc_map[RTC_END] = {
+ [RTC_T3] = NVVRS_PSEQ_REG_RTC_T3,
+ [RTC_T2] = NVVRS_PSEQ_REG_RTC_T2,
+ [RTC_T1] = NVVRS_PSEQ_REG_RTC_T1,
+ [RTC_T0] = NVVRS_PSEQ_REG_RTC_T0,
+ [RTC_A3] = NVVRS_PSEQ_REG_RTC_A3,
+ [RTC_A2] = NVVRS_PSEQ_REG_RTC_A2,
+ [RTC_A1] = NVVRS_PSEQ_REG_RTC_A1,
+ [RTC_A0] = NVVRS_PSEQ_REG_RTC_A0,
+ [CTL1_REG] = NVVRS_PSEQ_REG_CTL_1,
+ [CTL2_REG] = NVVRS_PSEQ_REG_CTL_2,
+};
+
+static int nvvrs_update_bits(struct nvvrs_rtc_info *info, u8 reg,
+ u8 mask, u8 value)
+{
+ int ret;
+ u8 val;
+
+ ret = i2c_smbus_read_byte_data(info->client, reg);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read reg:0x%02x ret:(%d)\n",
+ reg, ret);
+ return ret;
+ }
+ val = (u8)ret;
+ val &= ~mask;
+ val |= (value & mask);
+
+ ret = i2c_smbus_write_byte_data(info->client, reg, val);
+ if (ret < 0)
+ dev_err(info->dev, "Failed to write reg:0x%02x val:0x%02x ret:(%d)\n",
+ reg, val, ret);
+
+ return ret;
+}
+
+static int nvvrs_rtc_update_alarm_reg(struct i2c_client *client,
+ struct nvvrs_rtc_info *info, u8 *time)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_A3], time[3]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_A3], ret);
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_A2], time[2]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_A2], ret);
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_A1], time[1]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_A1], ret);
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_A0], time[0]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_A0], ret);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static int nvvrs_rtc_enable_alarm(struct nvvrs_rtc_info *info)
+{
+ int ret;
+
+ /* Set RTC_WAKE bit for autonomous wake from sleep */
+ ret = nvvrs_update_bits(info, info->map[CTL2_REG],
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE,
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to set RTC_WAKE bit (%d)\n", ret);
+ return ret;
+ }
+
+ /* Set RTC_PU bit for autonomous wake from shutdown */
+ ret = nvvrs_update_bits(info, info->map[CTL2_REG],
+ NVVRS_PSEQ_REG_CTL_2_RTC_PU,
+ NVVRS_PSEQ_REG_CTL_2_RTC_PU);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to set RTC_PU bit (%d)\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int nvvrs_rtc_disable_alarm(struct nvvrs_rtc_info *info)
+{
+ struct i2c_client *client = info->client;
+ u8 val[REG_LEN_IN_BYTES];
+ int ret;
+
+ /* Clear RTC_WAKE bit */
+ ret = nvvrs_update_bits(info, info->map[CTL2_REG],
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE, 0);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to clear RTC_WAKE bit (%d)\n", ret);
+ goto out;
+ }
+
+ /* Clear RTC_PU bit */
+ ret = nvvrs_update_bits(info, info->map[CTL2_REG],
+ NVVRS_PSEQ_REG_CTL_2_RTC_PU, 0);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to clear RTC_PU bit (%d)\n", ret);
+ goto out;
+ }
+
+ /* Write ALARM_RESET_VAL in RTC Alarm register to disable alarm */
+ val[0] = 0xff;
+ val[1] = 0xff;
+ val[2] = 0xff;
+ val[3] = 0xff;
+
+ ret = nvvrs_rtc_update_alarm_reg(client, info, val);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to disable Alarm (%d)\n", ret);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static irqreturn_t nvvrs_rtc_irq_handler(int irq, void *data)
+{
+ struct nvvrs_rtc_info *info = data;
+
+ dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq);
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int nvvrs_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ struct i2c_client *client = info->client;
+ time64_t secs = 0;
+ int ret;
+ u8 val;
+
+ mutex_lock(&info->lock);
+
+ /* Multi-byte transfers are not supported with PEC enabled */
+ /* Read MSB first to avoid coherency issues */
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_T3]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
+ info->map[RTC_T3], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ secs |= (time64_t)val << 24;
+
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_T2]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
+ info->map[RTC_T2], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ secs |= (time64_t)val << 16;
+
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_T1]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
+ info->map[RTC_T1], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ secs |= (time64_t)val << 8;
+
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_T0]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
+ info->map[RTC_T0], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ secs |= val;
+
+ rtc_time64_to_tm(secs, tm);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int nvvrs_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ struct i2c_client *client = info->client;
+ u8 time[REG_LEN_IN_BYTES];
+ time64_t secs;
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ secs = rtc_tm_to_time64(tm);
+ time[0] = secs & 0xff;
+ time[1] = (secs >> 8) & 0xff;
+ time[2] = (secs >> 16) & 0xff;
+ time[3] = (secs >> 24) & 0xff;
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_T3], time[3]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_T3], ret);
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_T2], time[2]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_T2], ret);
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_T1], time[1]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_T1], ret);
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, info->map[RTC_T0], time[0]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
+ info->map[RTC_T0], ret);
+ goto out;
+ }
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int nvvrs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ struct i2c_client *client = info->client;
+ time64_t alarm_val = 0;
+ int ret;
+ u8 val;
+
+ mutex_lock(&info->lock);
+
+ /* Multi-byte transfers are not supported with PEC enabled */
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_A3]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
+ info->map[RTC_A3], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ alarm_val |= (time64_t)val << 24;
+
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_A2]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
+ info->map[RTC_A2], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ alarm_val |= (time64_t)val << 16;
+
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_A1]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
+ info->map[RTC_A1], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ alarm_val |= (time64_t)val << 8;
+
+ ret = i2c_smbus_read_byte_data(client, info->map[RTC_A0]);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
+ info->map[RTC_A0], ret);
+ goto out;
+ }
+ val = (u8)ret;
+ alarm_val |= val;
+
+ if (alarm_val == ALARM_RESET_VAL)
+ alrm->enabled = 0;
+ else
+ alrm->enabled = 1;
+
+ rtc_time64_to_tm(alarm_val, &alrm->time);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int nvvrs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ struct i2c_client *client = info->client;
+ u8 time[REG_LEN_IN_BYTES];
+ time64_t secs;
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ ret = nvvrs_rtc_enable_alarm(info);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to enable alarm! (%d)\n", ret);
+ goto out;
+ }
+
+ secs = rtc_tm_to_time64(&alrm->time);
+ time[0] = secs & 0xff;
+ time[1] = (secs >> 8) & 0xff;
+ time[2] = (secs >> 16) & 0xff;
+ time[3] = (secs >> 24) & 0xff;
+
+ ret = nvvrs_rtc_update_alarm_reg(client, info, time);
+
+ alrm->enabled = 1;
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int nvvrs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&info->lock);
+ if (enabled)
+ ret = nvvrs_rtc_enable_alarm(info);
+ else
+ ret = nvvrs_rtc_disable_alarm(info);
+
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static const struct rtc_class_ops nvvrs_rtc_ops = {
+ .read_time = nvvrs_rtc_read_time,
+ .set_time = nvvrs_rtc_set_time,
+ .read_alarm = nvvrs_rtc_read_alarm,
+ .set_alarm = nvvrs_rtc_set_alarm,
+ .alarm_irq_enable = nvvrs_rtc_alarm_irq_enable,
+};
+
+static int nvvrs_rtc_probe(struct platform_device *pdev)
+{
+ struct nvvrs_rtc_info *info;
+ struct device *parent;
+ struct i2c_client *client;
+ int ret;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct nvvrs_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get irq\n");
+ return ret;
+ }
+ info->rtc_irq = ret;
+
+ mutex_init(&info->lock);
+ info->dev = &pdev->dev;
+ parent = info->dev->parent;
+ client = to_i2c_client(parent);
+ client->flags |= I2C_CLIENT_PEC;
+ i2c_set_clientdata(client, info);
+ info->client = client;
+ info->map = rtc_map;
+ info->rtc_irq_chip = &nvvrs_rtc_irq_chip;
+ platform_set_drvdata(pdev, info);
+
+ /* Initialize regmap */
+ info->regmap = dev_get_regmap(parent, NULL);
+ if (!info->regmap) {
+ dev_err(info->dev, "Failed to get RTC regmap\n");
+ return -ENODEV;
+ }
+
+ /* RTC as a wakeup source */
+ device_init_wakeup(info->dev, 1);
+
+ /* Register RTC device */
+ info->rtc_dev = devm_rtc_device_register(info->dev, "nvvrs-rtc",
+ &nvvrs_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ return ret;
+ }
+
+ ret = request_threaded_irq(info->rtc_irq, NULL, nvvrs_rtc_irq_handler, 0,
+ "rtc-alarm", info);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->rtc_irq, ret);
+
+ return ret;
+}
+
+static void nvvrs_rtc_remove(struct platform_device *pdev)
+{
+ struct nvvrs_rtc_info *info = platform_get_drvdata(pdev);
+
+ free_irq(info->rtc_irq, info);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nvvrs_rtc_suspend(struct device *dev)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (device_may_wakeup(dev)) {
+ /* Set RTC_WAKE bit for auto wake system from suspend state */
+ ret = nvvrs_update_bits(info, info->map[CTL2_REG],
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE,
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to set RTC_WAKE bit (%d)\n", ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(info->rtc_irq);
+ }
+
+ return ret;
+}
+
+static int nvvrs_rtc_resume(struct device *dev)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ if (device_may_wakeup(dev)) {
+ /* Clear FORCE_ACT bit */
+ ret = nvvrs_update_bits(info, info->map[CTL1_REG],
+ NVVRS_PSEQ_REG_CTL_1_FORCE_ACT, 0);
+ if (ret < 0) {
+ dev_err(info->dev, "Failed to clear FORCE_ACT bit (%d)\n", ret);
+ return ret;
+ }
+
+ return disable_irq_wake(info->rtc_irq);
+ }
+
+ return 0;
+}
+
+#endif
+static SIMPLE_DEV_PM_OPS(nvvrs_rtc_pm_ops, nvvrs_rtc_suspend, nvvrs_rtc_resume);
+
+static const struct platform_device_id nvvrs_rtc_id[] = {
+ { "nvvrs-pseq-rtc", },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, nvvrs_rtc_id);
+
+static struct platform_driver nvvrs_rtc_driver = {
+ .driver = {
+ .name = "nvvrs-pseq-rtc",
+ .pm = &nvvrs_rtc_pm_ops,
+ },
+ .probe = nvvrs_rtc_probe,
+ .remove = nvvrs_rtc_remove,
+ .id_table = nvvrs_rtc_id,
+};
+
+module_platform_driver(nvvrs_rtc_driver);
+
+MODULE_AUTHOR("Shubhi Garg <shgarg@nvidia.com>");
+MODULE_DESCRIPTION("NVVRS PSEQ RTC driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 4/5] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver
2025-04-16 12:06 ` [PATCH 4/5] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver Shubhi Garg
@ 2025-04-16 14:20 ` Alexandre Belloni
0 siblings, 0 replies; 12+ messages in thread
From: Alexandre Belloni @ 2025-04-16 14:20 UTC (permalink / raw)
To: Shubhi Garg
Cc: lee, thierry.reding, jonathanh, devicetree, linux-kernel,
linux-tegra
Hello,
On 16/04/2025 12:06:18+0000, Shubhi Garg wrote:
> Add support for NVIDIA VRS (Voltage Regulator Specification) Power
> Sequencer RTC device driver. This RTC can be used to get/set system
> time, retain system time across boot, wake system from suspend and
> shutdown state.
>
> Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
> ---
> drivers/rtc/Kconfig | 10 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-nvidia-vrs-pseq.c | 559 ++++++++++++++++++++++++++++++
> 3 files changed, 570 insertions(+)
> create mode 100644 drivers/rtc/rtc-nvidia-vrs-pseq.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 838bdc138ffe..3b6dc27a50af 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -406,6 +406,16 @@ config RTC_DRV_MAX77686
> This driver can also be built as a module. If so, the module
> will be called rtc-max77686.
>
> +config RTC_DRV_NVVRS_PSEQ
> + tristate "NVIDIA VRS Power Sequencer RTC device"
> + depends on MFD_NVVRS_PSEQ
> + help
> + If you say yes here you will get support for the battery backed RTC device
> + of NVIDIA VRS Power Sequencer. The RTC is connected via I2C interface and
> + supports alarm functionality.
> + This driver can also be built as a module. If so, the module will be called
> + rtc-nvidia-vrs-pseq.
> +
> config RTC_DRV_NCT3018Y
> tristate "Nuvoton NCT3018Y"
> depends on OF
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 31473b3276d9..543c5a9fe851 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -119,6 +119,7 @@ obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
> obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
> obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o
> obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
> +obj-$(CONFIG_RTC_DRV_NVVRS_PSEQ)+= rtc-nvidia-vrs-pseq.o
> obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
> obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
> obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o
> diff --git a/drivers/rtc/rtc-nvidia-vrs-pseq.c b/drivers/rtc/rtc-nvidia-vrs-pseq.c
> new file mode 100644
> index 000000000000..1a4194e4edf4
> --- /dev/null
> +++ b/drivers/rtc/rtc-nvidia-vrs-pseq.c
> @@ -0,0 +1,559 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
How old is this driver actually, it uses decades old APIs :)
> + *
> + * RTC driver for NVIDIA Voltage Regulator Power Sequencer
> + *
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/rtc.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_device.h>
> +#include <linux/mfd/nvidia-vrs-pseq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/regmap.h>
> +#include <linux/bits.h>
> +
> +#define ALARM_RESET_VAL 0xFFFFFFFF /* Alarm reset/disable value */
> +#define NVVRS_INT_RTC_INDEX 0 /* Only one RTC interrupt register */
> +#define REG_LEN_IN_BYTES 4
> +
> +struct nvvrs_rtc_info {
> + struct device *dev;
> + struct i2c_client *client;
> + struct rtc_device *rtc_dev;
> + struct regmap *regmap;
So you have a regmap but never use it?
> + struct regmap_irq_chip_data *rtc_irq_data;
> + /* Mutex to protect RTC operations */
> + struct mutex lock;
This lock is useless, simply use rtc_lock/rtc_unlock in your irq handler
> + unsigned int rtc_irq;
> + /* Registers offset to I2C addresses map */
> + const u8 *map;
I don't get how this indirection is useful
> + /* RTC IRQ CHIP */
> + const struct regmap_irq_chip *rtc_irq_chip;
> +};
> +
> +/* RTC Registers offsets */
> +enum nvvrs_rtc_reg_offset {
> + RTC_T3 = 0,
> + RTC_T2,
> + RTC_T1,
> + RTC_T0,
> + RTC_A3,
> + RTC_A2,
> + RTC_A1,
> + RTC_A0,
> + CTL1_REG,
> + CTL2_REG,
> + RTC_END,
> +};
> +
> +static const struct regmap_irq nvvrs_rtc_irq[] = {
> + REGMAP_IRQ_REG(NVVRS_INT_RTC_INDEX, 0, NVVRS_PSEQ_INT_SRC1_RTC_MASK),
> +};
> +
> +static const struct regmap_irq_chip nvvrs_rtc_irq_chip = {
> + .name = "nvvrs-rtc",
> + .status_base = NVVRS_PSEQ_REG_INT_SRC1,
> + .num_regs = 1,
> + .irqs = nvvrs_rtc_irq,
> + .num_irqs = ARRAY_SIZE(nvvrs_rtc_irq),
> +};
> +
> +static const u8 rtc_map[RTC_END] = {
> + [RTC_T3] = NVVRS_PSEQ_REG_RTC_T3,
> + [RTC_T2] = NVVRS_PSEQ_REG_RTC_T2,
> + [RTC_T1] = NVVRS_PSEQ_REG_RTC_T1,
> + [RTC_T0] = NVVRS_PSEQ_REG_RTC_T0,
> + [RTC_A3] = NVVRS_PSEQ_REG_RTC_A3,
> + [RTC_A2] = NVVRS_PSEQ_REG_RTC_A2,
> + [RTC_A1] = NVVRS_PSEQ_REG_RTC_A1,
> + [RTC_A0] = NVVRS_PSEQ_REG_RTC_A0,
> + [CTL1_REG] = NVVRS_PSEQ_REG_CTL_1,
> + [CTL2_REG] = NVVRS_PSEQ_REG_CTL_2,
> +};
> +
> +static int nvvrs_update_bits(struct nvvrs_rtc_info *info, u8 reg,
> + u8 mask, u8 value)
> +{
> + int ret;
> + u8 val;
> +
> + ret = i2c_smbus_read_byte_data(info->client, reg);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read reg:0x%02x ret:(%d)\n",
> + reg, ret);
The driver is too verbose, you have plenty of dev_err that are very
unlikely to happen and that have no impact on the user actions but those
strings will always be bloating the kernel.
> + return ret;
> + }
> + val = (u8)ret;
> + val &= ~mask;
> + val |= (value & mask);
> +
> + ret = i2c_smbus_write_byte_data(info->client, reg, val);
> + if (ret < 0)
> + dev_err(info->dev, "Failed to write reg:0x%02x val:0x%02x ret:(%d)\n",
> + reg, val, ret);
> +
> + return ret;
> +}
> +
> +static int nvvrs_rtc_update_alarm_reg(struct i2c_client *client,
> + struct nvvrs_rtc_info *info, u8 *time)
> +{
> + int ret;
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_A3], time[3]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_A3], ret);
> + goto out;
> + }
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_A2], time[2]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_A2], ret);
> + goto out;
> + }
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_A1], time[1]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_A1], ret);
> + goto out;
> + }
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_A0], time[0]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write alarm reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_A0], ret);
> + goto out;
> + }
> +
> +out:
> + return ret;
> +}
> +
> +static int nvvrs_rtc_enable_alarm(struct nvvrs_rtc_info *info)
> +{
> + int ret;
> +
> + /* Set RTC_WAKE bit for autonomous wake from sleep */
> + ret = nvvrs_update_bits(info, info->map[CTL2_REG],
> + NVVRS_PSEQ_REG_CTL_2_RTC_WAKE,
> + NVVRS_PSEQ_REG_CTL_2_RTC_WAKE);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to set RTC_WAKE bit (%d)\n", ret);
> + return ret;
> + }
> +
> + /* Set RTC_PU bit for autonomous wake from shutdown */
> + ret = nvvrs_update_bits(info, info->map[CTL2_REG],
> + NVVRS_PSEQ_REG_CTL_2_RTC_PU,
> + NVVRS_PSEQ_REG_CTL_2_RTC_PU);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to set RTC_PU bit (%d)\n", ret);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int nvvrs_rtc_disable_alarm(struct nvvrs_rtc_info *info)
> +{
> + struct i2c_client *client = info->client;
> + u8 val[REG_LEN_IN_BYTES];
> + int ret;
> +
> + /* Clear RTC_WAKE bit */
> + ret = nvvrs_update_bits(info, info->map[CTL2_REG],
> + NVVRS_PSEQ_REG_CTL_2_RTC_WAKE, 0);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to clear RTC_WAKE bit (%d)\n", ret);
> + goto out;
> + }
> +
> + /* Clear RTC_PU bit */
> + ret = nvvrs_update_bits(info, info->map[CTL2_REG],
> + NVVRS_PSEQ_REG_CTL_2_RTC_PU, 0);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to clear RTC_PU bit (%d)\n", ret);
> + goto out;
> + }
> +
> + /* Write ALARM_RESET_VAL in RTC Alarm register to disable alarm */
> + val[0] = 0xff;
> + val[1] = 0xff;
> + val[2] = 0xff;
> + val[3] = 0xff;
> +
> + ret = nvvrs_rtc_update_alarm_reg(client, info, val);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to disable Alarm (%d)\n", ret);
> + goto out;
> + }
> +out:
> + return ret;
> +}
> +
> +static irqreturn_t nvvrs_rtc_irq_handler(int irq, void *data)
> +{
> + struct nvvrs_rtc_info *info = data;
> +
> + dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq);
> +
> + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int nvvrs_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
> + struct i2c_client *client = info->client;
> + time64_t secs = 0;
> + int ret;
> + u8 val;
> +
> + mutex_lock(&info->lock);
> +
> + /* Multi-byte transfers are not supported with PEC enabled */
> + /* Read MSB first to avoid coherency issues */
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_T3]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
> + info->map[RTC_T3], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + secs |= (time64_t)val << 24;
> +
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_T2]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
> + info->map[RTC_T2], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + secs |= (time64_t)val << 16;
> +
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_T1]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
> + info->map[RTC_T1], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + secs |= (time64_t)val << 8;
> +
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_T0]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read time reg:0x%02x ret:(%d)\n",
> + info->map[RTC_T0], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + secs |= val;
> +
> + rtc_time64_to_tm(secs, tm);
> +out:
> + mutex_unlock(&info->lock);
> + return ret;
> +}
> +
> +static int nvvrs_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
> + struct i2c_client *client = info->client;
> + u8 time[REG_LEN_IN_BYTES];
> + time64_t secs;
> + int ret;
> +
> + mutex_lock(&info->lock);
> +
> + secs = rtc_tm_to_time64(tm);
> + time[0] = secs & 0xff;
> + time[1] = (secs >> 8) & 0xff;
> + time[2] = (secs >> 16) & 0xff;
> + time[3] = (secs >> 24) & 0xff;
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_T3], time[3]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_T3], ret);
> + goto out;
> + }
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_T2], time[2]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_T2], ret);
> + goto out;
> + }
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_T1], time[1]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_T1], ret);
> + goto out;
> + }
> +
> + ret = i2c_smbus_write_byte_data(client, info->map[RTC_T0], time[0]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to write time reg: 0x%02x ret:(%d)\n",
> + info->map[RTC_T0], ret);
> + goto out;
> + }
> +
> +out:
> + mutex_unlock(&info->lock);
> + return ret;
> +}
> +
> +static int nvvrs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
> + struct i2c_client *client = info->client;
> + time64_t alarm_val = 0;
> + int ret;
> + u8 val;
> +
> + mutex_lock(&info->lock);
> +
> + /* Multi-byte transfers are not supported with PEC enabled */
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_A3]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
> + info->map[RTC_A3], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + alarm_val |= (time64_t)val << 24;
> +
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_A2]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
> + info->map[RTC_A2], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + alarm_val |= (time64_t)val << 16;
> +
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_A1]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
> + info->map[RTC_A1], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + alarm_val |= (time64_t)val << 8;
> +
> + ret = i2c_smbus_read_byte_data(client, info->map[RTC_A0]);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to read alarm reg:0x%02x ret:(%d)\n",
> + info->map[RTC_A0], ret);
> + goto out;
> + }
> + val = (u8)ret;
> + alarm_val |= val;
> +
> + if (alarm_val == ALARM_RESET_VAL)
> + alrm->enabled = 0;
> + else
> + alrm->enabled = 1;
> +
> + rtc_time64_to_tm(alarm_val, &alrm->time);
> +out:
> + mutex_unlock(&info->lock);
> + return ret;
> +}
> +
> +static int nvvrs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
> + struct i2c_client *client = info->client;
> + u8 time[REG_LEN_IN_BYTES];
> + time64_t secs;
> + int ret;
> +
> + mutex_lock(&info->lock);
> +
> + ret = nvvrs_rtc_enable_alarm(info);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to enable alarm! (%d)\n", ret);
> + goto out;
> + }
> +
> + secs = rtc_tm_to_time64(&alrm->time);
> + time[0] = secs & 0xff;
> + time[1] = (secs >> 8) & 0xff;
> + time[2] = (secs >> 16) & 0xff;
> + time[3] = (secs >> 24) & 0xff;
> +
> + ret = nvvrs_rtc_update_alarm_reg(client, info, time);
> +
> + alrm->enabled = 1;
> +out:
> + mutex_unlock(&info->lock);
> + return ret;
> +}
> +
> +static int nvvrs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> + struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + mutex_lock(&info->lock);
> + if (enabled)
> + ret = nvvrs_rtc_enable_alarm(info);
> + else
> + ret = nvvrs_rtc_disable_alarm(info);
> +
> + mutex_unlock(&info->lock);
> + return ret;
> +}
> +
> +static const struct rtc_class_ops nvvrs_rtc_ops = {
> + .read_time = nvvrs_rtc_read_time,
> + .set_time = nvvrs_rtc_set_time,
> + .read_alarm = nvvrs_rtc_read_alarm,
> + .set_alarm = nvvrs_rtc_set_alarm,
> + .alarm_irq_enable = nvvrs_rtc_alarm_irq_enable,
> +};
> +
> +static int nvvrs_rtc_probe(struct platform_device *pdev)
> +{
> + struct nvvrs_rtc_info *info;
> + struct device *parent;
> + struct i2c_client *client;
> + int ret;
> +
> + info = devm_kzalloc(&pdev->dev, sizeof(struct nvvrs_rtc_info), GFP_KERNEL);
> + if (!info)
> + return -ENOMEM;
> +
> + ret = platform_get_irq(pdev, 0);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Failed to get irq\n");
> + return ret;
> + }
> + info->rtc_irq = ret;
> +
> + mutex_init(&info->lock);
> + info->dev = &pdev->dev;
> + parent = info->dev->parent;
> + client = to_i2c_client(parent);
> + client->flags |= I2C_CLIENT_PEC;
> + i2c_set_clientdata(client, info);
> + info->client = client;
> + info->map = rtc_map;
> + info->rtc_irq_chip = &nvvrs_rtc_irq_chip;
> + platform_set_drvdata(pdev, info);
> +
> + /* Initialize regmap */
> + info->regmap = dev_get_regmap(parent, NULL);
> + if (!info->regmap) {
> + dev_err(info->dev, "Failed to get RTC regmap\n");
> + return -ENODEV;
> + }
> +
> + /* RTC as a wakeup source */
> + device_init_wakeup(info->dev, 1);
> +
> + /* Register RTC device */
> + info->rtc_dev = devm_rtc_device_register(info->dev, "nvvrs-rtc",
> + &nvvrs_rtc_ops, THIS_MODULE);
You must use devm_rtc_allocate_device and devm_rtc_register_device,
devm_rtc_allocate_device is deprecated.
You must also set range_min and range_max.
> +
> + if (IS_ERR(info->rtc_dev)) {
> + ret = PTR_ERR(info->rtc_dev);
> + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
> + return ret;
> + }
> +
> + ret = request_threaded_irq(info->rtc_irq, NULL, nvvrs_rtc_irq_handler, 0,
> + "rtc-alarm", info);
Why don't you use the devm_ version and drop nvvrs_rtc_remove() ?
> + if (ret < 0)
> + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
> + info->rtc_irq, ret);
It is not allowed to fail after registering an rtc_device
> +
> + return ret;
> +}
> +
> +static void nvvrs_rtc_remove(struct platform_device *pdev)
> +{
> + struct nvvrs_rtc_info *info = platform_get_drvdata(pdev);
> +
> + free_irq(info->rtc_irq, info);
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int nvvrs_rtc_suspend(struct device *dev)
> +{
> + struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + if (device_may_wakeup(dev)) {
> + /* Set RTC_WAKE bit for auto wake system from suspend state */
> + ret = nvvrs_update_bits(info, info->map[CTL2_REG],
> + NVVRS_PSEQ_REG_CTL_2_RTC_WAKE,
> + NVVRS_PSEQ_REG_CTL_2_RTC_WAKE);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to set RTC_WAKE bit (%d)\n", ret);
> + return ret;
> + }
> +
> + ret = enable_irq_wake(info->rtc_irq);
> + }
> +
> + return ret;
> +}
> +
> +static int nvvrs_rtc_resume(struct device *dev)
> +{
> + struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
> + int ret;
> +
> + if (device_may_wakeup(dev)) {
> + /* Clear FORCE_ACT bit */
> + ret = nvvrs_update_bits(info, info->map[CTL1_REG],
> + NVVRS_PSEQ_REG_CTL_1_FORCE_ACT, 0);
> + if (ret < 0) {
> + dev_err(info->dev, "Failed to clear FORCE_ACT bit (%d)\n", ret);
> + return ret;
> + }
> +
> + return disable_irq_wake(info->rtc_irq);
> + }
> +
> + return 0;
> +}
> +
> +#endif
> +static SIMPLE_DEV_PM_OPS(nvvrs_rtc_pm_ops, nvvrs_rtc_suspend, nvvrs_rtc_resume);
> +
> +static const struct platform_device_id nvvrs_rtc_id[] = {
> + { "nvvrs-pseq-rtc", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(platform, nvvrs_rtc_id);
> +
> +static struct platform_driver nvvrs_rtc_driver = {
> + .driver = {
> + .name = "nvvrs-pseq-rtc",
> + .pm = &nvvrs_rtc_pm_ops,
> + },
> + .probe = nvvrs_rtc_probe,
> + .remove = nvvrs_rtc_remove,
> + .id_table = nvvrs_rtc_id,
> +};
> +
> +module_platform_driver(nvvrs_rtc_driver);
> +
> +MODULE_AUTHOR("Shubhi Garg <shgarg@nvidia.com>");
> +MODULE_DESCRIPTION("NVVRS PSEQ RTC driver");
> +MODULE_LICENSE("GPL");
> --
> 2.25.1
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 5/5] arm64: defconfig: enable NVIDIA VRS PSEQ
2025-04-16 12:06 [PATCH 0/5] Add NVIDIA VRS PSEQ support Shubhi Garg
` (3 preceding siblings ...)
2025-04-16 12:06 ` [PATCH 4/5] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver Shubhi Garg
@ 2025-04-16 12:06 ` Shubhi Garg
2025-04-17 5:28 ` Krzysztof Kozlowski
4 siblings, 1 reply; 12+ messages in thread
From: Shubhi Garg @ 2025-04-16 12:06 UTC (permalink / raw)
To: lee, alexandre.belloni, thierry.reding, jonathanh, devicetree,
linux-kernel, linux-tegra
Cc: shgarg
Enable NVIDIA VRS (Voltage Regulator Specification) power sequencer
device modules. NVIDIA VRS PSEQ controls ON/OFF and suspend/resume
power sequencing of system power rails on Tegra234 SoC. This device
also provides 32kHz RTC support with backup battery for system timing.
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
arch/arm64/configs/defconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 5c85e5da152f..9c6cba664a43 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -780,6 +780,8 @@ CONFIG_MFD_TPS6594_I2C=m
CONFIG_MFD_ROHM_BD718XX=y
CONFIG_MFD_WCD934X=m
CONFIG_MFD_KHADAS_MCU=m
+CONFIG_MFD_NVVRS_PSEQ=m
+CONFIG_RTC_DRV_NVVRS_PSEQ=m
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_AXP20X=y
CONFIG_REGULATOR_BD718XX=y
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 5/5] arm64: defconfig: enable NVIDIA VRS PSEQ
2025-04-16 12:06 ` [PATCH 5/5] arm64: defconfig: enable NVIDIA VRS PSEQ Shubhi Garg
@ 2025-04-17 5:28 ` Krzysztof Kozlowski
0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-04-17 5:28 UTC (permalink / raw)
To: Shubhi Garg, lee, alexandre.belloni, thierry.reding, jonathanh,
devicetree, linux-kernel, linux-tegra
On 16/04/2025 14:06, Shubhi Garg wrote:
> Enable NVIDIA VRS (Voltage Regulator Specification) power sequencer
> device modules. NVIDIA VRS PSEQ controls ON/OFF and suspend/resume
> power sequencing of system power rails on Tegra234 SoC. This device
Which upstream boards use it?
> also provides 32kHz RTC support with backup battery for system timing.
>
> Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
> ---
> arch/arm64/configs/defconfig | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
> index 5c85e5da152f..9c6cba664a43 100644
> --- a/arch/arm64/configs/defconfig
> +++ b/arch/arm64/configs/defconfig
> @@ -780,6 +780,8 @@ CONFIG_MFD_TPS6594_I2C=m
> CONFIG_MFD_ROHM_BD718XX=y
> CONFIG_MFD_WCD934X=m
> CONFIG_MFD_KHADAS_MCU=m
> +CONFIG_MFD_NVVRS_PSEQ=m
> +CONFIG_RTC_DRV_NVVRS_PSEQ=m
Does not look like properly ordered.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 12+ messages in thread