[PATCH v4 4/9] regulator: renesas-usb-vbus-regulator: Add RZ/G3L VBUS regulator support

From: Biju

Date: Thu Jul 09 2026 - 04:36:39 EST


From: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>

Add dual USB VBUS regulator support for the RZ/G3L (r9a08g046) SoC, which
has two OTG controllers (one per port), unlike RZ/G3S which has only one.

Introduce a RZG3L_USB_VBUS_REG macro and a rzg3l_usb_vbus_regulators
array with two descriptors: vbus0 (BIT(0)) and vbus1 (BIT(1)), both
sourced from a regulators sub-node as defined in the binding. Add a
dedicated rzg3l_usb_vbus_regulator_probe() that iterates over the array
and registers both regulators using devm_regulator_register().

Convert the existing platform driver to use an id_table, allowing it to
dispatch to either the RZ/G2L or RZ/G3L probe function based on the
matched platform device name.

Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
v3->v4:
* Dropped rzg3l specific platform driver.
* Dropped the tag as there are new changes
* Added id_table handling by introducing a common probe().
v2->v3:
* No change.
v1->v2:
* Passing pointer to an array of regulators to make it scalable.
* Updated commit description.
---
.../regulator/renesas-usb-vbus-regulator.c | 74 ++++++++++++++++++-
1 file changed, 73 insertions(+), 1 deletion(-)

diff --git a/drivers/regulator/renesas-usb-vbus-regulator.c b/drivers/regulator/renesas-usb-vbus-regulator.c
index 9ba791bd72ec..493397143617 100644
--- a/drivers/regulator/renesas-usb-vbus-regulator.c
+++ b/drivers/regulator/renesas-usb-vbus-regulator.c
@@ -55,8 +55,80 @@ static int rzg2l_usb_vbus_regulator_probe(struct platform_device *pdev)
return 0;
}

+#define RZG3L_USB_VBUS_REG(rname, en_mask) \
+ { \
+ .name = #rname, \
+ .of_match = of_match_ptr(#rname), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .ops = &rzg2l_usb_vbus_reg_ops, \
+ .enable_reg = 0, \
+ .enable_mask = (en_mask), \
+ .enable_is_inverted = true, \
+ .fixed_uV = 5000000, \
+ .n_voltages = 1, \
+ }
+
+static const struct regulator_desc rzg3l_usb_vbus_regulators[] = {
+ RZG3L_USB_VBUS_REG(vbus0, BIT(0)),
+ RZG3L_USB_VBUS_REG(vbus1, BIT(1)),
+};
+
+static int rzg3l_usb_vbus_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_config config = { };
+ struct device *dev = &pdev->dev;
+ struct regulator_dev *rdev;
+
+ config.dev = pdev->dev.parent;
+ config.regmap = dev_get_regmap(dev->parent, NULL);
+ if (!config.regmap)
+ return dev_err_probe(dev, -ENOENT, "Failed to get regmap\n");
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(rzg3l_usb_vbus_regulators); i++) {
+ rdev = devm_regulator_register(dev, &rzg3l_usb_vbus_regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s regulator\n",
+ rzg3l_usb_vbus_regulators[i].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static int rzg2l_usb_vbus_regulator_common_probe(struct platform_device *pdev)
+{
+ int (*probe_func)(struct platform_device *pdev);
+ const struct platform_device_id *id;
+
+ id = platform_get_device_id(pdev);
+ if (!id)
+ return dev_err_probe(&pdev->dev, -ENODEV, "No ID match found\n");
+
+ probe_func = (int (*)(struct platform_device *))id->driver_data;
+
+ return probe_func(pdev);
+}
+
+static const struct platform_device_id rzg2l_usb_vbus_regulator_ids[] = {
+ {
+ .name = "rzg2l-vbus-regulator",
+ .driver_data = (kernel_ulong_t)rzg2l_usb_vbus_regulator_probe
+ },
+ {
+ .name = "rzg3l-vbus-regulator",
+ .driver_data = (kernel_ulong_t)rzg3l_usb_vbus_regulator_probe
+ },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, rzg2l_usb_vbus_regulator_ids);
+
static struct platform_driver rzg2l_usb_vbus_regulator_driver = {
- .probe = rzg2l_usb_vbus_regulator_probe,
+ .probe = rzg2l_usb_vbus_regulator_common_probe,
+ .id_table = rzg2l_usb_vbus_regulator_ids,
.driver = {
.name = "rzg2l-usb-vbus-regulator",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
--
2.43.0