Skip to content

Commit e3911d7

Browse files
prabhakarladpH5
authored andcommitted
reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)
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 <[email protected]> Reviewed-by: Biju Das <[email protected]> Reviewed-by: Fabrizio Castro <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Philipp Zabel <[email protected]>
1 parent 261f3ff commit e3911d7

File tree

3 files changed

+244
-0
lines changed

3 files changed

+244
-0
lines changed

drivers/reset/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ config RESET_RZG2L_USBPHY_CTRL
225225
Support for USBPHY Control found on RZ/G2L family. It mainly
226226
controls reset and power down of the USB/PHY.
227227

228+
config RESET_RZV2H_USB2PHY
229+
tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
230+
depends on ARCH_RENESAS || COMPILE_TEST
231+
help
232+
Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
233+
(and similar SoCs).
234+
228235
config RESET_SCMI
229236
tristate "Reset driver controlled via ARM SCMI interface"
230237
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST

drivers/reset/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
3131
obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
3232
obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
3333
obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
34+
obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
3435
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
3536
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
3637
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o

drivers/reset/reset-rzv2h-usb2phy.c

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Renesas RZ/V2H(P) USB2PHY Port reset control driver
4+
*
5+
* Copyright (C) 2025 Renesas Electronics Corporation
6+
*/
7+
8+
#include <linux/cleanup.h>
9+
#include <linux/delay.h>
10+
#include <linux/io.h>
11+
#include <linux/module.h>
12+
#include <linux/of.h>
13+
#include <linux/platform_device.h>
14+
#include <linux/pm_runtime.h>
15+
#include <linux/reset.h>
16+
#include <linux/reset-controller.h>
17+
18+
struct rzv2h_usb2phy_regval {
19+
u16 reg;
20+
u16 val;
21+
};
22+
23+
struct rzv2h_usb2phy_reset_of_data {
24+
const struct rzv2h_usb2phy_regval *init_vals;
25+
unsigned int init_val_count;
26+
27+
u16 reset_reg;
28+
u16 reset_assert_val;
29+
u16 reset_deassert_val;
30+
u16 reset_status_bits;
31+
u16 reset_release_val;
32+
33+
u16 reset2_reg;
34+
u16 reset2_acquire_val;
35+
u16 reset2_release_val;
36+
};
37+
38+
struct rzv2h_usb2phy_reset_priv {
39+
const struct rzv2h_usb2phy_reset_of_data *data;
40+
void __iomem *base;
41+
struct device *dev;
42+
struct reset_controller_dev rcdev;
43+
spinlock_t lock; /* protects register accesses */
44+
};
45+
46+
static inline struct rzv2h_usb2phy_reset_priv
47+
*rzv2h_usbphy_rcdev_to_priv(struct reset_controller_dev *rcdev)
48+
{
49+
return container_of(rcdev, struct rzv2h_usb2phy_reset_priv, rcdev);
50+
}
51+
52+
/* This function must be called only after pm_runtime_resume_and_get() has been called */
53+
static void rzv2h_usbphy_assert_helper(struct rzv2h_usb2phy_reset_priv *priv)
54+
{
55+
const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
56+
57+
scoped_guard(spinlock, &priv->lock) {
58+
writel(data->reset2_acquire_val, priv->base + data->reset2_reg);
59+
writel(data->reset_assert_val, priv->base + data->reset_reg);
60+
}
61+
62+
usleep_range(11, 20);
63+
}
64+
65+
static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
66+
unsigned long id)
67+
{
68+
struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
69+
struct device *dev = priv->dev;
70+
int ret;
71+
72+
ret = pm_runtime_resume_and_get(dev);
73+
if (ret) {
74+
dev_err(dev, "pm_runtime_resume_and_get failed\n");
75+
return ret;
76+
}
77+
78+
rzv2h_usbphy_assert_helper(priv);
79+
80+
pm_runtime_put(dev);
81+
82+
return 0;
83+
}
84+
85+
static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
86+
unsigned long id)
87+
{
88+
struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
89+
const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
90+
struct device *dev = priv->dev;
91+
int ret;
92+
93+
ret = pm_runtime_resume_and_get(dev);
94+
if (ret) {
95+
dev_err(dev, "pm_runtime_resume_and_get failed\n");
96+
return ret;
97+
}
98+
99+
scoped_guard(spinlock, &priv->lock) {
100+
writel(data->reset_deassert_val, priv->base + data->reset_reg);
101+
writel(data->reset2_release_val, priv->base + data->reset2_reg);
102+
writel(data->reset_release_val, priv->base + data->reset_reg);
103+
}
104+
105+
pm_runtime_put(dev);
106+
107+
return 0;
108+
}
109+
110+
static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
111+
unsigned long id)
112+
{
113+
struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
114+
struct device *dev = priv->dev;
115+
int ret;
116+
u32 reg;
117+
118+
ret = pm_runtime_resume_and_get(dev);
119+
if (ret) {
120+
dev_err(dev, "pm_runtime_resume_and_get failed\n");
121+
return ret;
122+
}
123+
124+
reg = readl(priv->base + priv->data->reset_reg);
125+
126+
pm_runtime_put(dev);
127+
128+
return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits;
129+
}
130+
131+
static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
132+
.assert = rzv2h_usbphy_reset_assert,
133+
.deassert = rzv2h_usbphy_reset_deassert,
134+
.status = rzv2h_usbphy_reset_status,
135+
};
136+
137+
static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
138+
const struct of_phandle_args *reset_spec)
139+
{
140+
/* No special handling needed, we have only one reset line per device */
141+
return 0;
142+
}
143+
144+
static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
145+
{
146+
const struct rzv2h_usb2phy_reset_of_data *data;
147+
struct rzv2h_usb2phy_reset_priv *priv;
148+
struct device *dev = &pdev->dev;
149+
struct reset_control *rstc;
150+
int error;
151+
152+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
153+
if (!priv)
154+
return -ENOMEM;
155+
156+
data = of_device_get_match_data(dev);
157+
priv->data = data;
158+
priv->dev = dev;
159+
priv->base = devm_platform_ioremap_resource(pdev, 0);
160+
if (IS_ERR(priv->base))
161+
return PTR_ERR(priv->base);
162+
163+
rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
164+
if (IS_ERR(rstc))
165+
return dev_err_probe(dev, PTR_ERR(rstc),
166+
"failed to get deasserted reset\n");
167+
168+
spin_lock_init(&priv->lock);
169+
170+
error = devm_pm_runtime_enable(dev);
171+
if (error)
172+
return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
173+
174+
error = pm_runtime_resume_and_get(dev);
175+
if (error)
176+
return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n");
177+
178+
for (unsigned int i = 0; i < data->init_val_count; i++)
179+
writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
180+
181+
/* keep usb2phy in asserted state */
182+
rzv2h_usbphy_assert_helper(priv);
183+
184+
pm_runtime_put(dev);
185+
186+
priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
187+
priv->rcdev.of_reset_n_cells = 0;
188+
priv->rcdev.nr_resets = 1;
189+
priv->rcdev.of_xlate = rzv2h_usb2phy_reset_of_xlate;
190+
priv->rcdev.of_node = dev->of_node;
191+
priv->rcdev.dev = dev;
192+
193+
return devm_reset_controller_register(dev, &priv->rcdev);
194+
}
195+
196+
/*
197+
* initialization values required to prepare the PHY to receive
198+
* assert and deassert requests.
199+
*/
200+
static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = {
201+
{ .reg = 0xc10, .val = 0x67c },
202+
{ .reg = 0xc14, .val = 0x1f },
203+
{ .reg = 0x600, .val = 0x909 },
204+
};
205+
206+
static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = {
207+
.init_vals = rzv2h_init_vals,
208+
.init_val_count = ARRAY_SIZE(rzv2h_init_vals),
209+
.reset_reg = 0,
210+
.reset_assert_val = 0x206,
211+
.reset_status_bits = BIT(2),
212+
.reset_deassert_val = 0x200,
213+
.reset_release_val = 0x0,
214+
.reset2_reg = 0xb04,
215+
.reset2_acquire_val = 0x303,
216+
.reset2_release_val = 0x3,
217+
};
218+
219+
static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = {
220+
{ .compatible = "renesas,r9a09g057-usb2phy-reset", .data = &rzv2h_reset_of_data },
221+
{ /* Sentinel */ }
222+
};
223+
MODULE_DEVICE_TABLE(of, rzv2h_usb2phy_reset_of_match);
224+
225+
static struct platform_driver rzv2h_usb2phy_reset_driver = {
226+
.driver = {
227+
.name = "rzv2h_usb2phy_reset",
228+
.of_match_table = rzv2h_usb2phy_reset_of_match,
229+
},
230+
.probe = rzv2h_usb2phy_reset_probe,
231+
};
232+
module_platform_driver(rzv2h_usb2phy_reset_driver);
233+
234+
MODULE_LICENSE("GPL");
235+
MODULE_AUTHOR("Lad Prabhakar <[email protected]>");
236+
MODULE_DESCRIPTION("Renesas RZ/V2H(P) USB2PHY Control");

0 commit comments

Comments
 (0)