diff mbox series

[v4,2/3] reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)

Message ID 20250414130020.248374-3-prabhakar.mahadev-lad.rj@bp.renesas.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show
Series Add USB2PHY Port Reset Control driver for Renesas RZ/V2H(P) SoC | expand

Commit Message

Lad, Prabhakar April 14, 2025, 1 p.m. UTC
From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Implement a USB2PHY port reset driver for the Renesas RZ/V2H(P) SoC.
Enable control of USB2.0 PHY reset and power-down operations, including
assert and deassert functionalities for the PHY.

Leverage device tree (OF) data to support future SoCs with similar USB2PHY
hardware but varying register configurations. Define initialization values
and control register settings to ensure flexibility for upcoming platforms.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
---
 drivers/reset/Kconfig               |   7 +
 drivers/reset/Makefile              |   1 +
 drivers/reset/reset-rzv2h-usb2phy.c | 241 ++++++++++++++++++++++++++++
 3 files changed, 249 insertions(+)
 create mode 100644 drivers/reset/reset-rzv2h-usb2phy.c

Comments

Biju Das April 14, 2025, 1:05 p.m. UTC | #1
Hi Prabhakar,

Thanks for the patch.

> -----Original Message-----
> From: Prabhakar <prabhakar.csengg@gmail.com>
> Sent: 14 April 2025 14:00
> Subject: [PATCH v4 2/3] reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)
> 
> From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> 
> Implement a USB2PHY port reset driver for the Renesas RZ/V2H(P) SoC.
> Enable control of USB2.0 PHY reset and power-down operations, including assert and deassert
> functionalities for the PHY.
> 
> Leverage device tree (OF) data to support future SoCs with similar USB2PHY hardware but varying
> register configurations. Define initialization values and control register settings to ensure
> flexibility for upcoming platforms.
> 
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> ---
>  drivers/reset/Kconfig               |   7 +
>  drivers/reset/Makefile              |   1 +
>  drivers/reset/reset-rzv2h-usb2phy.c | 241 ++++++++++++++++++++++++++++
>  3 files changed, 249 insertions(+)
>  create mode 100644 drivers/reset/reset-rzv2h-usb2phy.c
> 
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 99f6f9784e68..6c7dbd8ca580 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -225,6 +225,13 @@ config RESET_RZG2L_USBPHY_CTRL
>  	  Support for USBPHY Control found on RZ/G2L family. It mainly
>  	  controls reset and power down of the USB/PHY.
> 
> +config RESET_RZV2H_USB2PHY
> +	tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
> +	depends on ARCH_RENESAS || COMPILE_TEST
> +	help
> +	  Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
> +	  (and similar SoCs).
> +
>  config RESET_SCMI
>  	tristate "Reset driver controlled via ARM SCMI interface"
>  	depends on ARM_SCMI_PROTOCOL || COMPILE_TEST diff --git a/drivers/reset/Makefile
> b/drivers/reset/Makefile index 31f9904d13f9..30d0fb4ac965 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
>  obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
>  obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
>  obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
> +obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
>  obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
>  obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
>  obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o diff --git a/drivers/reset/reset-rzv2h-usb2phy.c
> b/drivers/reset/reset-rzv2h-usb2phy.c
> new file mode 100644
> index 000000000000..737b768829c9
> --- /dev/null
> +++ b/drivers/reset/reset-rzv2h-usb2phy.c
> @@ -0,0 +1,241 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Renesas RZ/V2H(P) USB2PHY Port reset control driver
> + *
> + * Copyright (C) 2025 Renesas Electronics Corporation  */
> +
> +#include <linux/cleanup.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +#include <linux/reset-controller.h>
> +
> +struct rzv2h_usb2phy_regval {
> +	u16 reg;
> +	u16 val;
> +};
> +
> +struct rzv2h_usb2phy_reset_of_data {
> +	const struct rzv2h_usb2phy_regval *init_vals;
> +	unsigned int init_val_count;
> +
> +	u16 reset_reg;
> +	u16 reset_assert_val;
> +	u16 reset_deassert_val;
> +	u16 reset_status_bits;
> +	u16 reset_release_val;
> +
> +	u16 reset2_reg;
> +	u16 reset2_acquire_val;
> +	u16 reset2_release_val;
> +};
> +
> +struct rzv2h_usb2phy_reset_priv {
> +	const struct rzv2h_usb2phy_reset_of_data *data;
> +	void __iomem *base;
> +	struct device *dev;
> +	struct reset_controller_dev rcdev;
> +	spinlock_t lock; /* protects register accesses */ };
> +
> +static inline struct rzv2h_usb2phy_reset_priv
> +*rzv2h_usbphy_rcdev_to_priv(struct reset_controller_dev *rcdev) {
> +	return container_of(rcdev, struct rzv2h_usb2phy_reset_priv, rcdev); }
> +
> +/* This function must be called only after pm_runtime_resume_and_get()
> +has been called */ static void rzv2h_usbphy_assert_helper(struct
> +rzv2h_usb2phy_reset_priv *priv) {
> +	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
> +
> +	scoped_guard(spinlock, &priv->lock) {
> +		writel(data->reset2_acquire_val, priv->base + data->reset2_reg);
> +		writel(data->reset_assert_val, priv->base + data->reset_reg);
> +	}
> +
> +	usleep_range(11, 20);
> +}
> +
> +static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
> +				     unsigned long id)
> +{
> +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> +	struct device *dev = priv->dev;
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret) {
> +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> +		return ret;
> +	}
> +
> +	rzv2h_usbphy_assert_helper(priv);
> +
> +	pm_runtime_put(dev);
> +
> +	return 0;
> +}
> +
> +static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
> +				       unsigned long id)
> +{
> +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> +	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
> +	struct device *dev = priv->dev;
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret) {
> +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> +		return ret;
> +	}
> +
> +	scoped_guard(spinlock, &priv->lock) {
> +		writel(data->reset_deassert_val, priv->base + data->reset_reg);
> +		writel(data->reset2_release_val, priv->base + data->reset2_reg);
> +		writel(data->reset_release_val, priv->base + data->reset_reg);
> +	}
> +
> +	pm_runtime_put(dev);
> +
> +	return 0;
> +}
> +
> +static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
> +				     unsigned long id)
> +{
> +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> +	struct device *dev = priv->dev;
> +	int ret;
> +	u32 reg;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret) {
> +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> +		return ret;
> +	}
> +
> +	reg = readl(priv->base + priv->data->reset_reg);
> +
> +	pm_runtime_put(dev);
> +
> +	return (reg & priv->data->reset_status_bits) ==
> +priv->data->reset_status_bits; }
> +
> +static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
> +	.assert = rzv2h_usbphy_reset_assert,
> +	.deassert = rzv2h_usbphy_reset_deassert,
> +	.status = rzv2h_usbphy_reset_status,
> +};
> +
> +static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
> +					const struct of_phandle_args *reset_spec) {
> +	/* No special handling needed, we have only one reset line per device */
> +	return 0;
> +}
> +
> +static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) {
> +	const struct rzv2h_usb2phy_reset_of_data *data;
> +	struct rzv2h_usb2phy_reset_priv *priv;
> +	struct device *dev = &pdev->dev;
> +	struct reset_control *rstc;
> +	int error;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	data = of_device_get_match_data(dev);
> +	if (!data)
> +		return dev_err_probe(dev, -ENODEV,
> +				     "failed to match device\n");

This check is not needed as there is always data.

With this addressed.

Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>

Cheers,
Biju

> +
> +	priv->data = data;
> +	priv->dev = dev;
> +	priv->base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
> +	if (IS_ERR(rstc))
> +		return dev_err_probe(dev, PTR_ERR(rstc),
> +				     "failed to get deasserted reset\n");
> +
> +	spin_lock_init(&priv->lock);
> +	dev_set_drvdata(dev, priv);
> +
> +	error = devm_pm_runtime_enable(dev);
> +	if (error)
> +		return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
> +
> +	error = pm_runtime_resume_and_get(dev);
> +	if (error)
> +		return dev_err_probe(dev, error, "pm_runtime_resume_and_get
> +failed\n");
> +
> +	for (unsigned int i = 0; i < data->init_val_count; i++)
> +		writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
> +
> +	/* keep usb2phy in asserted state */
> +	rzv2h_usbphy_assert_helper(priv);
> +
> +	pm_runtime_put(dev);
> +
> +	priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
> +	priv->rcdev.of_reset_n_cells = 0;
> +	priv->rcdev.nr_resets = 1;
> +	priv->rcdev.of_xlate = rzv2h_usb2phy_reset_of_xlate;
> +	priv->rcdev.of_node = dev->of_node;
> +	priv->rcdev.dev = dev;
> +
> +	return devm_reset_controller_register(dev, &priv->rcdev); }
> +
> +/*
> + * initialization values required to prepare the PHY to receive
> + * assert and deassert requests.
> + */
> +static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = {
> +	{ .reg = 0xc10, .val = 0x67c },
> +	{ .reg = 0xc14, .val = 0x1f },
> +	{ .reg = 0x600, .val = 0x909 },
> +};
> +
> +static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = {
> +	.init_vals = rzv2h_init_vals,
> +	.init_val_count = ARRAY_SIZE(rzv2h_init_vals),
> +	.reset_reg = 0,
> +	.reset_assert_val = 0x206,
> +	.reset_status_bits = BIT(2),
> +	.reset_deassert_val = 0x200,
> +	.reset_release_val = 0x0,
> +	.reset2_reg = 0xb04,
> +	.reset2_acquire_val = 0x303,
> +	.reset2_release_val = 0x3,
> +};
> +
> +static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = {
> +	{ .compatible = "renesas,r9a09g057-usb2phy-reset", .data = &rzv2h_reset_of_data },
> +	{ /* Sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, rzv2h_usb2phy_reset_of_match);
> +
> +static struct platform_driver rzv2h_usb2phy_reset_driver = {
> +	.driver = {
> +		.name		= "rzv2h_usb2phy_reset",
> +		.of_match_table	= rzv2h_usb2phy_reset_of_match,
> +	},
> +	.probe = rzv2h_usb2phy_reset_probe,
> +};
> +module_platform_driver(rzv2h_usb2phy_reset_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Lad Prabhakar
> +<prabhakar.mahadev-lad.rj@bp.renesas.com>");
> +MODULE_DESCRIPTION("Renesas RZ/V2H(P) USB2PHY Control");
> --
> 2.49.0
Biju Das April 14, 2025, 1:12 p.m. UTC | #2
> -----Original Message-----
> From: Biju Das <biju.das.jz@bp.renesas.com>
> Sent: 14 April 2025 14:05
> To: Prabhakar <prabhakar.csengg@gmail.com>; Philipp Zabel <p.zabel@pengutronix.de>; Rob Herring
> <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Geert
> Uytterhoeven <geert+renesas@glider.be>; Magnus Damm <magnus.damm@gmail.com>
> Cc: linux-renesas-soc@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org;
> Fabrizio Castro <fabrizio.castro.jz@renesas.com>; Prabhakar Mahadev Lad <prabhakar.mahadev-
> lad.rj@bp.renesas.com>
> Subject: RE: [PATCH v4 2/3] reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)
> 
> Hi Prabhakar,
> 
> Thanks for the patch.
> 
> > -----Original Message-----
> > From: Prabhakar <prabhakar.csengg@gmail.com>
> > Sent: 14 April 2025 14:00
> > Subject: [PATCH v4 2/3] reset: Add USB2PHY port reset driver for
> > Renesas RZ/V2H(P)
> >
> > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Implement a USB2PHY port reset driver for the Renesas RZ/V2H(P) SoC.
> > Enable control of USB2.0 PHY reset and power-down operations,
> > including assert and deassert functionalities for the PHY.
> >
> > Leverage device tree (OF) data to support future SoCs with similar
> > USB2PHY hardware but varying register configurations. Define
> > initialization values and control register settings to ensure flexibility for upcoming platforms.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > ---
> >  drivers/reset/Kconfig               |   7 +
> >  drivers/reset/Makefile              |   1 +
> >  drivers/reset/reset-rzv2h-usb2phy.c | 241
> > ++++++++++++++++++++++++++++
> >  3 files changed, 249 insertions(+)
> >  create mode 100644 drivers/reset/reset-rzv2h-usb2phy.c
> >
> > diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index
> > 99f6f9784e68..6c7dbd8ca580 100644
> > --- a/drivers/reset/Kconfig
> > +++ b/drivers/reset/Kconfig
> > @@ -225,6 +225,13 @@ config RESET_RZG2L_USBPHY_CTRL
> >  	  Support for USBPHY Control found on RZ/G2L family. It mainly
> >  	  controls reset and power down of the USB/PHY.
> >
> > +config RESET_RZV2H_USB2PHY
> > +	tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
> > +	depends on ARCH_RENESAS || COMPILE_TEST
> > +	help
> > +	  Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
> > +	  (and similar SoCs).
> > +
> >  config RESET_SCMI
> >  	tristate "Reset driver controlled via ARM SCMI interface"
> >  	depends on ARM_SCMI_PROTOCOL || COMPILE_TEST diff --git
> > a/drivers/reset/Makefile b/drivers/reset/Makefile index
> > 31f9904d13f9..30d0fb4ac965 100644
> > --- a/drivers/reset/Makefile
> > +++ b/drivers/reset/Makefile
> > @@ -31,6 +31,7 @@ obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
> >  obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
> >  obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
> >  obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
> > +obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
> >  obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
> >  obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
> >  obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o diff --git
> > a/drivers/reset/reset-rzv2h-usb2phy.c
> > b/drivers/reset/reset-rzv2h-usb2phy.c
> > new file mode 100644
> > index 000000000000..737b768829c9
> > --- /dev/null
> > +++ b/drivers/reset/reset-rzv2h-usb2phy.c
> > @@ -0,0 +1,241 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Renesas RZ/V2H(P) USB2PHY Port reset control driver
> > + *
> > + * Copyright (C) 2025 Renesas Electronics Corporation  */
> > +
> > +#include <linux/cleanup.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/reset.h>
> > +#include <linux/reset-controller.h>
> 
> > +
> > +	priv->data = data;
> > +	priv->dev = dev;
> > +	priv->base = devm_platform_ioremap_resource(pdev, 0);
> > +	if (IS_ERR(priv->base))
> > +		return PTR_ERR(priv->base);
> > +
> > +	rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
> > +	if (IS_ERR(rstc))
> > +		return dev_err_probe(dev, PTR_ERR(rstc),
> > +				     "failed to get deasserted reset\n");
> > +
> > +	spin_lock_init(&priv->lock);
> > +	dev_set_drvdata(dev, priv);

Also looks it is unused.

Cheers,
Biju
ALOK TIWARI April 14, 2025, 1:46 p.m. UTC | #3
> +static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
> +				     unsigned long id)
> +{
> +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> +	struct device *dev = priv->dev;
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret) {

nit: it will good if we check similar to reset-rzg2l-usbphy-ctrl.c
pm_runtime_resume_and_get -> 0 on success, or a negative error code 
otherwise.
1 → if the device was resumed and incremented usage count
0 → if the device was already active or successfully resumed
if (ret < 0)

> +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> +		return ret;
> +	}
> +
> +	rzv2h_usbphy_assert_helper(priv);
> +
> +	pm_runtime_put(dev);
> +
> +	return 0;
> +}
> +
> +static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
> +				       unsigned long id)
> +{
> +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> +	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
> +	struct device *dev = priv->dev;
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);

pm_runtime_resume_and_get -> 0 on success, or a negative error code 
otherwise.
if (ret < 0)

> +	if (ret) {
> +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> +		return ret;
> +	}
> +
> +	scoped_guard(spinlock, &priv->lock) {
> +		writel(data->reset_deassert_val, priv->base + data->reset_reg);
> +		writel(data->reset2_release_val, priv->base + data->reset2_reg);
> +		writel(data->reset_release_val, priv->base + data->reset_reg);
> +	}
> +
> +	pm_runtime_put(dev);
> +
> +	return 0;
> +}
> +
> +static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
> +				     unsigned long id)
> +{
> +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> +	struct device *dev = priv->dev;
> +	int ret;
> +	u32 reg;
> +
> +	ret = pm_runtime_resume_and_get(dev);

pm_runtime_resume_and_get -> 0 on success, or a negative error code 
otherwise.
if (ret < 0)

> +	if (ret) {
> +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> +		return ret;
> +	}
> +
> +	reg = readl(priv->base + priv->data->reset_reg);
> +
> +	pm_runtime_put(dev);
> +
> +	return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits;
> +}
> +
> +static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
> +	.assert = rzv2h_usbphy_reset_assert,
> +	.deassert = rzv2h_usbphy_reset_deassert,
> +	.status = rzv2h_usbphy_reset_status,
> +};
> +
> +static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
> +					const struct of_phandle_args *reset_spec)
> +{
> +	/* No special handling needed, we have only one reset line per device */
> +	return 0;
> +}
> +
> +static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
> +{
> +	const struct rzv2h_usb2phy_reset_of_data *data;
> +	struct rzv2h_usb2phy_reset_priv *priv;
> +	struct device *dev = &pdev->dev;
> +	struct reset_control *rstc;
> +	int error;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	data = of_device_get_match_data(dev);
> +	if (!data)
> +		return dev_err_probe(dev, -ENODEV,
> +				     "failed to match device\n");
> +
> +	priv->data = data;
> +	priv->dev = dev;
> +	priv->base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
> +	if (IS_ERR(rstc))
> +		return dev_err_probe(dev, PTR_ERR(rstc),
> +				     "failed to get deasserted reset\n");
> +
> +	spin_lock_init(&priv->lock);
> +	dev_set_drvdata(dev, priv);
> +
> +	error = devm_pm_runtime_enable(dev);
> +	if (error)
> +		return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
> +
> +	error = pm_runtime_resume_and_get(dev); 

nit: it will good if we check similar to reset-rzg2l-usbphy-ctrl.c
pm_runtime_resume_and_get -> 0 on success, or a negative error code 
otherwise.
if (error < 0)

> +	if (error)
> +		return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n");
> +
> +	for (unsigned int i = 0; i < data->init_val_count; i++)
> +		writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
> +
> +	/* keep usb2phy in asserted state */
> +	rzv2h_usbphy_assert_helper(priv);
> +
> +	pm_runtime_put(dev);
> +
> +	priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
> +	priv->rcdev.of_reset_n_cells = 0;
> +	priv->rcdev.nr_resets = 1;
> +	priv->rcdev.of_xlate = rzv2h_usb2phy_reset_of_xlate;
> +	priv->rcdev.of_node = dev->of_node;
> +	priv->rcdev.dev = dev;
> +
> +	return devm_reset_controller_register(dev, &priv->rcdev);
> +}
> +


Thanks
Alok
Fabrizio Castro April 14, 2025, 3:43 p.m. UTC | #4
Hi Alok,

Thanks for your email.

> From: ALOK TIWARI <alok.a.tiwari@oracle.com>
> Sent: 14 April 2025 14:46
> Subject: Re: [PATCH v4 2/3] reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)
> 
> 
> > +static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
> > +				     unsigned long id)
> > +{
> > +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> > +	struct device *dev = priv->dev;
> > +	int ret;
> > +
> > +	ret = pm_runtime_resume_and_get(dev);
> > +	if (ret) {
> 
> nit: it will good if we check similar to reset-rzg2l-usbphy-ctrl.c
> pm_runtime_resume_and_get -> 0 on success, or a negative error code
> otherwise.
> 1 → if the device was resumed and incremented usage count
> 0 → if the device was already active or successfully resumed
> if (ret < 0)

No.

As you can see from:
https://github.com/torvalds/linux/blob/master/include/linux/pm_runtime.h#L444

pm_runtime_resume_and_get returns a negative error code or 0 (when
successful).

The same explanation applies to your other comments.


Kind regards,
Fab

> 
> > +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> > +		return ret;
> > +	}
> > +
> > +	rzv2h_usbphy_assert_helper(priv);
> > +
> > +	pm_runtime_put(dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
> > +				       unsigned long id)
> > +{
> > +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> > +	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
> > +	struct device *dev = priv->dev;
> > +	int ret;
> > +
> > +	ret = pm_runtime_resume_and_get(dev);
> 
> pm_runtime_resume_and_get -> 0 on success, or a negative error code
> otherwise.
> if (ret < 0)
> 
> > +	if (ret) {
> > +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> > +		return ret;
> > +	}
> > +
> > +	scoped_guard(spinlock, &priv->lock) {
> > +		writel(data->reset_deassert_val, priv->base + data->reset_reg);
> > +		writel(data->reset2_release_val, priv->base + data->reset2_reg);
> > +		writel(data->reset_release_val, priv->base + data->reset_reg);
> > +	}
> > +
> > +	pm_runtime_put(dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
> > +				     unsigned long id)
> > +{
> > +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> > +	struct device *dev = priv->dev;
> > +	int ret;
> > +	u32 reg;
> > +
> > +	ret = pm_runtime_resume_and_get(dev);
> 
> pm_runtime_resume_and_get -> 0 on success, or a negative error code
> otherwise.
> if (ret < 0)
> 
> > +	if (ret) {
> > +		dev_err(dev, "pm_runtime_resume_and_get failed\n");
> > +		return ret;
> > +	}
> > +
> > +	reg = readl(priv->base + priv->data->reset_reg);
> > +
> > +	pm_runtime_put(dev);
> > +
> > +	return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits;
> > +}
> > +
> > +static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
> > +	.assert = rzv2h_usbphy_reset_assert,
> > +	.deassert = rzv2h_usbphy_reset_deassert,
> > +	.status = rzv2h_usbphy_reset_status,
> > +};
> > +
> > +static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
> > +					const struct of_phandle_args *reset_spec)
> > +{
> > +	/* No special handling needed, we have only one reset line per device */
> > +	return 0;
> > +}
> > +
> > +static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
> > +{
> > +	const struct rzv2h_usb2phy_reset_of_data *data;
> > +	struct rzv2h_usb2phy_reset_priv *priv;
> > +	struct device *dev = &pdev->dev;
> > +	struct reset_control *rstc;
> > +	int error;
> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	data = of_device_get_match_data(dev);
> > +	if (!data)
> > +		return dev_err_probe(dev, -ENODEV,
> > +				     "failed to match device\n");
> > +
> > +	priv->data = data;
> > +	priv->dev = dev;
> > +	priv->base = devm_platform_ioremap_resource(pdev, 0);
> > +	if (IS_ERR(priv->base))
> > +		return PTR_ERR(priv->base);
> > +
> > +	rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
> > +	if (IS_ERR(rstc))
> > +		return dev_err_probe(dev, PTR_ERR(rstc),
> > +				     "failed to get deasserted reset\n");
> > +
> > +	spin_lock_init(&priv->lock);
> > +	dev_set_drvdata(dev, priv);
> > +
> > +	error = devm_pm_runtime_enable(dev);
> > +	if (error)
> > +		return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
> > +
> > +	error = pm_runtime_resume_and_get(dev);
> 
> nit: it will good if we check similar to reset-rzg2l-usbphy-ctrl.c
> pm_runtime_resume_and_get -> 0 on success, or a negative error code
> otherwise.
> if (error < 0)
> 
> > +	if (error)
> > +		return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n");
> > +
> > +	for (unsigned int i = 0; i < data->init_val_count; i++)
> > +		writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
> > +
> > +	/* keep usb2phy in asserted state */
> > +	rzv2h_usbphy_assert_helper(priv);
> > +
> > +	pm_runtime_put(dev);
> > +
> > +	priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
> > +	priv->rcdev.of_reset_n_cells = 0;
> > +	priv->rcdev.nr_resets = 1;
> > +	priv->rcdev.of_xlate = rzv2h_usb2phy_reset_of_xlate;
> > +	priv->rcdev.of_node = dev->of_node;
> > +	priv->rcdev.dev = dev;
> > +
> > +	return devm_reset_controller_register(dev, &priv->rcdev);
> > +}
> > +
> 
> 
> Thanks
> Alok
ALOK TIWARI April 14, 2025, 4:51 p.m. UTC | #5
Hi Fabrizio,

On 14-04-2025 21:13, Fabrizio Castro wrote:
> Hi Alok,
> 
> Thanks for your email.
> 
>> From: ALOK TIWARI <alok.a.tiwari@oracle.com>
>> Sent: 14 April 2025 14:46
>> Subject: Re: [PATCH v4 2/3] reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)
>>
>>
>>> +static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
>>> +				     unsigned long id)
>>> +{
>>> +	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
>>> +	struct device *dev = priv->dev;
>>> +	int ret;
>>> +
>>> +	ret = pm_runtime_resume_and_get(dev);
>>> +	if (ret) {
>>
>> nit: it will good if we check similar to reset-rzg2l-usbphy-ctrl.c
>> pm_runtime_resume_and_get -> 0 on success, or a negative error code
>> otherwise.
>> 1 → if the device was resumed and incremented usage count
>> 0 → if the device was already active or successfully resumed
>> if (ret < 0)
> 
> No.
> 
> As you can see from:
> https://urldefense.com/v3/__https://github.com/torvalds/linux/blob/master/include/linux/pm_runtime.h*L444__;Iw!!ACWV5N9M2RV99hQ!Ly8gpEBQHhYXOeCcKQavVHfM1XUSy1IubKnHjuQAgvfkK0jrMXc0ebBcvFRvNDcpaJwoUOk1JLLuzih2fLd7JReyapWOouY$
> 
> pm_runtime_resume_and_get returns a negative error code or 0 (when
> successful).
> 
> The same explanation applies to your other comments.
> 

Thanks to you for the explanation.
I got you point.

so We are keeping different styles of error checks:
In reset-rzv2h-usb2phy.c, we check using if (error),
Whereas in reset-rzg2l-usbphy-ctrl.c, we use if (error < 0)."
https://github.com/torvalds/linux/blob/master/drivers/reset/reset-rzg2l-usbphy-ctrl.c#L148
That's what I conclude for now.

> 
> Kind regards,
> Fab
> 



Thanks,
Alok
Lad, Prabhakar April 14, 2025, 5:43 p.m. UTC | #6
Hi Biju,

Thank you for the review.

On Mon, Apr 14, 2025 at 2:05 PM Biju Das <biju.das.jz@bp.renesas.com> wrote:
>
> Hi Prabhakar,
>
> Thanks for the patch.
>
> > -----Original Message-----
> > From: Prabhakar <prabhakar.csengg@gmail.com>
> > Sent: 14 April 2025 14:00
> > Subject: [PATCH v4 2/3] reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)
> >
> > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Implement a USB2PHY port reset driver for the Renesas RZ/V2H(P) SoC.
> > Enable control of USB2.0 PHY reset and power-down operations, including assert and deassert
> > functionalities for the PHY.
> >
> > Leverage device tree (OF) data to support future SoCs with similar USB2PHY hardware but varying
> > register configurations. Define initialization values and control register settings to ensure
> > flexibility for upcoming platforms.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > ---
> >  drivers/reset/Kconfig               |   7 +
> >  drivers/reset/Makefile              |   1 +
> >  drivers/reset/reset-rzv2h-usb2phy.c | 241 ++++++++++++++++++++++++++++
> >  3 files changed, 249 insertions(+)
> >  create mode 100644 drivers/reset/reset-rzv2h-usb2phy.c
> >
> > diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 99f6f9784e68..6c7dbd8ca580 100644
> > --- a/drivers/reset/Kconfig
> > +++ b/drivers/reset/Kconfig
> > @@ -225,6 +225,13 @@ config RESET_RZG2L_USBPHY_CTRL
> >         Support for USBPHY Control found on RZ/G2L family. It mainly
> >         controls reset and power down of the USB/PHY.
> >
> > +config RESET_RZV2H_USB2PHY
> > +     tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
> > +     depends on ARCH_RENESAS || COMPILE_TEST
> > +     help
> > +       Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
> > +       (and similar SoCs).
> > +
> >  config RESET_SCMI
> >       tristate "Reset driver controlled via ARM SCMI interface"
> >       depends on ARM_SCMI_PROTOCOL || COMPILE_TEST diff --git a/drivers/reset/Makefile
> > b/drivers/reset/Makefile index 31f9904d13f9..30d0fb4ac965 100644
> > --- a/drivers/reset/Makefile
> > +++ b/drivers/reset/Makefile
> > @@ -31,6 +31,7 @@ obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
> >  obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
> >  obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
> >  obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
> > +obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
> >  obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
> >  obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
> >  obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o diff --git a/drivers/reset/reset-rzv2h-usb2phy.c
> > b/drivers/reset/reset-rzv2h-usb2phy.c
> > new file mode 100644
> > index 000000000000..737b768829c9
> > --- /dev/null
> > +++ b/drivers/reset/reset-rzv2h-usb2phy.c
> > @@ -0,0 +1,241 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Renesas RZ/V2H(P) USB2PHY Port reset control driver
> > + *
> > + * Copyright (C) 2025 Renesas Electronics Corporation  */
> > +
> > +#include <linux/cleanup.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/reset.h>
> > +#include <linux/reset-controller.h>
> > +
> > +struct rzv2h_usb2phy_regval {
> > +     u16 reg;
> > +     u16 val;
> > +};
> > +
> > +struct rzv2h_usb2phy_reset_of_data {
> > +     const struct rzv2h_usb2phy_regval *init_vals;
> > +     unsigned int init_val_count;
> > +
> > +     u16 reset_reg;
> > +     u16 reset_assert_val;
> > +     u16 reset_deassert_val;
> > +     u16 reset_status_bits;
> > +     u16 reset_release_val;
> > +
> > +     u16 reset2_reg;
> > +     u16 reset2_acquire_val;
> > +     u16 reset2_release_val;
> > +};
> > +
<snip>
> > +static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
> > +     .assert = rzv2h_usbphy_reset_assert,
> > +     .deassert = rzv2h_usbphy_reset_deassert,
> > +     .status = rzv2h_usbphy_reset_status,
> > +};
> > +
> > +static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
> > +                                     const struct of_phandle_args *reset_spec) {
> > +     /* No special handling needed, we have only one reset line per device */
> > +     return 0;
> > +}
> > +
> > +static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) {
> > +     const struct rzv2h_usb2phy_reset_of_data *data;
> > +     struct rzv2h_usb2phy_reset_priv *priv;
> > +     struct device *dev = &pdev->dev;
> > +     struct reset_control *rstc;
> > +     int error;
> > +
> > +     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +     if (!priv)
> > +             return -ENOMEM;
> > +
> > +     data = of_device_get_match_data(dev);
> > +     if (!data)
> > +             return dev_err_probe(dev, -ENODEV,
> > +                                  "failed to match device\n");
>
> This check is not needed as there is always data.
>
Agreed, I will drop this.

> > +
> > +     priv->data = data;
> > +     priv->dev = dev;
> > +     priv->base = devm_platform_ioremap_resource(pdev, 0);
> > +     if (IS_ERR(priv->base))
> > +             return PTR_ERR(priv->base);
> > +
> > +     rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
> > +     if (IS_ERR(rstc))
> > +             return dev_err_probe(dev, PTR_ERR(rstc),
> > +                                  "failed to get deasserted reset\n");
> > +
> > +     spin_lock_init(&priv->lock);
> > +     dev_set_drvdata(dev, priv);
and this too and send a new version.


Cheers,
Prabhakar
Lad, Prabhakar April 14, 2025, 5:46 p.m. UTC | #7
Hi ALOK,

On Mon, Apr 14, 2025 at 5:51 PM ALOK TIWARI <alok.a.tiwari@oracle.com> wrote:
>
> Hi Fabrizio,
>
> On 14-04-2025 21:13, Fabrizio Castro wrote:
> > Hi Alok,
> >
> > Thanks for your email.
> >
> >> From: ALOK TIWARI <alok.a.tiwari@oracle.com>
> >> Sent: 14 April 2025 14:46
> >> Subject: Re: [PATCH v4 2/3] reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)
> >>
> >>
> >>> +static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
> >>> +                                unsigned long id)
> >>> +{
> >>> +   struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
> >>> +   struct device *dev = priv->dev;
> >>> +   int ret;
> >>> +
> >>> +   ret = pm_runtime_resume_and_get(dev);
> >>> +   if (ret) {
> >>
> >> nit: it will good if we check similar to reset-rzg2l-usbphy-ctrl.c
> >> pm_runtime_resume_and_get -> 0 on success, or a negative error code
> >> otherwise.
> >> 1 → if the device was resumed and incremented usage count
> >> 0 → if the device was already active or successfully resumed
> >> if (ret < 0)
> >
> > No.
> >
> > As you can see from:
> > https://urldefense.com/v3/__https://github.com/torvalds/linux/blob/master/include/linux/pm_runtime.h*L444__;Iw!!ACWV5N9M2RV99hQ!Ly8gpEBQHhYXOeCcKQavVHfM1XUSy1IubKnHjuQAgvfkK0jrMXc0ebBcvFRvNDcpaJwoUOk1JLLuzih2fLd7JReyapWOouY$
> >
> > pm_runtime_resume_and_get returns a negative error code or 0 (when
> > successful).
> >
> > The same explanation applies to your other comments.
> >
>
> Thanks to you for the explanation.
> I got you point.
>
> so We are keeping different styles of error checks:
> In reset-rzv2h-usb2phy.c, we check using if (error),
> Whereas in reset-rzg2l-usbphy-ctrl.c, we use if (error < 0)."
> https://github.com/torvalds/linux/blob/master/drivers/reset/reset-rzg2l-usbphy-ctrl.c#L148
>
Thanks for pointing that out. I'll update reset-rzg2l-usbphy-ctrl.c to
use the same style of error checks for consistency.

Cheers,
Prabhakar
diff mbox series

Patch

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 99f6f9784e68..6c7dbd8ca580 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -225,6 +225,13 @@  config RESET_RZG2L_USBPHY_CTRL
 	  Support for USBPHY Control found on RZ/G2L family. It mainly
 	  controls reset and power down of the USB/PHY.
 
+config RESET_RZV2H_USB2PHY
+	tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	help
+	  Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
+	  (and similar SoCs).
+
 config RESET_SCMI
 	tristate "Reset driver controlled via ARM SCMI interface"
 	depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 31f9904d13f9..30d0fb4ac965 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -31,6 +31,7 @@  obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
 obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
 obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
 obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
+obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c
new file mode 100644
index 000000000000..737b768829c9
--- /dev/null
+++ b/drivers/reset/reset-rzv2h-usb2phy.c
@@ -0,0 +1,241 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/V2H(P) USB2PHY Port reset control driver
+ *
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/reset-controller.h>
+
+struct rzv2h_usb2phy_regval {
+	u16 reg;
+	u16 val;
+};
+
+struct rzv2h_usb2phy_reset_of_data {
+	const struct rzv2h_usb2phy_regval *init_vals;
+	unsigned int init_val_count;
+
+	u16 reset_reg;
+	u16 reset_assert_val;
+	u16 reset_deassert_val;
+	u16 reset_status_bits;
+	u16 reset_release_val;
+
+	u16 reset2_reg;
+	u16 reset2_acquire_val;
+	u16 reset2_release_val;
+};
+
+struct rzv2h_usb2phy_reset_priv {
+	const struct rzv2h_usb2phy_reset_of_data *data;
+	void __iomem *base;
+	struct device *dev;
+	struct reset_controller_dev rcdev;
+	spinlock_t lock; /* protects register accesses */
+};
+
+static inline struct rzv2h_usb2phy_reset_priv
+*rzv2h_usbphy_rcdev_to_priv(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct rzv2h_usb2phy_reset_priv, rcdev);
+}
+
+/* This function must be called only after pm_runtime_resume_and_get() has been called */
+static void rzv2h_usbphy_assert_helper(struct rzv2h_usb2phy_reset_priv *priv)
+{
+	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
+
+	scoped_guard(spinlock, &priv->lock) {
+		writel(data->reset2_acquire_val, priv->base + data->reset2_reg);
+		writel(data->reset_assert_val, priv->base + data->reset_reg);
+	}
+
+	usleep_range(11, 20);
+}
+
+static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
+				     unsigned long id)
+{
+	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
+	struct device *dev = priv->dev;
+	int ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret) {
+		dev_err(dev, "pm_runtime_resume_and_get failed\n");
+		return ret;
+	}
+
+	rzv2h_usbphy_assert_helper(priv);
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
+				       unsigned long id)
+{
+	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
+	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
+	struct device *dev = priv->dev;
+	int ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret) {
+		dev_err(dev, "pm_runtime_resume_and_get failed\n");
+		return ret;
+	}
+
+	scoped_guard(spinlock, &priv->lock) {
+		writel(data->reset_deassert_val, priv->base + data->reset_reg);
+		writel(data->reset2_release_val, priv->base + data->reset2_reg);
+		writel(data->reset_release_val, priv->base + data->reset_reg);
+	}
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
+				     unsigned long id)
+{
+	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
+	struct device *dev = priv->dev;
+	int ret;
+	u32 reg;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret) {
+		dev_err(dev, "pm_runtime_resume_and_get failed\n");
+		return ret;
+	}
+
+	reg = readl(priv->base + priv->data->reset_reg);
+
+	pm_runtime_put(dev);
+
+	return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits;
+}
+
+static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
+	.assert = rzv2h_usbphy_reset_assert,
+	.deassert = rzv2h_usbphy_reset_deassert,
+	.status = rzv2h_usbphy_reset_status,
+};
+
+static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
+					const struct of_phandle_args *reset_spec)
+{
+	/* No special handling needed, we have only one reset line per device */
+	return 0;
+}
+
+static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
+{
+	const struct rzv2h_usb2phy_reset_of_data *data;
+	struct rzv2h_usb2phy_reset_priv *priv;
+	struct device *dev = &pdev->dev;
+	struct reset_control *rstc;
+	int error;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	data = of_device_get_match_data(dev);
+	if (!data)
+		return dev_err_probe(dev, -ENODEV,
+				     "failed to match device\n");
+
+	priv->data = data;
+	priv->dev = dev;
+	priv->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
+	if (IS_ERR(rstc))
+		return dev_err_probe(dev, PTR_ERR(rstc),
+				     "failed to get deasserted reset\n");
+
+	spin_lock_init(&priv->lock);
+	dev_set_drvdata(dev, priv);
+
+	error = devm_pm_runtime_enable(dev);
+	if (error)
+		return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
+
+	error = pm_runtime_resume_and_get(dev);
+	if (error)
+		return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n");
+
+	for (unsigned int i = 0; i < data->init_val_count; i++)
+		writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
+
+	/* keep usb2phy in asserted state */
+	rzv2h_usbphy_assert_helper(priv);
+
+	pm_runtime_put(dev);
+
+	priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
+	priv->rcdev.of_reset_n_cells = 0;
+	priv->rcdev.nr_resets = 1;
+	priv->rcdev.of_xlate = rzv2h_usb2phy_reset_of_xlate;
+	priv->rcdev.of_node = dev->of_node;
+	priv->rcdev.dev = dev;
+
+	return devm_reset_controller_register(dev, &priv->rcdev);
+}
+
+/*
+ * initialization values required to prepare the PHY to receive
+ * assert and deassert requests.
+ */
+static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = {
+	{ .reg = 0xc10, .val = 0x67c },
+	{ .reg = 0xc14, .val = 0x1f },
+	{ .reg = 0x600, .val = 0x909 },
+};
+
+static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = {
+	.init_vals = rzv2h_init_vals,
+	.init_val_count = ARRAY_SIZE(rzv2h_init_vals),
+	.reset_reg = 0,
+	.reset_assert_val = 0x206,
+	.reset_status_bits = BIT(2),
+	.reset_deassert_val = 0x200,
+	.reset_release_val = 0x0,
+	.reset2_reg = 0xb04,
+	.reset2_acquire_val = 0x303,
+	.reset2_release_val = 0x3,
+};
+
+static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = {
+	{ .compatible = "renesas,r9a09g057-usb2phy-reset", .data = &rzv2h_reset_of_data },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzv2h_usb2phy_reset_of_match);
+
+static struct platform_driver rzv2h_usb2phy_reset_driver = {
+	.driver = {
+		.name		= "rzv2h_usb2phy_reset",
+		.of_match_table	= rzv2h_usb2phy_reset_of_match,
+	},
+	.probe = rzv2h_usb2phy_reset_probe,
+};
+module_platform_driver(rzv2h_usb2phy_reset_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
+MODULE_DESCRIPTION("Renesas RZ/V2H(P) USB2PHY Control");