aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorGregory CLEMENT <gregory.clement@free-electrons.com>2014-05-15 06:17:28 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-27 18:33:12 -0400
commitac0696629d73d87e51c2fb75426cebad4399bac4 (patch)
treefc04eaeae3b8b58876c813f602e29d784d52bd23 /drivers/usb
parentd8ea69e28d72f85caa5dcce0db5b62ae468f2c5a (diff)
usb: ehci-orion: fix clock reference leaking
In order to disable the clock in the ->remove() function, a call to devm_clk_get() is being made, which further increases the reference count of the clock. In order to clean this up, a private structure holding a pointer to the clock is added using the override mechanism provided by the ehci framework. This makes the driver clock handling much more logical. The bug was introduced in v3.6, however the ehci framework allowing to use the override mechanism has only been introduced in v3.8, so this patch won't apply before it. [Thomas: reword commit log, fix goto label names.] Fixes: 8c869edaee07c623066266827371235fb9c12e01 ('ARM: Orion: EHCI: Add support for enabling clocks') Cc: <stable@vger.kernel.org> # v3.8+ Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-orion.c45
1 files changed, 29 insertions, 16 deletions
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 9298be7c0f9b..9c98bac0a5bc 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -42,6 +42,12 @@
42 42
43#define DRIVER_DESC "EHCI orion driver" 43#define DRIVER_DESC "EHCI orion driver"
44 44
45#define hcd_to_orion_priv(h) ((struct orion_ehci_hcd *)hcd_to_ehci(h)->priv)
46
47struct orion_ehci_hcd {
48 struct clk *clk;
49};
50
45static const char hcd_name[] = "ehci-orion"; 51static const char hcd_name[] = "ehci-orion";
46 52
47static struct hc_driver __read_mostly ehci_orion_hc_driver; 53static struct hc_driver __read_mostly ehci_orion_hc_driver;
@@ -137,6 +143,10 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,
137 } 143 }
138} 144}
139 145
146static const struct ehci_driver_overrides orion_overrides __initconst = {
147 .extra_priv_size = sizeof(struct orion_ehci_hcd),
148};
149
140static int ehci_orion_drv_probe(struct platform_device *pdev) 150static int ehci_orion_drv_probe(struct platform_device *pdev)
141{ 151{
142 struct orion_ehci_data *pd = dev_get_platdata(&pdev->dev); 152 struct orion_ehci_data *pd = dev_get_platdata(&pdev->dev);
@@ -144,10 +154,10 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
144 struct resource *res; 154 struct resource *res;
145 struct usb_hcd *hcd; 155 struct usb_hcd *hcd;
146 struct ehci_hcd *ehci; 156 struct ehci_hcd *ehci;
147 struct clk *clk;
148 void __iomem *regs; 157 void __iomem *regs;
149 int irq, err; 158 int irq, err;
150 enum orion_ehci_phy_ver phy_version; 159 enum orion_ehci_phy_ver phy_version;
160 struct orion_ehci_hcd *priv;
151 161
152 if (usb_disabled()) 162 if (usb_disabled())
153 return -ENODEV; 163 return -ENODEV;
@@ -187,17 +197,11 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
187 goto err; 197 goto err;
188 } 198 }
189 199
190 /* Not all platforms can gate the clock, so it is not
191 an error if the clock does not exists. */
192 clk = devm_clk_get(&pdev->dev, NULL);
193 if (!IS_ERR(clk))
194 clk_prepare_enable(clk);
195
196 hcd = usb_create_hcd(&ehci_orion_hc_driver, 200 hcd = usb_create_hcd(&ehci_orion_hc_driver,
197 &pdev->dev, dev_name(&pdev->dev)); 201 &pdev->dev, dev_name(&pdev->dev));
198 if (!hcd) { 202 if (!hcd) {
199 err = -ENOMEM; 203 err = -ENOMEM;
200 goto err_create_hcd; 204 goto err;
201 } 205 }
202 206
203 hcd->rsrc_start = res->start; 207 hcd->rsrc_start = res->start;
@@ -208,6 +212,15 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
208 ehci->caps = hcd->regs + 0x100; 212 ehci->caps = hcd->regs + 0x100;
209 hcd->has_tt = 1; 213 hcd->has_tt = 1;
210 214
215 priv = hcd_to_orion_priv(hcd);
216 /*
217 * Not all platforms can gate the clock, so it is not an error if
218 * the clock does not exists.
219 */
220 priv->clk = devm_clk_get(&pdev->dev, NULL);
221 if (!IS_ERR(priv->clk))
222 clk_prepare_enable(priv->clk);
223
211 /* 224 /*
212 * (Re-)program MBUS remapping windows if we are asked to. 225 * (Re-)program MBUS remapping windows if we are asked to.
213 */ 226 */
@@ -243,10 +256,9 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
243 return 0; 256 return 0;
244 257
245err_add_hcd: 258err_add_hcd:
259 if (!IS_ERR(priv->clk))
260 clk_disable_unprepare(priv->clk);
246 usb_put_hcd(hcd); 261 usb_put_hcd(hcd);
247err_create_hcd:
248 if (!IS_ERR(clk))
249 clk_disable_unprepare(clk);
250err: 262err:
251 dev_err(&pdev->dev, "init %s fail, %d\n", 263 dev_err(&pdev->dev, "init %s fail, %d\n",
252 dev_name(&pdev->dev), err); 264 dev_name(&pdev->dev), err);
@@ -257,14 +269,15 @@ err:
257static int ehci_orion_drv_remove(struct platform_device *pdev) 269static int ehci_orion_drv_remove(struct platform_device *pdev)
258{ 270{
259 struct usb_hcd *hcd = platform_get_drvdata(pdev); 271 struct usb_hcd *hcd = platform_get_drvdata(pdev);
260 struct clk *clk; 272 struct orion_ehci_hcd *priv = hcd_to_orion_priv(hcd);
261 273
262 usb_remove_hcd(hcd); 274 usb_remove_hcd(hcd);
275
276 if (!IS_ERR(priv->clk))
277 clk_disable_unprepare(priv->clk);
278
263 usb_put_hcd(hcd); 279 usb_put_hcd(hcd);
264 280
265 clk = devm_clk_get(&pdev->dev, NULL);
266 if (!IS_ERR(clk))
267 clk_disable_unprepare(clk);
268 return 0; 281 return 0;
269} 282}
270 283
@@ -292,7 +305,7 @@ static int __init ehci_orion_init(void)
292 305
293 pr_info("%s: " DRIVER_DESC "\n", hcd_name); 306 pr_info("%s: " DRIVER_DESC "\n", hcd_name);
294 307
295 ehci_init_driver(&ehci_orion_hc_driver, NULL); 308 ehci_init_driver(&ehci_orion_hc_driver, &orion_overrides);
296 return platform_driver_register(&ehci_orion_driver); 309 return platform_driver_register(&ehci_orion_driver);
297} 310}
298module_init(ehci_orion_init); 311module_init(ehci_orion_init);