xref: /openbmc/linux/drivers/usb/host/ehci-mv.c (revision 3a082ec9b2f544a81e977cfa259e3f990a995dc8)
1*3a082ec9SNeil Zhang /*
2*3a082ec9SNeil Zhang  * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
3*3a082ec9SNeil Zhang  * Author: Chao Xie <chao.xie@marvell.com>
4*3a082ec9SNeil Zhang  *        Neil Zhang <zhangwm@marvell.com>
5*3a082ec9SNeil Zhang  *
6*3a082ec9SNeil Zhang  * This program is free software; you can redistribute  it and/or modify it
7*3a082ec9SNeil Zhang  * under  the terms of  the GNU General  Public License as published by the
8*3a082ec9SNeil Zhang  * Free Software Foundation;  either version 2 of the  License, or (at your
9*3a082ec9SNeil Zhang  * option) any later version.
10*3a082ec9SNeil Zhang  */
11*3a082ec9SNeil Zhang 
12*3a082ec9SNeil Zhang #include <linux/kernel.h>
13*3a082ec9SNeil Zhang #include <linux/module.h>
14*3a082ec9SNeil Zhang #include <linux/platform_device.h>
15*3a082ec9SNeil Zhang #include <linux/clk.h>
16*3a082ec9SNeil Zhang #include <linux/usb/otg.h>
17*3a082ec9SNeil Zhang #include <linux/platform_data/mv_usb.h>
18*3a082ec9SNeil Zhang 
19*3a082ec9SNeil Zhang #define CAPLENGTH_MASK         (0xff)
20*3a082ec9SNeil Zhang 
21*3a082ec9SNeil Zhang struct ehci_hcd_mv {
22*3a082ec9SNeil Zhang 	struct usb_hcd *hcd;
23*3a082ec9SNeil Zhang 
24*3a082ec9SNeil Zhang 	/* Which mode does this ehci running OTG/Host ? */
25*3a082ec9SNeil Zhang 	int mode;
26*3a082ec9SNeil Zhang 
27*3a082ec9SNeil Zhang 	void __iomem *phy_regs;
28*3a082ec9SNeil Zhang 	void __iomem *cap_regs;
29*3a082ec9SNeil Zhang 	void __iomem *op_regs;
30*3a082ec9SNeil Zhang 
31*3a082ec9SNeil Zhang 	struct otg_transceiver *otg;
32*3a082ec9SNeil Zhang 
33*3a082ec9SNeil Zhang 	struct mv_usb_platform_data *pdata;
34*3a082ec9SNeil Zhang 
35*3a082ec9SNeil Zhang 	/* clock source and total clock number */
36*3a082ec9SNeil Zhang 	unsigned int clknum;
37*3a082ec9SNeil Zhang 	struct clk *clk[0];
38*3a082ec9SNeil Zhang };
39*3a082ec9SNeil Zhang 
40*3a082ec9SNeil Zhang static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
41*3a082ec9SNeil Zhang {
42*3a082ec9SNeil Zhang 	unsigned int i;
43*3a082ec9SNeil Zhang 
44*3a082ec9SNeil Zhang 	for (i = 0; i < ehci_mv->clknum; i++)
45*3a082ec9SNeil Zhang 		clk_enable(ehci_mv->clk[i]);
46*3a082ec9SNeil Zhang }
47*3a082ec9SNeil Zhang 
48*3a082ec9SNeil Zhang static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
49*3a082ec9SNeil Zhang {
50*3a082ec9SNeil Zhang 	unsigned int i;
51*3a082ec9SNeil Zhang 
52*3a082ec9SNeil Zhang 	for (i = 0; i < ehci_mv->clknum; i++)
53*3a082ec9SNeil Zhang 		clk_disable(ehci_mv->clk[i]);
54*3a082ec9SNeil Zhang }
55*3a082ec9SNeil Zhang 
56*3a082ec9SNeil Zhang static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
57*3a082ec9SNeil Zhang {
58*3a082ec9SNeil Zhang 	int retval;
59*3a082ec9SNeil Zhang 
60*3a082ec9SNeil Zhang 	ehci_clock_enable(ehci_mv);
61*3a082ec9SNeil Zhang 	if (ehci_mv->pdata->phy_init) {
62*3a082ec9SNeil Zhang 		retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs);
63*3a082ec9SNeil Zhang 		if (retval)
64*3a082ec9SNeil Zhang 			return retval;
65*3a082ec9SNeil Zhang 	}
66*3a082ec9SNeil Zhang 
67*3a082ec9SNeil Zhang 	return 0;
68*3a082ec9SNeil Zhang }
69*3a082ec9SNeil Zhang 
70*3a082ec9SNeil Zhang static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
71*3a082ec9SNeil Zhang {
72*3a082ec9SNeil Zhang 	if (ehci_mv->pdata->phy_deinit)
73*3a082ec9SNeil Zhang 		ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs);
74*3a082ec9SNeil Zhang 	ehci_clock_disable(ehci_mv);
75*3a082ec9SNeil Zhang }
76*3a082ec9SNeil Zhang 
77*3a082ec9SNeil Zhang static int mv_ehci_reset(struct usb_hcd *hcd)
78*3a082ec9SNeil Zhang {
79*3a082ec9SNeil Zhang 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
80*3a082ec9SNeil Zhang 	struct device *dev = hcd->self.controller;
81*3a082ec9SNeil Zhang 	struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
82*3a082ec9SNeil Zhang 	int retval;
83*3a082ec9SNeil Zhang 
84*3a082ec9SNeil Zhang 	if (ehci_mv == NULL) {
85*3a082ec9SNeil Zhang 		dev_err(dev, "Can not find private ehci data\n");
86*3a082ec9SNeil Zhang 		return -ENODEV;
87*3a082ec9SNeil Zhang 	}
88*3a082ec9SNeil Zhang 
89*3a082ec9SNeil Zhang 	/*
90*3a082ec9SNeil Zhang 	 * data structure init
91*3a082ec9SNeil Zhang 	 */
92*3a082ec9SNeil Zhang 	retval = ehci_init(hcd);
93*3a082ec9SNeil Zhang 	if (retval) {
94*3a082ec9SNeil Zhang 		dev_err(dev, "ehci_init failed %d\n", retval);
95*3a082ec9SNeil Zhang 		return retval;
96*3a082ec9SNeil Zhang 	}
97*3a082ec9SNeil Zhang 
98*3a082ec9SNeil Zhang 	hcd->has_tt = 1;
99*3a082ec9SNeil Zhang 	ehci->sbrn = 0x20;
100*3a082ec9SNeil Zhang 
101*3a082ec9SNeil Zhang 	retval = ehci_reset(ehci);
102*3a082ec9SNeil Zhang 	if (retval) {
103*3a082ec9SNeil Zhang 		dev_err(dev, "ehci_reset failed %d\n", retval);
104*3a082ec9SNeil Zhang 		return retval;
105*3a082ec9SNeil Zhang 	}
106*3a082ec9SNeil Zhang 
107*3a082ec9SNeil Zhang 	return 0;
108*3a082ec9SNeil Zhang }
109*3a082ec9SNeil Zhang 
110*3a082ec9SNeil Zhang static const struct hc_driver mv_ehci_hc_driver = {
111*3a082ec9SNeil Zhang 	.description = hcd_name,
112*3a082ec9SNeil Zhang 	.product_desc = "Marvell EHCI",
113*3a082ec9SNeil Zhang 	.hcd_priv_size = sizeof(struct ehci_hcd),
114*3a082ec9SNeil Zhang 
115*3a082ec9SNeil Zhang 	/*
116*3a082ec9SNeil Zhang 	 * generic hardware linkage
117*3a082ec9SNeil Zhang 	 */
118*3a082ec9SNeil Zhang 	.irq = ehci_irq,
119*3a082ec9SNeil Zhang 	.flags = HCD_MEMORY | HCD_USB2,
120*3a082ec9SNeil Zhang 
121*3a082ec9SNeil Zhang 	/*
122*3a082ec9SNeil Zhang 	 * basic lifecycle operations
123*3a082ec9SNeil Zhang 	 */
124*3a082ec9SNeil Zhang 	.reset = mv_ehci_reset,
125*3a082ec9SNeil Zhang 	.start = ehci_run,
126*3a082ec9SNeil Zhang 	.stop = ehci_stop,
127*3a082ec9SNeil Zhang 	.shutdown = ehci_shutdown,
128*3a082ec9SNeil Zhang 
129*3a082ec9SNeil Zhang 	/*
130*3a082ec9SNeil Zhang 	 * managing i/o requests and associated device resources
131*3a082ec9SNeil Zhang 	 */
132*3a082ec9SNeil Zhang 	.urb_enqueue = ehci_urb_enqueue,
133*3a082ec9SNeil Zhang 	.urb_dequeue = ehci_urb_dequeue,
134*3a082ec9SNeil Zhang 	.endpoint_disable = ehci_endpoint_disable,
135*3a082ec9SNeil Zhang 	.endpoint_reset = ehci_endpoint_reset,
136*3a082ec9SNeil Zhang 	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
137*3a082ec9SNeil Zhang 
138*3a082ec9SNeil Zhang 	/*
139*3a082ec9SNeil Zhang 	 * scheduling support
140*3a082ec9SNeil Zhang 	 */
141*3a082ec9SNeil Zhang 	.get_frame_number = ehci_get_frame,
142*3a082ec9SNeil Zhang 
143*3a082ec9SNeil Zhang 	/*
144*3a082ec9SNeil Zhang 	 * root hub support
145*3a082ec9SNeil Zhang 	 */
146*3a082ec9SNeil Zhang 	.hub_status_data = ehci_hub_status_data,
147*3a082ec9SNeil Zhang 	.hub_control = ehci_hub_control,
148*3a082ec9SNeil Zhang 	.bus_suspend = ehci_bus_suspend,
149*3a082ec9SNeil Zhang 	.bus_resume = ehci_bus_resume,
150*3a082ec9SNeil Zhang };
151*3a082ec9SNeil Zhang 
152*3a082ec9SNeil Zhang static int mv_ehci_probe(struct platform_device *pdev)
153*3a082ec9SNeil Zhang {
154*3a082ec9SNeil Zhang 	struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
155*3a082ec9SNeil Zhang 	struct usb_hcd *hcd;
156*3a082ec9SNeil Zhang 	struct ehci_hcd *ehci;
157*3a082ec9SNeil Zhang 	struct ehci_hcd_mv *ehci_mv;
158*3a082ec9SNeil Zhang 	struct resource *r;
159*3a082ec9SNeil Zhang 	int clk_i, retval = -ENODEV;
160*3a082ec9SNeil Zhang 	u32 offset;
161*3a082ec9SNeil Zhang 	size_t size;
162*3a082ec9SNeil Zhang 
163*3a082ec9SNeil Zhang 	if (!pdata) {
164*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "missing platform_data\n");
165*3a082ec9SNeil Zhang 		return -ENODEV;
166*3a082ec9SNeil Zhang 	}
167*3a082ec9SNeil Zhang 
168*3a082ec9SNeil Zhang 	if (usb_disabled())
169*3a082ec9SNeil Zhang 		return -ENODEV;
170*3a082ec9SNeil Zhang 
171*3a082ec9SNeil Zhang 	hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci");
172*3a082ec9SNeil Zhang 	if (!hcd)
173*3a082ec9SNeil Zhang 		return -ENOMEM;
174*3a082ec9SNeil Zhang 
175*3a082ec9SNeil Zhang 	size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum;
176*3a082ec9SNeil Zhang 	ehci_mv = kzalloc(size, GFP_KERNEL);
177*3a082ec9SNeil Zhang 	if (ehci_mv == NULL) {
178*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n");
179*3a082ec9SNeil Zhang 		retval = -ENOMEM;
180*3a082ec9SNeil Zhang 		goto err_put_hcd;
181*3a082ec9SNeil Zhang 	}
182*3a082ec9SNeil Zhang 
183*3a082ec9SNeil Zhang 	platform_set_drvdata(pdev, ehci_mv);
184*3a082ec9SNeil Zhang 	ehci_mv->pdata = pdata;
185*3a082ec9SNeil Zhang 	ehci_mv->hcd = hcd;
186*3a082ec9SNeil Zhang 
187*3a082ec9SNeil Zhang 	ehci_mv->clknum = pdata->clknum;
188*3a082ec9SNeil Zhang 	for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) {
189*3a082ec9SNeil Zhang 		ehci_mv->clk[clk_i] =
190*3a082ec9SNeil Zhang 		    clk_get(&pdev->dev, pdata->clkname[clk_i]);
191*3a082ec9SNeil Zhang 		if (IS_ERR(ehci_mv->clk[clk_i])) {
192*3a082ec9SNeil Zhang 			dev_err(&pdev->dev, "error get clck \"%s\"\n",
193*3a082ec9SNeil Zhang 				pdata->clkname[clk_i]);
194*3a082ec9SNeil Zhang 			retval = PTR_ERR(ehci_mv->clk[clk_i]);
195*3a082ec9SNeil Zhang 			goto err_put_clk;
196*3a082ec9SNeil Zhang 		}
197*3a082ec9SNeil Zhang 	}
198*3a082ec9SNeil Zhang 
199*3a082ec9SNeil Zhang 	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs");
200*3a082ec9SNeil Zhang 	if (r == NULL) {
201*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
202*3a082ec9SNeil Zhang 		retval = -ENODEV;
203*3a082ec9SNeil Zhang 		goto err_put_clk;
204*3a082ec9SNeil Zhang 	}
205*3a082ec9SNeil Zhang 
206*3a082ec9SNeil Zhang 	ehci_mv->phy_regs = ioremap(r->start, resource_size(r));
207*3a082ec9SNeil Zhang 	if (ehci_mv->phy_regs == 0) {
208*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "failed to map phy I/O memory\n");
209*3a082ec9SNeil Zhang 		retval = -EFAULT;
210*3a082ec9SNeil Zhang 		goto err_put_clk;
211*3a082ec9SNeil Zhang 	}
212*3a082ec9SNeil Zhang 
213*3a082ec9SNeil Zhang 	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
214*3a082ec9SNeil Zhang 	if (!r) {
215*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "no I/O memory resource defined\n");
216*3a082ec9SNeil Zhang 		retval = -ENODEV;
217*3a082ec9SNeil Zhang 		goto err_iounmap_phyreg;
218*3a082ec9SNeil Zhang 	}
219*3a082ec9SNeil Zhang 
220*3a082ec9SNeil Zhang 	ehci_mv->cap_regs = ioremap(r->start, resource_size(r));
221*3a082ec9SNeil Zhang 	if (ehci_mv->cap_regs == NULL) {
222*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "failed to map I/O memory\n");
223*3a082ec9SNeil Zhang 		retval = -EFAULT;
224*3a082ec9SNeil Zhang 		goto err_iounmap_phyreg;
225*3a082ec9SNeil Zhang 	}
226*3a082ec9SNeil Zhang 
227*3a082ec9SNeil Zhang 	retval = mv_ehci_enable(ehci_mv);
228*3a082ec9SNeil Zhang 	if (retval) {
229*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "init phy error %d\n", retval);
230*3a082ec9SNeil Zhang 		goto err_iounmap_capreg;
231*3a082ec9SNeil Zhang 	}
232*3a082ec9SNeil Zhang 
233*3a082ec9SNeil Zhang 	offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
234*3a082ec9SNeil Zhang 	ehci_mv->op_regs =
235*3a082ec9SNeil Zhang 		(void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
236*3a082ec9SNeil Zhang 
237*3a082ec9SNeil Zhang 	hcd->rsrc_start = r->start;
238*3a082ec9SNeil Zhang 	hcd->rsrc_len = r->end - r->start + 1;
239*3a082ec9SNeil Zhang 	hcd->regs = ehci_mv->op_regs;
240*3a082ec9SNeil Zhang 
241*3a082ec9SNeil Zhang 	hcd->irq = platform_get_irq(pdev, 0);
242*3a082ec9SNeil Zhang 	if (!hcd->irq) {
243*3a082ec9SNeil Zhang 		dev_err(&pdev->dev, "Cannot get irq.");
244*3a082ec9SNeil Zhang 		retval = -ENODEV;
245*3a082ec9SNeil Zhang 		goto err_disable_clk;
246*3a082ec9SNeil Zhang 	}
247*3a082ec9SNeil Zhang 
248*3a082ec9SNeil Zhang 	ehci = hcd_to_ehci(hcd);
249*3a082ec9SNeil Zhang 	ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
250*3a082ec9SNeil Zhang 	ehci->regs = (struct ehci_regs *) ehci_mv->op_regs;
251*3a082ec9SNeil Zhang 	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
252*3a082ec9SNeil Zhang 
253*3a082ec9SNeil Zhang 	ehci_mv->mode = pdata->mode;
254*3a082ec9SNeil Zhang 	if (ehci_mv->mode == MV_USB_MODE_OTG) {
255*3a082ec9SNeil Zhang #ifdef CONFIG_USB_OTG_UTILS
256*3a082ec9SNeil Zhang 		ehci_mv->otg = otg_get_transceiver();
257*3a082ec9SNeil Zhang 		if (!ehci_mv->otg) {
258*3a082ec9SNeil Zhang 			dev_err(&pdev->dev,
259*3a082ec9SNeil Zhang 				"unable to find transceiver\n");
260*3a082ec9SNeil Zhang 			retval = -ENODEV;
261*3a082ec9SNeil Zhang 			goto err_disable_clk;
262*3a082ec9SNeil Zhang 		}
263*3a082ec9SNeil Zhang 
264*3a082ec9SNeil Zhang 		retval = otg_set_host(ehci_mv->otg, &hcd->self);
265*3a082ec9SNeil Zhang 		if (retval < 0) {
266*3a082ec9SNeil Zhang 			dev_err(&pdev->dev,
267*3a082ec9SNeil Zhang 				"unable to register with transceiver\n");
268*3a082ec9SNeil Zhang 			retval = -ENODEV;
269*3a082ec9SNeil Zhang 			goto err_put_transceiver;
270*3a082ec9SNeil Zhang 		}
271*3a082ec9SNeil Zhang 		/* otg will enable clock before use as host */
272*3a082ec9SNeil Zhang 		mv_ehci_disable(ehci_mv);
273*3a082ec9SNeil Zhang #else
274*3a082ec9SNeil Zhang 		dev_info(&pdev->dev, "MV_USB_MODE_OTG "
275*3a082ec9SNeil Zhang 			 "must have CONFIG_USB_OTG_UTILS enabled\n");
276*3a082ec9SNeil Zhang 		goto err_disable_clk;
277*3a082ec9SNeil Zhang #endif
278*3a082ec9SNeil Zhang 	} else {
279*3a082ec9SNeil Zhang 		if (pdata->set_vbus)
280*3a082ec9SNeil Zhang 			pdata->set_vbus(1);
281*3a082ec9SNeil Zhang 
282*3a082ec9SNeil Zhang 		retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
283*3a082ec9SNeil Zhang 		if (retval) {
284*3a082ec9SNeil Zhang 			dev_err(&pdev->dev,
285*3a082ec9SNeil Zhang 				"failed to add hcd with err %d\n", retval);
286*3a082ec9SNeil Zhang 			goto err_set_vbus;
287*3a082ec9SNeil Zhang 		}
288*3a082ec9SNeil Zhang 	}
289*3a082ec9SNeil Zhang 
290*3a082ec9SNeil Zhang 	if (pdata->private_init)
291*3a082ec9SNeil Zhang 		pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs);
292*3a082ec9SNeil Zhang 
293*3a082ec9SNeil Zhang 	dev_info(&pdev->dev,
294*3a082ec9SNeil Zhang 		 "successful find EHCI device with regs 0x%p irq %d"
295*3a082ec9SNeil Zhang 		 " working in %s mode\n", hcd->regs, hcd->irq,
296*3a082ec9SNeil Zhang 		 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host");
297*3a082ec9SNeil Zhang 
298*3a082ec9SNeil Zhang 	return 0;
299*3a082ec9SNeil Zhang 
300*3a082ec9SNeil Zhang err_set_vbus:
301*3a082ec9SNeil Zhang 	if (pdata->set_vbus)
302*3a082ec9SNeil Zhang 		pdata->set_vbus(0);
303*3a082ec9SNeil Zhang #ifdef CONFIG_USB_OTG_UTILS
304*3a082ec9SNeil Zhang err_put_transceiver:
305*3a082ec9SNeil Zhang 	if (ehci_mv->otg)
306*3a082ec9SNeil Zhang 		otg_put_transceiver(ehci_mv->otg);
307*3a082ec9SNeil Zhang #endif
308*3a082ec9SNeil Zhang err_disable_clk:
309*3a082ec9SNeil Zhang 	mv_ehci_disable(ehci_mv);
310*3a082ec9SNeil Zhang err_iounmap_capreg:
311*3a082ec9SNeil Zhang 	iounmap(ehci_mv->cap_regs);
312*3a082ec9SNeil Zhang err_iounmap_phyreg:
313*3a082ec9SNeil Zhang 	iounmap(ehci_mv->phy_regs);
314*3a082ec9SNeil Zhang err_put_clk:
315*3a082ec9SNeil Zhang 	for (clk_i--; clk_i >= 0; clk_i--)
316*3a082ec9SNeil Zhang 		clk_put(ehci_mv->clk[clk_i]);
317*3a082ec9SNeil Zhang 	platform_set_drvdata(pdev, NULL);
318*3a082ec9SNeil Zhang 	kfree(ehci_mv);
319*3a082ec9SNeil Zhang err_put_hcd:
320*3a082ec9SNeil Zhang 	usb_put_hcd(hcd);
321*3a082ec9SNeil Zhang 
322*3a082ec9SNeil Zhang 	return retval;
323*3a082ec9SNeil Zhang }
324*3a082ec9SNeil Zhang 
325*3a082ec9SNeil Zhang static int mv_ehci_remove(struct platform_device *pdev)
326*3a082ec9SNeil Zhang {
327*3a082ec9SNeil Zhang 	struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
328*3a082ec9SNeil Zhang 	struct usb_hcd *hcd = ehci_mv->hcd;
329*3a082ec9SNeil Zhang 	int clk_i;
330*3a082ec9SNeil Zhang 
331*3a082ec9SNeil Zhang 	if (hcd->rh_registered)
332*3a082ec9SNeil Zhang 		usb_remove_hcd(hcd);
333*3a082ec9SNeil Zhang 
334*3a082ec9SNeil Zhang 	if (ehci_mv->otg) {
335*3a082ec9SNeil Zhang 		otg_set_host(ehci_mv->otg, NULL);
336*3a082ec9SNeil Zhang 		otg_put_transceiver(ehci_mv->otg);
337*3a082ec9SNeil Zhang 	}
338*3a082ec9SNeil Zhang 
339*3a082ec9SNeil Zhang 	if (ehci_mv->mode == MV_USB_MODE_HOST) {
340*3a082ec9SNeil Zhang 		if (ehci_mv->pdata->set_vbus)
341*3a082ec9SNeil Zhang 			ehci_mv->pdata->set_vbus(0);
342*3a082ec9SNeil Zhang 
343*3a082ec9SNeil Zhang 		mv_ehci_disable(ehci_mv);
344*3a082ec9SNeil Zhang 	}
345*3a082ec9SNeil Zhang 
346*3a082ec9SNeil Zhang 	iounmap(ehci_mv->cap_regs);
347*3a082ec9SNeil Zhang 	iounmap(ehci_mv->phy_regs);
348*3a082ec9SNeil Zhang 
349*3a082ec9SNeil Zhang 	for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++)
350*3a082ec9SNeil Zhang 		clk_put(ehci_mv->clk[clk_i]);
351*3a082ec9SNeil Zhang 
352*3a082ec9SNeil Zhang 	platform_set_drvdata(pdev, NULL);
353*3a082ec9SNeil Zhang 
354*3a082ec9SNeil Zhang 	kfree(ehci_mv);
355*3a082ec9SNeil Zhang 	usb_put_hcd(hcd);
356*3a082ec9SNeil Zhang 
357*3a082ec9SNeil Zhang 	return 0;
358*3a082ec9SNeil Zhang }
359*3a082ec9SNeil Zhang 
360*3a082ec9SNeil Zhang MODULE_ALIAS("mv-ehci");
361*3a082ec9SNeil Zhang 
362*3a082ec9SNeil Zhang static const struct platform_device_id ehci_id_table[] = {
363*3a082ec9SNeil Zhang 	{"pxa-u2oehci", PXA_U2OEHCI},
364*3a082ec9SNeil Zhang 	{"pxa-sph", PXA_SPH},
365*3a082ec9SNeil Zhang 	{"mmp3-hsic", MMP3_HSIC},
366*3a082ec9SNeil Zhang 	{"mmp3-fsic", MMP3_FSIC},
367*3a082ec9SNeil Zhang 	{},
368*3a082ec9SNeil Zhang };
369*3a082ec9SNeil Zhang 
370*3a082ec9SNeil Zhang static void mv_ehci_shutdown(struct platform_device *pdev)
371*3a082ec9SNeil Zhang {
372*3a082ec9SNeil Zhang 	struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
373*3a082ec9SNeil Zhang 	struct usb_hcd *hcd = ehci_mv->hcd;
374*3a082ec9SNeil Zhang 
375*3a082ec9SNeil Zhang 	if (!hcd->rh_registered)
376*3a082ec9SNeil Zhang 		return;
377*3a082ec9SNeil Zhang 
378*3a082ec9SNeil Zhang 	if (hcd->driver->shutdown)
379*3a082ec9SNeil Zhang 		hcd->driver->shutdown(hcd);
380*3a082ec9SNeil Zhang }
381*3a082ec9SNeil Zhang 
382*3a082ec9SNeil Zhang static struct platform_driver ehci_mv_driver = {
383*3a082ec9SNeil Zhang 	.probe = mv_ehci_probe,
384*3a082ec9SNeil Zhang 	.remove = mv_ehci_remove,
385*3a082ec9SNeil Zhang 	.shutdown = mv_ehci_shutdown,
386*3a082ec9SNeil Zhang 	.driver = {
387*3a082ec9SNeil Zhang 		   .name = "mv-ehci",
388*3a082ec9SNeil Zhang 		   .bus = &platform_bus_type,
389*3a082ec9SNeil Zhang 		   },
390*3a082ec9SNeil Zhang 	.id_table = ehci_id_table,
391*3a082ec9SNeil Zhang };
392