Blob Blame History Raw
From: Thierry Reding <treding@nvidia.com>
Date: Wed, 18 Mar 2020 23:25:54 +0100
Subject: phy: tegra: Fix regulator leak

Git-commit: 2f8da84def73e1dd89385146e1dbb2ae2c8e0a6a
Patch-mainline: v5.7-rc1
References: jsc#SLE-15847

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>
Signed-off-by: Mian Yousaf Kaukab <yousaf.kaukab@suse.com>
---
 drivers/phy/tegra/xusb-tegra124.c |  2 ++
 drivers/phy/tegra/xusb-tegra186.c |  2 ++
 drivers/phy/tegra/xusb-tegra210.c |  2 ++
 drivers/phy/tegra/xusb.c          | 21 +++++++++++++++++++--
 drivers/phy/tegra/xusb.h          |  3 +++
 5 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c
index 98d84920c676..0080de727bba 100644
--- a/drivers/phy/tegra/xusb-tegra124.c
+++ b/drivers/phy/tegra/xusb-tegra124.c
@@ -1422,6 +1422,7 @@ tegra124_usb2_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = {
+	.remove = tegra_xusb_usb2_port_remove,
 	.enable = tegra124_usb2_port_enable,
 	.disable = tegra124_usb2_port_disable,
 	.map = tegra124_usb2_port_map,
@@ -1647,6 +1648,7 @@ tegra124_usb3_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = {
+	.remove = tegra_xusb_usb3_port_remove,
 	.enable = tegra124_usb3_port_enable,
 	.disable = tegra124_usb3_port_disable,
 	.map = tegra124_usb3_port_map,
diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index fa700e56dc0f..973df722b93d 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -615,6 +615,7 @@ tegra186_usb2_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra186_usb2_port_ops = {
+	.remove = tegra_xusb_usb2_port_remove,
 	.enable = tegra186_usb2_port_enable,
 	.disable = tegra186_usb2_port_disable,
 	.map = tegra186_usb2_port_map,
@@ -674,6 +675,7 @@ tegra186_usb3_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = {
+	.remove = tegra_xusb_usb3_port_remove,
 	.enable = tegra186_usb3_port_enable,
 	.disable = tegra186_usb3_port_disable,
 	.map = tegra186_usb3_port_map,
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 54d6826854a9..0e11a8cf2591 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1953,6 +1953,7 @@ tegra210_usb2_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra210_usb2_port_ops = {
+	.remove = tegra_xusb_usb2_port_remove,
 	.enable = tegra210_usb2_port_enable,
 	.disable = tegra210_usb2_port_disable,
 	.map = tegra210_usb2_port_map,
@@ -2119,6 +2120,7 @@ tegra210_usb3_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = {
+	.remove = tegra_xusb_usb3_port_remove,
 	.enable = tegra210_usb3_port_enable,
 	.disable = tegra210_usb3_port_disable,
 	.map = tegra210_usb3_port_map,
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 @@ static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl,
 	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 @@ static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,
 	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;
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index 51d7aae0d623..fb32ffcb13fd 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -308,6 +308,7 @@ to_usb2_port(struct tegra_xusb_port *port)
 struct tegra_xusb_usb2_port *
 tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl,
 			  unsigned int index);
+void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port);
 
 struct tegra_xusb_ulpi_port {
 	struct tegra_xusb_port base;
@@ -355,8 +356,10 @@ to_usb3_port(struct tegra_xusb_port *port)
 struct tegra_xusb_usb3_port *
 tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl,
 			  unsigned int index);
+void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port);
 
 struct tegra_xusb_port_ops {
+	void (*remove)(struct tegra_xusb_port *port);
 	int (*enable)(struct tegra_xusb_port *port);
 	void (*disable)(struct tegra_xusb_port *port);
 	struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port);
-- 
2.26.2