diff options
author | Thierry Reding <treding@nvidia.com> | 2020-03-18 23:25:54 +0100 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2020-03-19 14:00:04 +0100 |
commit | 2f8da84def73e1dd89385146e1dbb2ae2c8e0a6a (patch) | |
tree | 49393740be1a16f05e58d9aa54787616a0ea6784 /drivers/phy/tegra/xusb.c | |
parent | 562835644667459c701b08c036fbe72443a3fb71 (diff) |
phy: tegra: Fix regulator leak
Devices are created for each port of the XUSB pad controller. Each USB 2
and USB 3 port can potentially have an associated VBUS power supply that
needs to be removed when the device is removed.
Since port devices never bind to a driver, the driver core will not get
to perform the cleanup of device-managed resources that usually happens
on driver unbind.
Now, the driver core will also perform device-managed resource cleanup
for driver-less devices when they are released. However, when a device
link is created between the regulator and the port device, as part of
regulator_get(), the regulator takes a reference to the port device and
prevents it from being released unless regulator_put() is called, which
will never happen.
Avoid this by using the non-device-managed API and manually releasing
the regulator reference when the port is unregistered.
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/phy/tegra/xusb.c')
-rw-r--r-- | drivers/phy/tegra/xusb.c | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index babc63e568da..5914cd9dfd7a 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -554,6 +554,9 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port) usb_remove_phy(&port->usb_phy); } + if (port->ops->remove) + port->ops->remove(port); + device_unregister(&port->dev); } @@ -734,7 +737,7 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2) } } - usb2->supply = devm_regulator_get(&port->dev, "vbus"); + usb2->supply = regulator_get(&port->dev, "vbus"); return PTR_ERR_OR_ZERO(usb2->supply); } @@ -784,6 +787,13 @@ out: return err; } +void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port); + + regulator_put(usb2->supply); +} + static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi) { struct tegra_xusb_port *port = &ulpi->base; @@ -912,7 +922,7 @@ static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) return -EINVAL; } - usb3->supply = devm_regulator_get(&port->dev, "vbus"); + usb3->supply = regulator_get(&port->dev, "vbus"); return PTR_ERR_OR_ZERO(usb3->supply); } @@ -963,6 +973,13 @@ out: return err; } +void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + + regulator_put(usb3->supply); +} + static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_port *port, *tmp; |