xref: /OK3568_Linux_fs/kernel/arch/arm/mach-pxa/pxa3xx-ulpi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * linux/arch/arm/mach-pxa/pxa3xx-ulpi.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * code specific to pxa3xx aka Monahans
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Copyright (C) 2010 CompuLab Ltd.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * 2010-13-07: Igor Grinberg <grinberg@compulab.co.il>
10*4882a593Smuzhiyun  *             initial version: pxa310 USB Host mode support
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/device.h>
17*4882a593Smuzhiyun #include <linux/platform_device.h>
18*4882a593Smuzhiyun #include <linux/err.h>
19*4882a593Smuzhiyun #include <linux/io.h>
20*4882a593Smuzhiyun #include <linux/delay.h>
21*4882a593Smuzhiyun #include <linux/clk.h>
22*4882a593Smuzhiyun #include <linux/usb.h>
23*4882a593Smuzhiyun #include <linux/usb/otg.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include <mach/hardware.h>
26*4882a593Smuzhiyun #include "regs-u2d.h"
27*4882a593Smuzhiyun #include <linux/platform_data/usb-pxa3xx-ulpi.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun struct pxa3xx_u2d_ulpi {
30*4882a593Smuzhiyun 	struct clk		*clk;
31*4882a593Smuzhiyun 	void __iomem		*mmio_base;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	struct usb_phy		*otg;
34*4882a593Smuzhiyun 	unsigned int		ulpi_mode;
35*4882a593Smuzhiyun };
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun static struct pxa3xx_u2d_ulpi *u2d;
38*4882a593Smuzhiyun 
u2d_readl(u32 reg)39*4882a593Smuzhiyun static inline u32 u2d_readl(u32 reg)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	return __raw_readl(u2d->mmio_base + reg);
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun 
u2d_writel(u32 reg,u32 val)44*4882a593Smuzhiyun static inline void u2d_writel(u32 reg, u32 val)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	__raw_writel(val, u2d->mmio_base + reg);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun #if defined(CONFIG_PXA310_ULPI)
50*4882a593Smuzhiyun enum u2d_ulpi_phy_mode {
51*4882a593Smuzhiyun 	SYNCH		= 0,
52*4882a593Smuzhiyun 	CARKIT		= (1 << 0),
53*4882a593Smuzhiyun 	SER_3PIN	= (1 << 1),
54*4882a593Smuzhiyun 	SER_6PIN	= (1 << 2),
55*4882a593Smuzhiyun 	LOWPOWER	= (1 << 3),
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun 
pxa310_ulpi_get_phymode(void)58*4882a593Smuzhiyun static inline enum u2d_ulpi_phy_mode pxa310_ulpi_get_phymode(void)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	return (u2d_readl(U2DOTGUSR) >> 28) & 0xF;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun 
pxa310_ulpi_poll(void)63*4882a593Smuzhiyun static int pxa310_ulpi_poll(void)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	int timeout = 50000;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	while (timeout--) {
68*4882a593Smuzhiyun 		if (!(u2d_readl(U2DOTGUCR) & U2DOTGUCR_RUN))
69*4882a593Smuzhiyun 			return 0;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 		cpu_relax();
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	pr_warn("%s: ULPI access timed out!\n", __func__);
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	return -ETIMEDOUT;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
pxa310_ulpi_read(struct usb_phy * otg,u32 reg)79*4882a593Smuzhiyun static int pxa310_ulpi_read(struct usb_phy *otg, u32 reg)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	int err;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	if (pxa310_ulpi_get_phymode() != SYNCH) {
84*4882a593Smuzhiyun 		pr_warn("%s: PHY is not in SYNCH mode!\n", __func__);
85*4882a593Smuzhiyun 		return -EBUSY;
86*4882a593Smuzhiyun 	}
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	u2d_writel(U2DOTGUCR, U2DOTGUCR_RUN | U2DOTGUCR_RNW | (reg << 16));
89*4882a593Smuzhiyun 	msleep(5);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	err = pxa310_ulpi_poll();
92*4882a593Smuzhiyun 	if (err)
93*4882a593Smuzhiyun 		return err;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	return u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
pxa310_ulpi_write(struct usb_phy * otg,u32 val,u32 reg)98*4882a593Smuzhiyun static int pxa310_ulpi_write(struct usb_phy *otg, u32 val, u32 reg)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	if (pxa310_ulpi_get_phymode() != SYNCH) {
101*4882a593Smuzhiyun 		pr_warn("%s: PHY is not in SYNCH mode!\n", __func__);
102*4882a593Smuzhiyun 		return -EBUSY;
103*4882a593Smuzhiyun 	}
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	u2d_writel(U2DOTGUCR, U2DOTGUCR_RUN | (reg << 16) | (val << 8));
106*4882a593Smuzhiyun 	msleep(5);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return pxa310_ulpi_poll();
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun struct usb_phy_io_ops pxa310_ulpi_access_ops = {
112*4882a593Smuzhiyun 	.read	= pxa310_ulpi_read,
113*4882a593Smuzhiyun 	.write	= pxa310_ulpi_write,
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun 
pxa310_otg_transceiver_rtsm(void)116*4882a593Smuzhiyun static void pxa310_otg_transceiver_rtsm(void)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	u32 u2dotgcr;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	/* put PHY to sync mode */
121*4882a593Smuzhiyun 	u2dotgcr = u2d_readl(U2DOTGCR);
122*4882a593Smuzhiyun 	u2dotgcr |=  U2DOTGCR_RTSM | U2DOTGCR_UTMID;
123*4882a593Smuzhiyun 	u2d_writel(U2DOTGCR, u2dotgcr);
124*4882a593Smuzhiyun 	msleep(10);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	/* setup OTG sync mode */
127*4882a593Smuzhiyun 	u2dotgcr = u2d_readl(U2DOTGCR);
128*4882a593Smuzhiyun 	u2dotgcr |= U2DOTGCR_ULAF;
129*4882a593Smuzhiyun 	u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
130*4882a593Smuzhiyun 	u2d_writel(U2DOTGCR, u2dotgcr);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
pxa310_start_otg_host_transcvr(struct usb_bus * host)133*4882a593Smuzhiyun static int pxa310_start_otg_host_transcvr(struct usb_bus *host)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	int err;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	pxa310_otg_transceiver_rtsm();
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	err = usb_phy_init(u2d->otg);
140*4882a593Smuzhiyun 	if (err) {
141*4882a593Smuzhiyun 		pr_err("OTG transceiver init failed");
142*4882a593Smuzhiyun 		return err;
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	err = otg_set_vbus(u2d->otg->otg, 1);
146*4882a593Smuzhiyun 	if (err) {
147*4882a593Smuzhiyun 		pr_err("OTG transceiver VBUS set failed");
148*4882a593Smuzhiyun 		return err;
149*4882a593Smuzhiyun 	}
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	err = otg_set_host(u2d->otg->otg, host);
152*4882a593Smuzhiyun 	if (err)
153*4882a593Smuzhiyun 		pr_err("OTG transceiver Host mode set failed");
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	return err;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
pxa310_start_otg_hc(struct usb_bus * host)158*4882a593Smuzhiyun static int pxa310_start_otg_hc(struct usb_bus *host)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	u32 u2dotgcr;
161*4882a593Smuzhiyun 	int err;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	/* disable USB device controller */
164*4882a593Smuzhiyun 	u2d_writel(U2DCR, u2d_readl(U2DCR) & ~U2DCR_UDE);
165*4882a593Smuzhiyun 	u2d_writel(U2DOTGCR, u2d_readl(U2DOTGCR) | U2DOTGCR_UTMID);
166*4882a593Smuzhiyun 	u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~0x37F7F);
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	err = pxa310_start_otg_host_transcvr(host);
169*4882a593Smuzhiyun 	if (err)
170*4882a593Smuzhiyun 		return err;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	/* set xceiver mode */
173*4882a593Smuzhiyun 	if (u2d->ulpi_mode & ULPI_IC_6PIN_SERIAL)
174*4882a593Smuzhiyun 		u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) & ~U2DP3CR_P2SS);
175*4882a593Smuzhiyun 	else if (u2d->ulpi_mode & ULPI_IC_3PIN_SERIAL)
176*4882a593Smuzhiyun 		u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) | U2DP3CR_P2SS);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	/* start OTG host controller */
179*4882a593Smuzhiyun 	u2dotgcr = u2d_readl(U2DOTGCR) | U2DOTGCR_SMAF;
180*4882a593Smuzhiyun 	u2d_writel(U2DOTGCR, u2dotgcr & ~(U2DOTGCR_ULAF | U2DOTGCR_CKAF));
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	return 0;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun 
pxa310_stop_otg_hc(void)185*4882a593Smuzhiyun static void pxa310_stop_otg_hc(void)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	pxa310_otg_transceiver_rtsm();
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	otg_set_host(u2d->otg->otg, NULL);
190*4882a593Smuzhiyun 	otg_set_vbus(u2d->otg->otg, 0);
191*4882a593Smuzhiyun 	usb_phy_shutdown(u2d->otg);
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
pxa310_u2d_setup_otg_hc(void)194*4882a593Smuzhiyun static void pxa310_u2d_setup_otg_hc(void)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	u32 u2dotgcr;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	u2dotgcr = u2d_readl(U2DOTGCR);
199*4882a593Smuzhiyun 	u2dotgcr |= U2DOTGCR_ULAF | U2DOTGCR_UTMID;
200*4882a593Smuzhiyun 	u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
201*4882a593Smuzhiyun 	u2d_writel(U2DOTGCR, u2dotgcr);
202*4882a593Smuzhiyun 	msleep(5);
203*4882a593Smuzhiyun 	u2d_writel(U2DOTGCR, u2dotgcr | U2DOTGCR_ULE);
204*4882a593Smuzhiyun 	msleep(5);
205*4882a593Smuzhiyun 	u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~0x37F7F);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
pxa310_otg_init(struct pxa3xx_u2d_platform_data * pdata)208*4882a593Smuzhiyun static int pxa310_otg_init(struct pxa3xx_u2d_platform_data *pdata)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun 	unsigned int ulpi_mode = ULPI_OTG_DRVVBUS;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	if (pdata) {
213*4882a593Smuzhiyun 		if (pdata->ulpi_mode & ULPI_SER_6PIN)
214*4882a593Smuzhiyun 			ulpi_mode |= ULPI_IC_6PIN_SERIAL;
215*4882a593Smuzhiyun 		else if (pdata->ulpi_mode & ULPI_SER_3PIN)
216*4882a593Smuzhiyun 			ulpi_mode |= ULPI_IC_3PIN_SERIAL;
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	u2d->ulpi_mode = ulpi_mode;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	u2d->otg = otg_ulpi_create(&pxa310_ulpi_access_ops, ulpi_mode);
222*4882a593Smuzhiyun 	if (!u2d->otg)
223*4882a593Smuzhiyun 		return -ENOMEM;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	u2d->otg->io_priv = u2d->mmio_base;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	return 0;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun 
pxa310_otg_exit(void)230*4882a593Smuzhiyun static void pxa310_otg_exit(void)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	kfree(u2d->otg);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun #else
pxa310_u2d_setup_otg_hc(void)235*4882a593Smuzhiyun static inline void pxa310_u2d_setup_otg_hc(void) {}
pxa310_start_otg_hc(struct usb_bus * host)236*4882a593Smuzhiyun static inline int pxa310_start_otg_hc(struct usb_bus *host)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun 	return 0;
239*4882a593Smuzhiyun }
pxa310_stop_otg_hc(void)240*4882a593Smuzhiyun static inline void pxa310_stop_otg_hc(void) {}
pxa310_otg_init(struct pxa3xx_u2d_platform_data * pdata)241*4882a593Smuzhiyun static inline int pxa310_otg_init(struct pxa3xx_u2d_platform_data *pdata)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	return 0;
244*4882a593Smuzhiyun }
pxa310_otg_exit(void)245*4882a593Smuzhiyun static inline void pxa310_otg_exit(void) {}
246*4882a593Smuzhiyun #endif /* CONFIG_PXA310_ULPI */
247*4882a593Smuzhiyun 
pxa3xx_u2d_start_hc(struct usb_bus * host)248*4882a593Smuzhiyun int pxa3xx_u2d_start_hc(struct usb_bus *host)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun 	int err = 0;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	/* In case the PXA3xx ULPI isn't used, do nothing. */
253*4882a593Smuzhiyun 	if (!u2d)
254*4882a593Smuzhiyun 		return 0;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	clk_prepare_enable(u2d->clk);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	if (cpu_is_pxa310()) {
259*4882a593Smuzhiyun 		pxa310_u2d_setup_otg_hc();
260*4882a593Smuzhiyun 		err = pxa310_start_otg_hc(host);
261*4882a593Smuzhiyun 	}
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	return err;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pxa3xx_u2d_start_hc);
266*4882a593Smuzhiyun 
pxa3xx_u2d_stop_hc(struct usb_bus * host)267*4882a593Smuzhiyun void pxa3xx_u2d_stop_hc(struct usb_bus *host)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	/* In case the PXA3xx ULPI isn't used, do nothing. */
270*4882a593Smuzhiyun 	if (!u2d)
271*4882a593Smuzhiyun 		return;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	if (cpu_is_pxa310())
274*4882a593Smuzhiyun 		pxa310_stop_otg_hc();
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	clk_disable_unprepare(u2d->clk);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pxa3xx_u2d_stop_hc);
279*4882a593Smuzhiyun 
pxa3xx_u2d_probe(struct platform_device * pdev)280*4882a593Smuzhiyun static int pxa3xx_u2d_probe(struct platform_device *pdev)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun 	struct pxa3xx_u2d_platform_data *pdata = pdev->dev.platform_data;
283*4882a593Smuzhiyun 	struct resource *r;
284*4882a593Smuzhiyun 	int err;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	u2d = kzalloc(sizeof(*u2d), GFP_KERNEL);
287*4882a593Smuzhiyun 	if (!u2d)
288*4882a593Smuzhiyun 		return -ENOMEM;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	u2d->clk = clk_get(&pdev->dev, NULL);
291*4882a593Smuzhiyun 	if (IS_ERR(u2d->clk)) {
292*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to get u2d clock\n");
293*4882a593Smuzhiyun 		err = PTR_ERR(u2d->clk);
294*4882a593Smuzhiyun 		goto err_free_mem;
295*4882a593Smuzhiyun 	}
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
298*4882a593Smuzhiyun 	if (!r) {
299*4882a593Smuzhiyun 		dev_err(&pdev->dev, "no IO memory resource defined\n");
300*4882a593Smuzhiyun 		err = -ENODEV;
301*4882a593Smuzhiyun 		goto err_put_clk;
302*4882a593Smuzhiyun 	}
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun         r = request_mem_region(r->start, resource_size(r), pdev->name);
305*4882a593Smuzhiyun         if (!r) {
306*4882a593Smuzhiyun                 dev_err(&pdev->dev, "failed to request memory resource\n");
307*4882a593Smuzhiyun                 err = -EBUSY;
308*4882a593Smuzhiyun                 goto err_put_clk;
309*4882a593Smuzhiyun         }
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	u2d->mmio_base = ioremap(r->start, resource_size(r));
312*4882a593Smuzhiyun 	if (!u2d->mmio_base) {
313*4882a593Smuzhiyun 		dev_err(&pdev->dev, "ioremap() failed\n");
314*4882a593Smuzhiyun 		err = -ENODEV;
315*4882a593Smuzhiyun 		goto err_free_res;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	if (pdata->init) {
319*4882a593Smuzhiyun 		err = pdata->init(&pdev->dev);
320*4882a593Smuzhiyun 		if (err)
321*4882a593Smuzhiyun 			goto err_free_io;
322*4882a593Smuzhiyun 	}
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	/* Only PXA310 U2D has OTG functionality */
325*4882a593Smuzhiyun 	if (cpu_is_pxa310()) {
326*4882a593Smuzhiyun 		err = pxa310_otg_init(pdata);
327*4882a593Smuzhiyun 		if (err)
328*4882a593Smuzhiyun 			goto err_free_plat;
329*4882a593Smuzhiyun 	}
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	platform_set_drvdata(pdev, u2d);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	return 0;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun err_free_plat:
336*4882a593Smuzhiyun 	if (pdata->exit)
337*4882a593Smuzhiyun 		pdata->exit(&pdev->dev);
338*4882a593Smuzhiyun err_free_io:
339*4882a593Smuzhiyun 	iounmap(u2d->mmio_base);
340*4882a593Smuzhiyun err_free_res:
341*4882a593Smuzhiyun 	release_mem_region(r->start, resource_size(r));
342*4882a593Smuzhiyun err_put_clk:
343*4882a593Smuzhiyun 	clk_put(u2d->clk);
344*4882a593Smuzhiyun err_free_mem:
345*4882a593Smuzhiyun 	kfree(u2d);
346*4882a593Smuzhiyun 	return err;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun 
pxa3xx_u2d_remove(struct platform_device * pdev)349*4882a593Smuzhiyun static int pxa3xx_u2d_remove(struct platform_device *pdev)
350*4882a593Smuzhiyun {
351*4882a593Smuzhiyun 	struct pxa3xx_u2d_platform_data *pdata = pdev->dev.platform_data;
352*4882a593Smuzhiyun 	struct resource *r;
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	if (cpu_is_pxa310()) {
355*4882a593Smuzhiyun 		pxa310_stop_otg_hc();
356*4882a593Smuzhiyun 		pxa310_otg_exit();
357*4882a593Smuzhiyun 	}
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	if (pdata->exit)
360*4882a593Smuzhiyun 		pdata->exit(&pdev->dev);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	platform_set_drvdata(pdev, NULL);
363*4882a593Smuzhiyun 	iounmap(u2d->mmio_base);
364*4882a593Smuzhiyun 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
365*4882a593Smuzhiyun 	release_mem_region(r->start, resource_size(r));
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	clk_put(u2d->clk);
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	kfree(u2d);
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	return 0;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun static struct platform_driver pxa3xx_u2d_ulpi_driver = {
375*4882a593Smuzhiyun         .driver		= {
376*4882a593Smuzhiyun                 .name   = "pxa3xx-u2d",
377*4882a593Smuzhiyun         },
378*4882a593Smuzhiyun         .probe          = pxa3xx_u2d_probe,
379*4882a593Smuzhiyun         .remove         = pxa3xx_u2d_remove,
380*4882a593Smuzhiyun };
381*4882a593Smuzhiyun module_platform_driver(pxa3xx_u2d_ulpi_driver);
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun MODULE_DESCRIPTION("PXA3xx U2D ULPI driver");
384*4882a593Smuzhiyun MODULE_AUTHOR("Igor Grinberg");
385*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
386