xref: /openbmc/linux/drivers/usb/host/ohci-da8xx.c (revision efe7daf2231a6beb59f0f641461d19fa62fb83ab)
1*efe7daf2SSergei Shtylyov /*
2*efe7daf2SSergei Shtylyov  * OHCI HCD (Host Controller Driver) for USB.
3*efe7daf2SSergei Shtylyov  *
4*efe7daf2SSergei Shtylyov  * TI DA8xx (OMAP-L1x) Bus Glue
5*efe7daf2SSergei Shtylyov  *
6*efe7daf2SSergei Shtylyov  * Derived from: ohci-omap.c and ohci-s3c2410.c
7*efe7daf2SSergei Shtylyov  * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
8*efe7daf2SSergei Shtylyov  *
9*efe7daf2SSergei Shtylyov  * This file is licensed under the terms of the GNU General Public License
10*efe7daf2SSergei Shtylyov  * version 2. This program is licensed "as is" without any warranty of any
11*efe7daf2SSergei Shtylyov  * kind, whether express or implied.
12*efe7daf2SSergei Shtylyov  */
13*efe7daf2SSergei Shtylyov 
14*efe7daf2SSergei Shtylyov #include <linux/interrupt.h>
15*efe7daf2SSergei Shtylyov #include <linux/jiffies.h>
16*efe7daf2SSergei Shtylyov #include <linux/platform_device.h>
17*efe7daf2SSergei Shtylyov #include <linux/clk.h>
18*efe7daf2SSergei Shtylyov 
19*efe7daf2SSergei Shtylyov #include <mach/da8xx.h>
20*efe7daf2SSergei Shtylyov #include <mach/usb.h>
21*efe7daf2SSergei Shtylyov 
22*efe7daf2SSergei Shtylyov #ifndef CONFIG_ARCH_DAVINCI_DA8XX
23*efe7daf2SSergei Shtylyov #error "This file is DA8xx bus glue.  Define CONFIG_ARCH_DAVINCI_DA8XX."
24*efe7daf2SSergei Shtylyov #endif
25*efe7daf2SSergei Shtylyov 
26*efe7daf2SSergei Shtylyov #define CFGCHIP2	DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP2_REG)
27*efe7daf2SSergei Shtylyov 
28*efe7daf2SSergei Shtylyov static struct clk *usb11_clk;
29*efe7daf2SSergei Shtylyov static struct clk *usb20_clk;
30*efe7daf2SSergei Shtylyov 
31*efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */
32*efe7daf2SSergei Shtylyov static volatile u16 ocic_mask;
33*efe7daf2SSergei Shtylyov 
34*efe7daf2SSergei Shtylyov static void ohci_da8xx_clock(int on)
35*efe7daf2SSergei Shtylyov {
36*efe7daf2SSergei Shtylyov 	u32 cfgchip2;
37*efe7daf2SSergei Shtylyov 
38*efe7daf2SSergei Shtylyov 	cfgchip2 = __raw_readl(CFGCHIP2);
39*efe7daf2SSergei Shtylyov 	if (on) {
40*efe7daf2SSergei Shtylyov 		clk_enable(usb11_clk);
41*efe7daf2SSergei Shtylyov 
42*efe7daf2SSergei Shtylyov 		/*
43*efe7daf2SSergei Shtylyov 		 * If USB 1.1 reference clock is sourced from USB 2.0 PHY, we
44*efe7daf2SSergei Shtylyov 		 * need to enable the USB 2.0 module clocking, start its PHY,
45*efe7daf2SSergei Shtylyov 		 * and not allow it to stop the clock during USB 2.0 suspend.
46*efe7daf2SSergei Shtylyov 		 */
47*efe7daf2SSergei Shtylyov 		if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) {
48*efe7daf2SSergei Shtylyov 			clk_enable(usb20_clk);
49*efe7daf2SSergei Shtylyov 
50*efe7daf2SSergei Shtylyov 			cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN);
51*efe7daf2SSergei Shtylyov 			cfgchip2 |= CFGCHIP2_PHY_PLLON;
52*efe7daf2SSergei Shtylyov 			__raw_writel(cfgchip2, CFGCHIP2);
53*efe7daf2SSergei Shtylyov 
54*efe7daf2SSergei Shtylyov 			pr_info("Waiting for USB PHY clock good...\n");
55*efe7daf2SSergei Shtylyov 			while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD))
56*efe7daf2SSergei Shtylyov 				cpu_relax();
57*efe7daf2SSergei Shtylyov 		}
58*efe7daf2SSergei Shtylyov 
59*efe7daf2SSergei Shtylyov 		/* Enable USB 1.1 PHY */
60*efe7daf2SSergei Shtylyov 		cfgchip2 |= CFGCHIP2_USB1SUSPENDM;
61*efe7daf2SSergei Shtylyov 	} else {
62*efe7daf2SSergei Shtylyov 		clk_disable(usb11_clk);
63*efe7daf2SSergei Shtylyov 		if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX))
64*efe7daf2SSergei Shtylyov 			clk_disable(usb20_clk);
65*efe7daf2SSergei Shtylyov 
66*efe7daf2SSergei Shtylyov 		/* Disable USB 1.1 PHY */
67*efe7daf2SSergei Shtylyov 		cfgchip2 &= ~CFGCHIP2_USB1SUSPENDM;
68*efe7daf2SSergei Shtylyov 	}
69*efe7daf2SSergei Shtylyov 	__raw_writel(cfgchip2, CFGCHIP2);
70*efe7daf2SSergei Shtylyov }
71*efe7daf2SSergei Shtylyov 
72*efe7daf2SSergei Shtylyov /*
73*efe7daf2SSergei Shtylyov  * Handle the port over-current indicator change.
74*efe7daf2SSergei Shtylyov  */
75*efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
76*efe7daf2SSergei Shtylyov 				    unsigned port)
77*efe7daf2SSergei Shtylyov {
78*efe7daf2SSergei Shtylyov 	ocic_mask |= 1 << port;
79*efe7daf2SSergei Shtylyov 
80*efe7daf2SSergei Shtylyov 	/* Once over-current is detected, the port needs to be powered down */
81*efe7daf2SSergei Shtylyov 	if (hub->get_oci(port) > 0)
82*efe7daf2SSergei Shtylyov 		hub->set_power(port, 0);
83*efe7daf2SSergei Shtylyov }
84*efe7daf2SSergei Shtylyov 
85*efe7daf2SSergei Shtylyov static int ohci_da8xx_init(struct usb_hcd *hcd)
86*efe7daf2SSergei Shtylyov {
87*efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
88*efe7daf2SSergei Shtylyov 	struct da8xx_ohci_root_hub *hub	= dev->platform_data;
89*efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci		= hcd_to_ohci(hcd);
90*efe7daf2SSergei Shtylyov 	int result;
91*efe7daf2SSergei Shtylyov 	u32 rh_a;
92*efe7daf2SSergei Shtylyov 
93*efe7daf2SSergei Shtylyov 	dev_dbg(dev, "starting USB controller\n");
94*efe7daf2SSergei Shtylyov 
95*efe7daf2SSergei Shtylyov 	ohci_da8xx_clock(1);
96*efe7daf2SSergei Shtylyov 
97*efe7daf2SSergei Shtylyov 	/*
98*efe7daf2SSergei Shtylyov 	 * DA8xx only have 1 port connected to the pins but the HC root hub
99*efe7daf2SSergei Shtylyov 	 * register A reports 2 ports, thus we'll have to override it...
100*efe7daf2SSergei Shtylyov 	 */
101*efe7daf2SSergei Shtylyov 	ohci->num_ports = 1;
102*efe7daf2SSergei Shtylyov 
103*efe7daf2SSergei Shtylyov 	result = ohci_init(ohci);
104*efe7daf2SSergei Shtylyov 	if (result < 0)
105*efe7daf2SSergei Shtylyov 		return result;
106*efe7daf2SSergei Shtylyov 
107*efe7daf2SSergei Shtylyov 	/*
108*efe7daf2SSergei Shtylyov 	 * Since we're providing a board-specific root hub port power control
109*efe7daf2SSergei Shtylyov 	 * and over-current reporting, we have to override the HC root hub A
110*efe7daf2SSergei Shtylyov 	 * register's default value, so that ohci_hub_control() could return
111*efe7daf2SSergei Shtylyov 	 * the correct hub descriptor...
112*efe7daf2SSergei Shtylyov 	 */
113*efe7daf2SSergei Shtylyov 	rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
114*efe7daf2SSergei Shtylyov 	if (hub->set_power) {
115*efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NPS;
116*efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_PSM;
117*efe7daf2SSergei Shtylyov 	}
118*efe7daf2SSergei Shtylyov 	if (hub->get_oci) {
119*efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NOCP;
120*efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_OCPM;
121*efe7daf2SSergei Shtylyov 	}
122*efe7daf2SSergei Shtylyov 	rh_a &= ~RH_A_POTPGT;
123*efe7daf2SSergei Shtylyov 	rh_a |= hub->potpgt << 24;
124*efe7daf2SSergei Shtylyov 	ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
125*efe7daf2SSergei Shtylyov 
126*efe7daf2SSergei Shtylyov 	return result;
127*efe7daf2SSergei Shtylyov }
128*efe7daf2SSergei Shtylyov 
129*efe7daf2SSergei Shtylyov static void ohci_da8xx_stop(struct usb_hcd *hcd)
130*efe7daf2SSergei Shtylyov {
131*efe7daf2SSergei Shtylyov 	ohci_stop(hcd);
132*efe7daf2SSergei Shtylyov 	ohci_da8xx_clock(0);
133*efe7daf2SSergei Shtylyov }
134*efe7daf2SSergei Shtylyov 
135*efe7daf2SSergei Shtylyov static int ohci_da8xx_start(struct usb_hcd *hcd)
136*efe7daf2SSergei Shtylyov {
137*efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci		= hcd_to_ohci(hcd);
138*efe7daf2SSergei Shtylyov 	int result;
139*efe7daf2SSergei Shtylyov 
140*efe7daf2SSergei Shtylyov 	result = ohci_run(ohci);
141*efe7daf2SSergei Shtylyov 	if (result < 0)
142*efe7daf2SSergei Shtylyov 		ohci_da8xx_stop(hcd);
143*efe7daf2SSergei Shtylyov 
144*efe7daf2SSergei Shtylyov 	return result;
145*efe7daf2SSergei Shtylyov }
146*efe7daf2SSergei Shtylyov 
147*efe7daf2SSergei Shtylyov /*
148*efe7daf2SSergei Shtylyov  * Update the status data from the hub with the over-current indicator change.
149*efe7daf2SSergei Shtylyov  */
150*efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
151*efe7daf2SSergei Shtylyov {
152*efe7daf2SSergei Shtylyov 	int length		= ohci_hub_status_data(hcd, buf);
153*efe7daf2SSergei Shtylyov 
154*efe7daf2SSergei Shtylyov 	/* See if we have OCIC bit set on port 1 */
155*efe7daf2SSergei Shtylyov 	if (ocic_mask & (1 << 1)) {
156*efe7daf2SSergei Shtylyov 		dev_dbg(hcd->self.controller, "over-current indicator change "
157*efe7daf2SSergei Shtylyov 			"on port 1\n");
158*efe7daf2SSergei Shtylyov 
159*efe7daf2SSergei Shtylyov 		if (!length)
160*efe7daf2SSergei Shtylyov 			length = 1;
161*efe7daf2SSergei Shtylyov 
162*efe7daf2SSergei Shtylyov 		buf[0] |= 1 << 1;
163*efe7daf2SSergei Shtylyov 	}
164*efe7daf2SSergei Shtylyov 	return length;
165*efe7daf2SSergei Shtylyov }
166*efe7daf2SSergei Shtylyov 
167*efe7daf2SSergei Shtylyov /*
168*efe7daf2SSergei Shtylyov  * Look at the control requests to the root hub and see if we need to override.
169*efe7daf2SSergei Shtylyov  */
170*efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
171*efe7daf2SSergei Shtylyov 				  u16 wIndex, char *buf, u16 wLength)
172*efe7daf2SSergei Shtylyov {
173*efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
174*efe7daf2SSergei Shtylyov 	struct da8xx_ohci_root_hub *hub	= dev->platform_data;
175*efe7daf2SSergei Shtylyov 	int temp;
176*efe7daf2SSergei Shtylyov 
177*efe7daf2SSergei Shtylyov 	switch (typeReq) {
178*efe7daf2SSergei Shtylyov 	case GetPortStatus:
179*efe7daf2SSergei Shtylyov 		/* Check the port number */
180*efe7daf2SSergei Shtylyov 		if (wIndex != 1)
181*efe7daf2SSergei Shtylyov 			break;
182*efe7daf2SSergei Shtylyov 
183*efe7daf2SSergei Shtylyov 		dev_dbg(dev, "GetPortStatus(%u)\n", wIndex);
184*efe7daf2SSergei Shtylyov 
185*efe7daf2SSergei Shtylyov 		temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
186*efe7daf2SSergei Shtylyov 
187*efe7daf2SSergei Shtylyov 		/* The port power status (PPS) bit defaults to 1 */
188*efe7daf2SSergei Shtylyov 		if (hub->get_power && hub->get_power(wIndex) == 0)
189*efe7daf2SSergei Shtylyov 			temp &= ~RH_PS_PPS;
190*efe7daf2SSergei Shtylyov 
191*efe7daf2SSergei Shtylyov 		/* The port over-current indicator (POCI) bit is always 0 */
192*efe7daf2SSergei Shtylyov 		if (hub->get_oci && hub->get_oci(wIndex) > 0)
193*efe7daf2SSergei Shtylyov 			temp |=  RH_PS_POCI;
194*efe7daf2SSergei Shtylyov 
195*efe7daf2SSergei Shtylyov 		/* The over-current indicator change (OCIC) bit is 0 too */
196*efe7daf2SSergei Shtylyov 		if (ocic_mask & (1 << wIndex))
197*efe7daf2SSergei Shtylyov 			temp |=  RH_PS_OCIC;
198*efe7daf2SSergei Shtylyov 
199*efe7daf2SSergei Shtylyov 		put_unaligned(cpu_to_le32(temp), (__le32 *)buf);
200*efe7daf2SSergei Shtylyov 		return 0;
201*efe7daf2SSergei Shtylyov 	case SetPortFeature:
202*efe7daf2SSergei Shtylyov 		temp = 1;
203*efe7daf2SSergei Shtylyov 		goto check_port;
204*efe7daf2SSergei Shtylyov 	case ClearPortFeature:
205*efe7daf2SSergei Shtylyov 		temp = 0;
206*efe7daf2SSergei Shtylyov 
207*efe7daf2SSergei Shtylyov check_port:
208*efe7daf2SSergei Shtylyov 		/* Check the port number */
209*efe7daf2SSergei Shtylyov 		if (wIndex != 1)
210*efe7daf2SSergei Shtylyov 			break;
211*efe7daf2SSergei Shtylyov 
212*efe7daf2SSergei Shtylyov 		switch (wValue) {
213*efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_POWER:
214*efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
215*efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex, "POWER");
216*efe7daf2SSergei Shtylyov 
217*efe7daf2SSergei Shtylyov 			if (!hub->set_power)
218*efe7daf2SSergei Shtylyov 				return -EPIPE;
219*efe7daf2SSergei Shtylyov 
220*efe7daf2SSergei Shtylyov 			return hub->set_power(wIndex, temp) ? -EPIPE : 0;
221*efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_C_OVER_CURRENT:
222*efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
223*efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex,
224*efe7daf2SSergei Shtylyov 				"C_OVER_CURRENT");
225*efe7daf2SSergei Shtylyov 
226*efe7daf2SSergei Shtylyov 			if (temp)
227*efe7daf2SSergei Shtylyov 				ocic_mask |= 1 << wIndex;
228*efe7daf2SSergei Shtylyov 			else
229*efe7daf2SSergei Shtylyov 				ocic_mask &= ~(1 << wIndex);
230*efe7daf2SSergei Shtylyov 			return 0;
231*efe7daf2SSergei Shtylyov 		}
232*efe7daf2SSergei Shtylyov 	}
233*efe7daf2SSergei Shtylyov 
234*efe7daf2SSergei Shtylyov 	return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
235*efe7daf2SSergei Shtylyov }
236*efe7daf2SSergei Shtylyov 
237*efe7daf2SSergei Shtylyov static const struct hc_driver ohci_da8xx_hc_driver = {
238*efe7daf2SSergei Shtylyov 	.description		= hcd_name,
239*efe7daf2SSergei Shtylyov 	.product_desc		= "DA8xx OHCI",
240*efe7daf2SSergei Shtylyov 	.hcd_priv_size		= sizeof(struct ohci_hcd),
241*efe7daf2SSergei Shtylyov 
242*efe7daf2SSergei Shtylyov 	/*
243*efe7daf2SSergei Shtylyov 	 * generic hardware linkage
244*efe7daf2SSergei Shtylyov 	 */
245*efe7daf2SSergei Shtylyov 	.irq			= ohci_irq,
246*efe7daf2SSergei Shtylyov 	.flags			= HCD_USB11 | HCD_MEMORY,
247*efe7daf2SSergei Shtylyov 
248*efe7daf2SSergei Shtylyov 	/*
249*efe7daf2SSergei Shtylyov 	 * basic lifecycle operations
250*efe7daf2SSergei Shtylyov 	 */
251*efe7daf2SSergei Shtylyov 	.reset			= ohci_da8xx_init,
252*efe7daf2SSergei Shtylyov 	.start			= ohci_da8xx_start,
253*efe7daf2SSergei Shtylyov 	.stop			= ohci_da8xx_stop,
254*efe7daf2SSergei Shtylyov 	.shutdown		= ohci_shutdown,
255*efe7daf2SSergei Shtylyov 
256*efe7daf2SSergei Shtylyov 	/*
257*efe7daf2SSergei Shtylyov 	 * managing i/o requests and associated device resources
258*efe7daf2SSergei Shtylyov 	 */
259*efe7daf2SSergei Shtylyov 	.urb_enqueue		= ohci_urb_enqueue,
260*efe7daf2SSergei Shtylyov 	.urb_dequeue		= ohci_urb_dequeue,
261*efe7daf2SSergei Shtylyov 	.endpoint_disable	= ohci_endpoint_disable,
262*efe7daf2SSergei Shtylyov 
263*efe7daf2SSergei Shtylyov 	/*
264*efe7daf2SSergei Shtylyov 	 * scheduling support
265*efe7daf2SSergei Shtylyov 	 */
266*efe7daf2SSergei Shtylyov 	.get_frame_number	= ohci_get_frame,
267*efe7daf2SSergei Shtylyov 
268*efe7daf2SSergei Shtylyov 	/*
269*efe7daf2SSergei Shtylyov 	 * root hub support
270*efe7daf2SSergei Shtylyov 	 */
271*efe7daf2SSergei Shtylyov 	.hub_status_data	= ohci_da8xx_hub_status_data,
272*efe7daf2SSergei Shtylyov 	.hub_control		= ohci_da8xx_hub_control,
273*efe7daf2SSergei Shtylyov 
274*efe7daf2SSergei Shtylyov #ifdef	CONFIG_PM
275*efe7daf2SSergei Shtylyov 	.bus_suspend		= ohci_bus_suspend,
276*efe7daf2SSergei Shtylyov 	.bus_resume		= ohci_bus_resume,
277*efe7daf2SSergei Shtylyov #endif
278*efe7daf2SSergei Shtylyov 	.start_port_reset	= ohci_start_port_reset,
279*efe7daf2SSergei Shtylyov };
280*efe7daf2SSergei Shtylyov 
281*efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/
282*efe7daf2SSergei Shtylyov 
283*efe7daf2SSergei Shtylyov 
284*efe7daf2SSergei Shtylyov /**
285*efe7daf2SSergei Shtylyov  * usb_hcd_da8xx_probe - initialize DA8xx-based HCDs
286*efe7daf2SSergei Shtylyov  * Context: !in_interrupt()
287*efe7daf2SSergei Shtylyov  *
288*efe7daf2SSergei Shtylyov  * Allocates basic resources for this USB host controller, and
289*efe7daf2SSergei Shtylyov  * then invokes the start() method for the HCD associated with it
290*efe7daf2SSergei Shtylyov  * through the hotplug entry's driver_data.
291*efe7daf2SSergei Shtylyov  */
292*efe7daf2SSergei Shtylyov static int usb_hcd_da8xx_probe(const struct hc_driver *driver,
293*efe7daf2SSergei Shtylyov 			       struct platform_device *pdev)
294*efe7daf2SSergei Shtylyov {
295*efe7daf2SSergei Shtylyov 	struct da8xx_ohci_root_hub *hub	= pdev->dev.platform_data;
296*efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd;
297*efe7daf2SSergei Shtylyov 	struct resource *mem;
298*efe7daf2SSergei Shtylyov 	int error, irq;
299*efe7daf2SSergei Shtylyov 
300*efe7daf2SSergei Shtylyov 	if (hub == NULL)
301*efe7daf2SSergei Shtylyov 		return -ENODEV;
302*efe7daf2SSergei Shtylyov 
303*efe7daf2SSergei Shtylyov 	usb11_clk = clk_get(&pdev->dev, "usb11");
304*efe7daf2SSergei Shtylyov 	if (IS_ERR(usb11_clk))
305*efe7daf2SSergei Shtylyov 		return PTR_ERR(usb11_clk);
306*efe7daf2SSergei Shtylyov 
307*efe7daf2SSergei Shtylyov 	usb20_clk = clk_get(&pdev->dev, "usb20");
308*efe7daf2SSergei Shtylyov 	if (IS_ERR(usb20_clk)) {
309*efe7daf2SSergei Shtylyov 		error = PTR_ERR(usb20_clk);
310*efe7daf2SSergei Shtylyov 		goto err0;
311*efe7daf2SSergei Shtylyov 	}
312*efe7daf2SSergei Shtylyov 
313*efe7daf2SSergei Shtylyov 	hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
314*efe7daf2SSergei Shtylyov 	if (!hcd) {
315*efe7daf2SSergei Shtylyov 		error = -ENOMEM;
316*efe7daf2SSergei Shtylyov 		goto err1;
317*efe7daf2SSergei Shtylyov 	}
318*efe7daf2SSergei Shtylyov 
319*efe7daf2SSergei Shtylyov 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
320*efe7daf2SSergei Shtylyov 	if (!mem) {
321*efe7daf2SSergei Shtylyov 		error = -ENODEV;
322*efe7daf2SSergei Shtylyov 		goto err2;
323*efe7daf2SSergei Shtylyov 	}
324*efe7daf2SSergei Shtylyov 	hcd->rsrc_start = mem->start;
325*efe7daf2SSergei Shtylyov 	hcd->rsrc_len = mem->end - mem->start + 1;
326*efe7daf2SSergei Shtylyov 
327*efe7daf2SSergei Shtylyov 	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
328*efe7daf2SSergei Shtylyov 		dev_dbg(&pdev->dev, "request_mem_region failed\n");
329*efe7daf2SSergei Shtylyov 		error = -EBUSY;
330*efe7daf2SSergei Shtylyov 		goto err2;
331*efe7daf2SSergei Shtylyov 	}
332*efe7daf2SSergei Shtylyov 
333*efe7daf2SSergei Shtylyov 	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
334*efe7daf2SSergei Shtylyov 	if (!hcd->regs) {
335*efe7daf2SSergei Shtylyov 		dev_err(&pdev->dev, "ioremap failed\n");
336*efe7daf2SSergei Shtylyov 		error = -ENOMEM;
337*efe7daf2SSergei Shtylyov 		goto err3;
338*efe7daf2SSergei Shtylyov 	}
339*efe7daf2SSergei Shtylyov 
340*efe7daf2SSergei Shtylyov 	ohci_hcd_init(hcd_to_ohci(hcd));
341*efe7daf2SSergei Shtylyov 
342*efe7daf2SSergei Shtylyov 	irq = platform_get_irq(pdev, 0);
343*efe7daf2SSergei Shtylyov 	if (irq < 0) {
344*efe7daf2SSergei Shtylyov 		error = -ENODEV;
345*efe7daf2SSergei Shtylyov 		goto err4;
346*efe7daf2SSergei Shtylyov 	}
347*efe7daf2SSergei Shtylyov 	error = usb_add_hcd(hcd, irq, IRQF_DISABLED);
348*efe7daf2SSergei Shtylyov 	if (error)
349*efe7daf2SSergei Shtylyov 		goto err4;
350*efe7daf2SSergei Shtylyov 
351*efe7daf2SSergei Shtylyov 	if (hub->ocic_notify) {
352*efe7daf2SSergei Shtylyov 		error = hub->ocic_notify(ohci_da8xx_ocic_handler);
353*efe7daf2SSergei Shtylyov 		if (!error)
354*efe7daf2SSergei Shtylyov 			return 0;
355*efe7daf2SSergei Shtylyov 	}
356*efe7daf2SSergei Shtylyov 
357*efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
358*efe7daf2SSergei Shtylyov err4:
359*efe7daf2SSergei Shtylyov 	iounmap(hcd->regs);
360*efe7daf2SSergei Shtylyov err3:
361*efe7daf2SSergei Shtylyov 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
362*efe7daf2SSergei Shtylyov err2:
363*efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
364*efe7daf2SSergei Shtylyov err1:
365*efe7daf2SSergei Shtylyov 	clk_put(usb20_clk);
366*efe7daf2SSergei Shtylyov err0:
367*efe7daf2SSergei Shtylyov 	clk_put(usb11_clk);
368*efe7daf2SSergei Shtylyov 	return error;
369*efe7daf2SSergei Shtylyov }
370*efe7daf2SSergei Shtylyov 
371*efe7daf2SSergei Shtylyov /**
372*efe7daf2SSergei Shtylyov  * usb_hcd_da8xx_remove - shutdown processing for DA8xx-based HCDs
373*efe7daf2SSergei Shtylyov  * @dev: USB Host Controller being removed
374*efe7daf2SSergei Shtylyov  * Context: !in_interrupt()
375*efe7daf2SSergei Shtylyov  *
376*efe7daf2SSergei Shtylyov  * Reverses the effect of usb_hcd_da8xx_probe(), first invoking
377*efe7daf2SSergei Shtylyov  * the HCD's stop() method.  It is always called from a thread
378*efe7daf2SSergei Shtylyov  * context, normally "rmmod", "apmd", or something similar.
379*efe7daf2SSergei Shtylyov  */
380*efe7daf2SSergei Shtylyov static inline void
381*efe7daf2SSergei Shtylyov usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev)
382*efe7daf2SSergei Shtylyov {
383*efe7daf2SSergei Shtylyov 	struct da8xx_ohci_root_hub *hub	= pdev->dev.platform_data;
384*efe7daf2SSergei Shtylyov 
385*efe7daf2SSergei Shtylyov 	hub->ocic_notify(NULL);
386*efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
387*efe7daf2SSergei Shtylyov 	iounmap(hcd->regs);
388*efe7daf2SSergei Shtylyov 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
389*efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
390*efe7daf2SSergei Shtylyov 	clk_put(usb20_clk);
391*efe7daf2SSergei Shtylyov 	clk_put(usb11_clk);
392*efe7daf2SSergei Shtylyov }
393*efe7daf2SSergei Shtylyov 
394*efe7daf2SSergei Shtylyov static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev)
395*efe7daf2SSergei Shtylyov {
396*efe7daf2SSergei Shtylyov 	return usb_hcd_da8xx_probe(&ohci_da8xx_hc_driver, dev);
397*efe7daf2SSergei Shtylyov }
398*efe7daf2SSergei Shtylyov 
399*efe7daf2SSergei Shtylyov static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev)
400*efe7daf2SSergei Shtylyov {
401*efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd = platform_get_drvdata(dev);
402*efe7daf2SSergei Shtylyov 
403*efe7daf2SSergei Shtylyov 	usb_hcd_da8xx_remove(hcd, dev);
404*efe7daf2SSergei Shtylyov 	platform_set_drvdata(dev, NULL);
405*efe7daf2SSergei Shtylyov 
406*efe7daf2SSergei Shtylyov 	return 0;
407*efe7daf2SSergei Shtylyov }
408*efe7daf2SSergei Shtylyov 
409*efe7daf2SSergei Shtylyov #ifdef CONFIG_PM
410*efe7daf2SSergei Shtylyov static int ohci_da8xx_suspend(struct platform_device *dev, pm_message_t message)
411*efe7daf2SSergei Shtylyov {
412*efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd	= platform_get_drvdata(dev);
413*efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
414*efe7daf2SSergei Shtylyov 
415*efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
416*efe7daf2SSergei Shtylyov 		msleep(5);
417*efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
418*efe7daf2SSergei Shtylyov 
419*efe7daf2SSergei Shtylyov 	ohci_da8xx_clock(0);
420*efe7daf2SSergei Shtylyov 	hcd->state = HC_STATE_SUSPENDED;
421*efe7daf2SSergei Shtylyov 	dev->dev.power.power_state = PMSG_SUSPEND;
422*efe7daf2SSergei Shtylyov 	return 0;
423*efe7daf2SSergei Shtylyov }
424*efe7daf2SSergei Shtylyov 
425*efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev)
426*efe7daf2SSergei Shtylyov {
427*efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd	= platform_get_drvdata(dev);
428*efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
429*efe7daf2SSergei Shtylyov 
430*efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
431*efe7daf2SSergei Shtylyov 		msleep(5);
432*efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
433*efe7daf2SSergei Shtylyov 
434*efe7daf2SSergei Shtylyov 	ohci_da8xx_clock(1);
435*efe7daf2SSergei Shtylyov 	dev->dev.power.power_state = PMSG_ON;
436*efe7daf2SSergei Shtylyov 	usb_hcd_resume_root_hub(hcd);
437*efe7daf2SSergei Shtylyov 	return 0;
438*efe7daf2SSergei Shtylyov }
439*efe7daf2SSergei Shtylyov #endif
440*efe7daf2SSergei Shtylyov 
441*efe7daf2SSergei Shtylyov /*
442*efe7daf2SSergei Shtylyov  * Driver definition to register with platform structure.
443*efe7daf2SSergei Shtylyov  */
444*efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = {
445*efe7daf2SSergei Shtylyov 	.probe		= ohci_hcd_da8xx_drv_probe,
446*efe7daf2SSergei Shtylyov 	.remove		= ohci_hcd_da8xx_drv_remove,
447*efe7daf2SSergei Shtylyov 	.shutdown 	= usb_hcd_platform_shutdown,
448*efe7daf2SSergei Shtylyov #ifdef	CONFIG_PM
449*efe7daf2SSergei Shtylyov 	.suspend	= ohci_da8xx_suspend,
450*efe7daf2SSergei Shtylyov 	.resume		= ohci_da8xx_resume,
451*efe7daf2SSergei Shtylyov #endif
452*efe7daf2SSergei Shtylyov 	.driver		= {
453*efe7daf2SSergei Shtylyov 		.owner	= THIS_MODULE,
454*efe7daf2SSergei Shtylyov 		.name	= "ohci",
455*efe7daf2SSergei Shtylyov 	},
456*efe7daf2SSergei Shtylyov };
457