* [PATCH v4 1/6] dt-bindings: mfd: add NVIDIA VRS PSEQ
2025-06-19 8:44 [PATCH v4 0/6] Add NVIDIA VRS PSEQ support Shubhi Garg
@ 2025-06-19 8:44 ` Shubhi Garg
2025-06-22 11:36 ` Krzysztof Kozlowski
2025-06-19 8:44 ` [PATCH v4 2/6] arm64: tegra: Add device-tree node for NVVRS PSEQ Shubhi Garg
` (4 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Shubhi Garg @ 2025-06-19 8:44 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Catalin Marinas, Will Deacon, Alexandre Belloni, Jonathan Hunter
Cc: devicetree, linux-arm-kernel, linux-rtc, linux-tegra, Shubhi Garg
Add support for NVIDIA VRS (Voltage Regulator Specification) power
sequencer device. NVIDIA VRS PSEQ provides 32kHz RTC support with backup
battery for system timing. It controls ON/OFF and suspend/resume power
sequencing of system power rails on below NVIDIA platforms:
- NVIDIA Jetson AGX Orin Developer Kit
- NVIDIA IGX Orin Development Kit
- NVIDIA Jetson Orin NX Developer Kit
- NVIDIA Jetson Orin Nano Developer Kit
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
v4:
- no changes
v3:
- fixed device tree node name to generic "pmic@3c"
- fixed indentation
v2:
- fixed copyrights
- updated description with RTC information
- added status node in dtb node example
.../bindings/mfd/nvidia,vrs-pseq.yaml | 60 +++++++++++++++++++
1 file changed, 60 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..65bf77f70c44
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%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. The device also acts as an interrupt controller for managing
+ interrupts from the VRS power sequencer.
+
+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>;
+
+ pmic@3c {
+ compatible = "nvidia,vrs-pseq";
+ reg = <0x3c>;
+ interrupt-parent = <&pmc>;
+ interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/6] dt-bindings: mfd: add NVIDIA VRS PSEQ
2025-06-19 8:44 ` [PATCH v4 1/6] dt-bindings: mfd: add NVIDIA VRS PSEQ Shubhi Garg
@ 2025-06-22 11:36 ` Krzysztof Kozlowski
0 siblings, 0 replies; 9+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-22 11:36 UTC (permalink / raw)
To: Shubhi Garg
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Catalin Marinas, Will Deacon, Alexandre Belloni, Jonathan Hunter,
devicetree, linux-arm-kernel, linux-rtc, linux-tegra
On Thu, Jun 19, 2025 at 08:44:22AM +0000, Shubhi Garg wrote:
> Add support for NVIDIA VRS (Voltage Regulator Specification) power
> sequencer device. NVIDIA VRS PSEQ provides 32kHz RTC support with backup
> battery for system timing. It controls ON/OFF and suspend/resume power
> sequencing of system power rails on below NVIDIA platforms:
>
> - NVIDIA Jetson AGX Orin Developer Kit
> - NVIDIA IGX Orin Development Kit
> - NVIDIA Jetson Orin NX Developer Kit
> - NVIDIA Jetson Orin Nano Developer Kit
>
> Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
> ---
>
> v4:
> - no changes
>
> v3:
> - fixed device tree node name to generic "pmic@3c"
> - fixed indentation
>
> v2:
> - fixed copyrights
> - updated description with RTC information
> - added status node in dtb node example
>
> .../bindings/mfd/nvidia,vrs-pseq.yaml | 60 +++++++++++++++++++
> 1 file changed, 60 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
Does not look like mfd device. Isn't there appropriate directory for this?
>
> 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..65bf77f70c44
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
> @@ -0,0 +1,60 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%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. The device also acts as an interrupt controller for managing
> + interrupts from the VRS power sequencer.
> +
> +properties:
> + compatible:
> + const: nvidia,vrs-pseq
I2C devices, even internal to vendors like Qcom, Samsung, Renesas,
usually have models and version numbers. This looks really incomplete. I
don't think generic compatible would be acceptable for I2C.
Plus, pseq is redundant. Can it be anything else?
> +
> + 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.
That's the default/standard, drop description.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 2/6] arm64: tegra: Add device-tree node for NVVRS PSEQ
2025-06-19 8:44 [PATCH v4 0/6] Add NVIDIA VRS PSEQ support Shubhi Garg
2025-06-19 8:44 ` [PATCH v4 1/6] dt-bindings: mfd: add NVIDIA VRS PSEQ Shubhi Garg
@ 2025-06-19 8:44 ` Shubhi Garg
2025-06-19 8:44 ` [PATCH v4 3/6] mfd: nvvrs: add NVVRS PSEQ MFD driver Shubhi Garg
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Shubhi Garg @ 2025-06-19 8:44 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Catalin Marinas, Will Deacon, Alexandre Belloni, Jonathan Hunter
Cc: devicetree, linux-arm-kernel, linux-rtc, linux-tegra, Shubhi Garg
Add NVIDIA VRS Power Sequencer device tree node for Tegra234 P3701 and
P3767 platforms. Assign VRS RTC as primary RTC (rtc0).
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
v4:
- fixed device tree node name to "pmic@3c" in aliases
v3:
- fixed device tree node name to generic "pmic@3c"
v2:
- added alias to assign VRS RTC to rtc0
- removed status node from VRS DTB node
arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi | 11 +++++++++++
arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi | 15 +++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
index 9086a0d010e5..f327ccf48c8f 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi
@@ -8,6 +8,7 @@ / {
aliases {
mmc0 = "/bus@0/mmc@3460000";
mmc1 = "/bus@0/mmc@3400000";
+ rtc0 = "/bpmp/i2c/pmic@3c";
};
bus@0 {
@@ -170,6 +171,16 @@ bpmp {
i2c {
status = "okay";
+ pmic@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>;
+ };
+
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..437fda14831a 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi
@@ -7,6 +7,7 @@ / {
aliases {
mmc0 = "/bus@0/mmc@3400000";
+ rtc0 = "/bpmp/i2c/pmic@3c";
};
bus@0 {
@@ -121,6 +122,20 @@ pmc@c360000 {
};
};
+ bpmp {
+ i2c {
+ pmic@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>;
+ };
+ };
+ };
+
vdd_5v0_sys: regulator-vdd-5v0-sys {
compatible = "regulator-fixed";
regulator-name = "VDD_5V0_SYS";
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 3/6] mfd: nvvrs: add NVVRS PSEQ MFD driver
2025-06-19 8:44 [PATCH v4 0/6] Add NVIDIA VRS PSEQ support Shubhi Garg
2025-06-19 8:44 ` [PATCH v4 1/6] dt-bindings: mfd: add NVIDIA VRS PSEQ Shubhi Garg
2025-06-19 8:44 ` [PATCH v4 2/6] arm64: tegra: Add device-tree node for NVVRS PSEQ Shubhi Garg
@ 2025-06-19 8:44 ` Shubhi Garg
2025-06-25 11:29 ` Lee Jones
2025-06-19 8:44 ` [PATCH v4 4/6] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver Shubhi Garg
` (2 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Shubhi Garg @ 2025-06-19 8:44 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Catalin Marinas, Will Deacon, Alexandre Belloni, Jonathan Hunter
Cc: devicetree, linux-arm-kernel, linux-rtc, linux-tegra, Shubhi Garg
Add support for NVIDIA VRS (Voltage Regulator Specification) power
sequencer device driver. NVIDIA VRS PSEQ provides 32kHz RTC support with
backup battery for system timing. It controls ON/OFF and suspend/resume
power sequencing of system power rails on below NVIDIA platforms:
- NVIDIA Jetson AGX Orin Developer Kit
- NVIDIA IGX Orin Development Kit
- NVIDIA Jetson Orin NX Developer Kit
- NVIDIA Jetson Orin Nano Developer Kit
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
v4:
- no changes
v3:
- added rate limiting to interrupt clearing debug logs
- removed unnecessary braces in if blocks
- changed dependency from I2C=y to I2C in mfd Kconfig
v2:
- removed unnecessary error logs
- changed dev_info to dev_dbg
- changed dev_err to dev_err_probe
- fixed "of_match_table" assignment
drivers/mfd/Kconfig | 12 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/nvidia-vrs-pseq.c | 267 ++++++++++++++++++++++++++++
include/linux/mfd/nvidia-vrs-pseq.h | 127 +++++++++++++
4 files changed, 407 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 6fb3768e3d71..9a3451eebd6e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1437,6 +1437,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
+ 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 79495f9f3457..9b07289985b5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -183,6 +183,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..cef7abac08b7
--- /dev/null
+++ b/drivers/mfd/nvidia-vrs-pseq.c
@@ -0,0 +1,267 @@
+// 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) {
+ val = (u8)ret;
+ dev_dbg_ratelimited(chip->dev,
+ "Clear IRQ reg 0x%02x=0x%02x\n",
+ reg, val);
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static 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)
+ return dev_err_probe(chip->dev, ret,
+ "Failed to read Vendor ID\n");
+
+ vendor_id = (u8)ret;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_MODEL_REV);
+ if (ret < 0)
+ return dev_err_probe(chip->dev, ret,
+ "Failed to read Model Rev\n");
+
+ 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_dbg(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))
+ return dev_err_probe(nvvrs_chip->dev, PTR_ERR(nvvrs_chip->rmap),
+ "Failed to initialise regmap\n");
+
+ ret = nvvrs_pseq_vendor_info(nvvrs_chip);
+ if (ret < 0)
+ return ret;
+
+ nvvrs_pseq_irq_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)
+ return dev_err_probe(nvvrs_chip->dev, ret,
+ "Failed to add regmap irq\n");
+
+ 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)
+ return dev_err_probe(nvvrs_chip->dev, ret,
+ "Failed to add MFD children\n");
+
+ 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 = 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.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 3/6] mfd: nvvrs: add NVVRS PSEQ MFD driver
2025-06-19 8:44 ` [PATCH v4 3/6] mfd: nvvrs: add NVVRS PSEQ MFD driver Shubhi Garg
@ 2025-06-25 11:29 ` Lee Jones
0 siblings, 0 replies; 9+ messages in thread
From: Lee Jones @ 2025-06-25 11:29 UTC (permalink / raw)
To: Shubhi Garg
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Catalin Marinas,
Will Deacon, Alexandre Belloni, Jonathan Hunter, devicetree,
linux-arm-kernel, linux-rtc, linux-tegra
With respect to the subject line, please remove "MFD driver".
You can use "core driver" or replace it for whatever this is.
> Add support for NVIDIA VRS (Voltage Regulator Specification) power
> sequencer device driver. NVIDIA VRS PSEQ provides 32kHz RTC support with
> backup battery for system timing. It controls ON/OFF and suspend/resume
> power sequencing of system power rails on below NVIDIA platforms:
>
> - NVIDIA Jetson AGX Orin Developer Kit
> - NVIDIA IGX Orin Development Kit
> - NVIDIA Jetson Orin NX Developer Kit
> - NVIDIA Jetson Orin Nano Developer Kit
>
> Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
> ---
>
> v4:
> - no changes
>
> v3:
> - added rate limiting to interrupt clearing debug logs
> - removed unnecessary braces in if blocks
> - changed dependency from I2C=y to I2C in mfd Kconfig
>
> v2:
> - removed unnecessary error logs
> - changed dev_info to dev_dbg
> - changed dev_err to dev_err_probe
> - fixed "of_match_table" assignment
>
> drivers/mfd/Kconfig | 12 ++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/nvidia-vrs-pseq.c | 267 ++++++++++++++++++++++++++++
> include/linux/mfd/nvidia-vrs-pseq.h | 127 +++++++++++++
> 4 files changed, 407 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 6fb3768e3d71..9a3451eebd6e 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1437,6 +1437,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
Suggest separating the company from the product.
If you want to use NV for this (I only see a single other instance of
this), then use NV_VRS_PSEQ.
> + tristate "NVIDIA Voltage Regulator Specification Power Sequencer"
"NVIDIA Voltage Regulator Specification (VRS) Power Sequencer"
> + depends on I2C
> + select MFD_CORE
> + select REGMAP_I2C
> + select REGMAP_IRQ
> + help
> + Say Y here to add support for NVIDIA Voltage Regulator Specification
(VRS) at the end.
> + Power Sequencer. NVVRS_PSEQ supports ON/OFF, suspend/resume sequence of
NVVRS_PSEQ seems like the wrong terminology for this paragraph.
"The NVIDIA VRS PSEQ" perhaps.
What does ON/OFF mean?
> + system power rails. It provides 32kHz RTC clock support with backup
This paragraph doesn't flow. You only have a list of 2 items here and
no connecting "and" anywhere.
"a backup battery"
> + battery for system timing.
Is it a "backup battery" if it has a primary function?
> 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 79495f9f3457..9b07289985b5 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -183,6 +183,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..cef7abac08b7
> --- /dev/null
> +++ b/drivers/mfd/nvidia-vrs-pseq.c
> @@ -0,0 +1,267 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> + * NVIDIA VRS Power Sequencer driver.
Put this at the top followed by a line separator before the copyright
line.
Drop "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>
Alphabetical.
> +
> +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),
> + },
> +};
One device is not and MFD. This is not allowed.
> +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;
This cast is superfluous.
> + struct i2c_client *client = chip->client;
> + u8 reg, val;
This line on the bottom please.
> + unsigned int i;
> + int ret = 0;
> +
> + /* Write 1 to clear the interrupt bit in the Interrupt
Properly formatted multi-line comments please.
Nit: The top line should be blank.
> + * 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) {
> + val = (u8)ret;
> + dev_dbg_ratelimited(chip->dev,
How useful is this now that it's ready for publishing?
> + "Clear IRQ reg 0x%02x=0x%02x\n",
> + reg, val);
> +
> + ret = i2c_smbus_write_byte_data(client, reg, val);
> + if (ret < 0)
> + return ret;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static 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)
> + return dev_err_probe(chip->dev, ret,
> + "Failed to read Vendor ID\n");
You use 100-chars above - why not use that everywhere to prevent these
line-breaks?
> +
> + vendor_id = (u8)ret;
All vendor IDs are supported?
Why does 'vendor_id' have to be u8?
> + ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_MODEL_REV);
> + if (ret < 0)
> + return dev_err_probe(chip->dev, ret,
> + "Failed to read Model Rev\n");
> +
> + model_rev = (u8)ret;
> +
> + if (model_rev < 0x40) {
No magic numbers. Please define them.
What if the model_rev is some larger unsupported number?
> + dev_err(chip->dev, "Chip revision 0x%02x is not supported!\n",
> + model_rev);
> + return -ENODEV;
> + }
> +
> + dev_dbg(chip->dev, "NVVRS Vendor ID: 0x%02x, Model Rev: 0x%02x\n",
> + vendor_id, model_rev);
How useful is this now, really?
> + return 0;
> +}
> +
> +static int nvvrs_pseq_probe(struct i2c_client *client)
> +{
> + const struct regmap_config *rmap_config;
"config"
Why does this need to exist at all?
> + struct nvvrs_pseq_chip *nvvrs_chip;
"ddata"
> + 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;
What are client and dev used for?
I suggest you only need one of them.
> + nvvrs_chip->chip_irq = client->irq;
Just "irq".
> + mfd_cells = nvvrs_pseq_children;
> + n_mfd_cells = ARRAY_SIZE(nvvrs_pseq_children);
Why are you placing this into another variable?
> + rmap_config = &nvvrs_pseq_regmap_config;
As above. Why have we created another local variable for this?
> + nvvrs_chip->irq_chip = &nvvrs_pseq_irq_chip;
Where is irq_chip used outside of this function?
> + nvvrs_chip->rmap = devm_regmap_init_i2c(client, rmap_config);
"regmap"
> + if (IS_ERR(nvvrs_chip->rmap))
> + return dev_err_probe(nvvrs_chip->dev, PTR_ERR(nvvrs_chip->rmap),
> + "Failed to initialise regmap\n");
> +
> + ret = nvvrs_pseq_vendor_info(nvvrs_chip);
> + if (ret < 0)
> + return ret;
dev_err_probe()
> + nvvrs_pseq_irq_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)
> + return dev_err_probe(nvvrs_chip->dev, ret,
> + "Failed to add regmap irq\n");
"IRQ Chip"
> + 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)
> + return dev_err_probe(nvvrs_chip->dev, ret,
> + "Failed to add MFD children\n");
Failed to add {children ,sub-}devices.
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
Use the helper instead.
git grep CONFIG_PM_SLEEP -- drivers/mfd
<nothing>
> +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
IRQs or The IRQ.
> + * 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
The interrupt ...
> + * will fail because I2C is still suspended.
> + */
Most of this is pretty self explanatory and implied TBH.
> + 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);
'\n' here.
> + 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 = nvvrs_dt_match,
> + },
> + .probe = nvvrs_pseq_probe,
> +};
> +
Remove this line.
> +module_i2c_driver(nvvrs_pseq_driver);
> +
> +MODULE_AUTHOR("Shubhi Garg <shgarg@nvidia.com>");
> +MODULE_DESCRIPTION("NVIDIA Voltage Regulator Specification Power Sequencer Driver");
As above.
> +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
This should be part of the comment above.
> +
> +#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;
Where is this used?
> +};
> +
> +#endif /* _MFD_NVIDIA_VRS_PSEQ_H_ */
> --
> 2.43.0
>
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 4/6] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver
2025-06-19 8:44 [PATCH v4 0/6] Add NVIDIA VRS PSEQ support Shubhi Garg
` (2 preceding siblings ...)
2025-06-19 8:44 ` [PATCH v4 3/6] mfd: nvvrs: add NVVRS PSEQ MFD driver Shubhi Garg
@ 2025-06-19 8:44 ` Shubhi Garg
2025-06-19 8:44 ` [PATCH v4 5/6] arm64: defconfig: enable NVIDIA VRS PSEQ Shubhi Garg
2025-06-19 8:44 ` [PATCH v4 6/6] MAINTAINERS: Add NVIDIA VRS PSEQ driver entry Shubhi Garg
5 siblings, 0 replies; 9+ messages in thread
From: Shubhi Garg @ 2025-06-19 8:44 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Catalin Marinas, Will Deacon, Alexandre Belloni, Jonathan Hunter
Cc: devicetree, linux-arm-kernel, linux-rtc, linux-tegra, Shubhi Garg
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>
---
v4:
- no changes
v3:
- fixed return value in RTC read_time and read_alarm functions
- fixed sizeof(*variable) inside rtc driver devm_kzalloc
- switch to devm_device_init_wakeup() for automatic cleanup
v2:
- removed regmap struct since it is not required
- removed rtc_map definition to directly use register definition
- removed unnecessary dev_err logs
- fixed dev_err logs to dev_dbg
- used rtc_lock/unlock in irq handler
- changed RTC allocation and register APIs as per latest kernel
- removed nvvrs_rtc_remove function since it's not required
drivers/rtc/Kconfig | 10 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-nvidia-vrs-pseq.c | 457 ++++++++++++++++++++++++++++++
3 files changed, 468 insertions(+)
create mode 100644 drivers/rtc/rtc-nvidia-vrs-pseq.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 9aec922613ce..d16de9b32299 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 4619aa2ac469..836e329a7c39 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -120,6 +120,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..fd57e67bda5a
--- /dev/null
+++ b/drivers/rtc/rtc-nvidia-vrs-pseq.c
@@ -0,0 +1,457 @@
+// 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 */
+
+struct nvvrs_rtc_info {
+ struct device *dev;
+ struct i2c_client *client;
+ struct rtc_device *rtc_dev;
+ unsigned int rtc_irq;
+ const struct regmap_irq_chip *rtc_irq_chip;
+ struct regmap_irq_chip_data *rtc_irq_data;
+ /* Mutex to protect RTC operations */
+ struct mutex lock;
+};
+
+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 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)
+ return ret;
+
+ val = (u8)ret;
+ val &= ~mask;
+ val |= (value & mask);
+
+ return i2c_smbus_write_byte_data(info->client, reg, val);
+}
+
+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, NVVRS_PSEQ_REG_RTC_A3, time[3]);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, NVVRS_PSEQ_REG_RTC_A2, time[2]);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, NVVRS_PSEQ_REG_RTC_A1, time[1]);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(client, NVVRS_PSEQ_REG_RTC_A0, time[0]);
+}
+
+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, NVVRS_PSEQ_REG_CTL_2,
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE,
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE);
+ if (ret < 0) {
+ dev_dbg(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, NVVRS_PSEQ_REG_CTL_2,
+ NVVRS_PSEQ_REG_CTL_2_RTC_PU,
+ NVVRS_PSEQ_REG_CTL_2_RTC_PU);
+ if (ret < 0) {
+ dev_dbg(info->dev, "Failed to set RTC_PU bit (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nvvrs_rtc_disable_alarm(struct nvvrs_rtc_info *info)
+{
+ struct i2c_client *client = info->client;
+ u8 val[4];
+ int ret;
+
+ /* Clear RTC_WAKE bit */
+ ret = nvvrs_update_bits(info, NVVRS_PSEQ_REG_CTL_2,
+ NVVRS_PSEQ_REG_CTL_2_RTC_WAKE, 0);
+ if (ret < 0) {
+ dev_dbg(info->dev, "Failed to clear RTC_WAKE bit (%d)\n", ret);
+ return ret;
+ }
+
+ /* Clear RTC_PU bit */
+ ret = nvvrs_update_bits(info, NVVRS_PSEQ_REG_CTL_2,
+ NVVRS_PSEQ_REG_CTL_2_RTC_PU, 0);
+ if (ret < 0) {
+ dev_dbg(info->dev, "Failed to clear RTC_PU bit (%d)\n", ret);
+ return ret;
+ }
+
+ /* 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_dbg(info->dev, "Failed to disable Alarm (%d)\n", ret);
+
+ 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_lock(info->rtc_dev);
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+ rtc_unlock(info->rtc_dev);
+
+ 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, NVVRS_PSEQ_REG_RTC_T3);
+ if (ret < 0)
+ goto out;
+
+ val = (u8)ret;
+ secs |= (time64_t)val << 24;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_RTC_T2);
+ if (ret < 0)
+ goto out;
+
+ val = (u8)ret;
+ secs |= (time64_t)val << 16;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_RTC_T1);
+ if (ret < 0)
+ goto out;
+
+ val = (u8)ret;
+ secs |= (time64_t)val << 8;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_RTC_T0);
+ if (ret < 0)
+ goto out;
+
+ val = (u8)ret;
+ secs |= val;
+
+ rtc_time64_to_tm(secs, tm);
+ ret = 0;
+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[4];
+ 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, NVVRS_PSEQ_REG_RTC_T3, time[3]);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, NVVRS_PSEQ_REG_RTC_T2, time[2]);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, NVVRS_PSEQ_REG_RTC_T1, time[1]);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, NVVRS_PSEQ_REG_RTC_T0, time[0]);
+
+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, NVVRS_PSEQ_REG_RTC_A3);
+ if (ret < 0)
+ goto out;
+
+ val = (u8)ret;
+ alarm_val |= (time64_t)val << 24;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_RTC_A2);
+ if (ret < 0)
+ goto out;
+
+ val = (u8)ret;
+ alarm_val |= (time64_t)val << 16;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_RTC_A1);
+ if (ret < 0)
+ goto out;
+
+ val = (u8)ret;
+ alarm_val |= (time64_t)val << 8;
+
+ ret = i2c_smbus_read_byte_data(client, NVVRS_PSEQ_REG_RTC_A0);
+ if (ret < 0)
+ 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);
+ ret = 0;
+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[4];
+ 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;
+
+ 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(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ mutex_init(&info->lock);
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Failed to get irq\n");
+
+ info->rtc_irq = ret;
+ 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->rtc_irq_chip = &nvvrs_rtc_irq_chip;
+ platform_set_drvdata(pdev, info);
+
+ /* Allocate RTC device */
+ info->rtc_dev = devm_rtc_allocate_device(info->dev);
+ if (IS_ERR(info->rtc_dev))
+ return PTR_ERR(info->rtc_dev);
+
+ info->rtc_dev->ops = &nvvrs_rtc_ops;
+ info->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ info->rtc_dev->range_max = RTC_TIMESTAMP_END_2099;
+
+ ret = devm_request_threaded_irq(info->dev, 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);
+
+ /* RTC as a wakeup source */
+ devm_device_init_wakeup(info->dev);
+
+ return devm_rtc_register_device(info->rtc_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nvvrs_rtc_suspend(struct device *dev)
+{
+ struct nvvrs_rtc_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ if (device_may_wakeup(dev)) {
+ /* Set RTC_WAKE bit for auto wake system from suspend state */
+ ret = nvvrs_update_bits(info, NVVRS_PSEQ_REG_CTL_2,
+ 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;
+ }
+
+ return enable_irq_wake(info->rtc_irq);
+ }
+
+ return 0;
+}
+
+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, NVVRS_PSEQ_REG_CTL_1,
+ 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,
+ .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.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 5/6] arm64: defconfig: enable NVIDIA VRS PSEQ
2025-06-19 8:44 [PATCH v4 0/6] Add NVIDIA VRS PSEQ support Shubhi Garg
` (3 preceding siblings ...)
2025-06-19 8:44 ` [PATCH v4 4/6] rtc: nvvrs: add NVIDIA VRS PSEQ RTC device driver Shubhi Garg
@ 2025-06-19 8:44 ` Shubhi Garg
2025-06-19 8:44 ` [PATCH v4 6/6] MAINTAINERS: Add NVIDIA VRS PSEQ driver entry Shubhi Garg
5 siblings, 0 replies; 9+ messages in thread
From: Shubhi Garg @ 2025-06-19 8:44 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Catalin Marinas, Will Deacon, Alexandre Belloni, Jonathan Hunter
Cc: devicetree, linux-arm-kernel, linux-rtc, linux-tegra, Shubhi Garg
Enable NVIDIA VRS (Voltage Regulator Specification) power sequencer
device modules. NVIDIA VRS PSEQ provides 32kHz RTC support with backup
battery for system timing. It controls ON/OFF and suspend/resume
power sequencing of system power rails on below NVIDIA platforms:
- NVIDIA Jetson AGX Orin Developer Kit
- NVIDIA IGX Orin Development Kit
- NVIDIA Jetson Orin NX Developer Kit
- NVIDIA Jetson Orin Nano Developer Kit
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
v4:
- no changes
v3:
- no changes
v2:
- moved VRS RTC config to correct place
arch/arm64/configs/defconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 96e21d458867..dbf40411e578 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -787,6 +787,7 @@ CONFIG_MFD_ROHM_BD718XX=y
CONFIG_MFD_STM32_LPTIMER=m
CONFIG_MFD_WCD934X=m
CONFIG_MFD_KHADAS_MCU=m
+CONFIG_MFD_NVVRS_PSEQ=m
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_AXP20X=y
CONFIG_REGULATOR_BD718XX=y
@@ -1265,6 +1266,7 @@ CONFIG_RTC_DRV_MT6397=m
CONFIG_RTC_DRV_XGENE=y
CONFIG_RTC_DRV_TI_K3=m
CONFIG_RTC_DRV_RENESAS_RTCA3=m
+CONFIG_RTC_DRV_NVVRS_PSEQ=m
CONFIG_DMADEVICES=y
CONFIG_DMA_BCM2835=y
CONFIG_DMA_SUN6I=m
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 6/6] MAINTAINERS: Add NVIDIA VRS PSEQ driver entry
2025-06-19 8:44 [PATCH v4 0/6] Add NVIDIA VRS PSEQ support Shubhi Garg
` (4 preceding siblings ...)
2025-06-19 8:44 ` [PATCH v4 5/6] arm64: defconfig: enable NVIDIA VRS PSEQ Shubhi Garg
@ 2025-06-19 8:44 ` Shubhi Garg
5 siblings, 0 replies; 9+ messages in thread
From: Shubhi Garg @ 2025-06-19 8:44 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Catalin Marinas, Will Deacon, Alexandre Belloni, Jonathan Hunter
Cc: devicetree, linux-arm-kernel, linux-rtc, linux-tegra, Shubhi Garg
Add NVIDIA VRS (Voltage Regulator Specification) power sequencer driver
entry in MAINTAINERS.
Signed-off-by: Shubhi Garg <shgarg@nvidia.com>
---
v4:
- no changes
v3:
- fixed indentation
v2:
- this is a new patch in V2
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 9165216d0cc0..11030db537ff 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17846,6 +17846,15 @@ S: Maintained
F: drivers/video/fbdev/nvidia/
F: drivers/video/fbdev/riva/
+NVIDIA VRS POWER SEQUENCER
+M: Shubhi Garg <shgarg@nvidia.com>
+L: linux-tegra@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/mfd/nvidia,vrs-pseq.yaml
+F: drivers/mfd/nvidia-vrs-pseq.c
+F: drivers/rtc/rtc-nvidia-vrs-pseq.c
+F: include/linux/mfd/nvidia-vrs-pseq.h
+
NVIDIA WMI EC BACKLIGHT DRIVER
M: Daniel Dadap <ddadap@nvidia.com>
L: platform-driver-x86@vger.kernel.org
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread