xref: /openbmc/linux/drivers/usb/host/ohci-da8xx.c (revision f3c56fb39658a3f81ffe878628109075a023c935)
1efe7daf2SSergei Shtylyov /*
2efe7daf2SSergei Shtylyov  * OHCI HCD (Host Controller Driver) for USB.
3efe7daf2SSergei Shtylyov  *
4efe7daf2SSergei Shtylyov  * TI DA8xx (OMAP-L1x) Bus Glue
5efe7daf2SSergei Shtylyov  *
6efe7daf2SSergei Shtylyov  * Derived from: ohci-omap.c and ohci-s3c2410.c
7efe7daf2SSergei Shtylyov  * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
8efe7daf2SSergei Shtylyov  *
9efe7daf2SSergei Shtylyov  * This file is licensed under the terms of the GNU General Public License
10efe7daf2SSergei Shtylyov  * version 2. This program is licensed "as is" without any warranty of any
11efe7daf2SSergei Shtylyov  * kind, whether express or implied.
12efe7daf2SSergei Shtylyov  */
13efe7daf2SSergei Shtylyov 
146c21caa3SManjunath Goudar #include <linux/clk.h>
156c21caa3SManjunath Goudar #include <linux/io.h>
16efe7daf2SSergei Shtylyov #include <linux/interrupt.h>
17efe7daf2SSergei Shtylyov #include <linux/jiffies.h>
186c21caa3SManjunath Goudar #include <linux/kernel.h>
196c21caa3SManjunath Goudar #include <linux/module.h>
20efe7daf2SSergei Shtylyov #include <linux/platform_device.h>
216110c425SDavid Lechner #include <linux/phy/phy.h>
22ec2a0833SArnd Bergmann #include <linux/platform_data/usb-davinci.h>
236c21caa3SManjunath Goudar #include <linux/usb.h>
246c21caa3SManjunath Goudar #include <linux/usb/hcd.h>
256c21caa3SManjunath Goudar #include <asm/unaligned.h>
26efe7daf2SSergei Shtylyov 
276c21caa3SManjunath Goudar #include "ohci.h"
286c21caa3SManjunath Goudar 
296c21caa3SManjunath Goudar #define DRIVER_DESC "DA8XX"
30eacae5d2SAxel Haslam #define DRV_NAME "ohci-da8xx"
316c21caa3SManjunath Goudar 
326c21caa3SManjunath Goudar static struct hc_driver __read_mostly ohci_da8xx_hc_driver;
336c21caa3SManjunath Goudar 
346c21caa3SManjunath Goudar static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
356c21caa3SManjunath Goudar 			u16 wValue, u16 wIndex, char *buf, u16 wLength);
366c21caa3SManjunath Goudar static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
37efe7daf2SSergei Shtylyov 
38c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd {
39c7a4f9f3SAxel Haslam 	struct clk *usb11_clk;
40c7a4f9f3SAxel Haslam 	struct phy *usb11_phy;
41c7a4f9f3SAxel Haslam };
42c7a4f9f3SAxel Haslam 
43c7a4f9f3SAxel Haslam #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
44efe7daf2SSergei Shtylyov 
45efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */
46efe7daf2SSergei Shtylyov static volatile u16 ocic_mask;
47efe7daf2SSergei Shtylyov 
48c7a4f9f3SAxel Haslam static int ohci_da8xx_enable(struct usb_hcd *hcd)
49efe7daf2SSergei Shtylyov {
50c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
516110c425SDavid Lechner 	int ret;
52efe7daf2SSergei Shtylyov 
53c7a4f9f3SAxel Haslam 	ret = clk_prepare_enable(da8xx_ohci->usb11_clk);
546110c425SDavid Lechner 	if (ret)
556110c425SDavid Lechner 		return ret;
56efe7daf2SSergei Shtylyov 
57c7a4f9f3SAxel Haslam 	ret = phy_init(da8xx_ohci->usb11_phy);
586110c425SDavid Lechner 	if (ret)
596110c425SDavid Lechner 		goto err_phy_init;
60efe7daf2SSergei Shtylyov 
61c7a4f9f3SAxel Haslam 	ret = phy_power_on(da8xx_ohci->usb11_phy);
626110c425SDavid Lechner 	if (ret)
636110c425SDavid Lechner 		goto err_phy_power_on;
64efe7daf2SSergei Shtylyov 
656110c425SDavid Lechner 	return 0;
666110c425SDavid Lechner 
676110c425SDavid Lechner err_phy_power_on:
68c7a4f9f3SAxel Haslam 	phy_exit(da8xx_ohci->usb11_phy);
696110c425SDavid Lechner err_phy_init:
70c7a4f9f3SAxel Haslam 	clk_disable_unprepare(da8xx_ohci->usb11_clk);
716110c425SDavid Lechner 
726110c425SDavid Lechner 	return ret;
73efe7daf2SSergei Shtylyov }
74efe7daf2SSergei Shtylyov 
75c7a4f9f3SAxel Haslam static void ohci_da8xx_disable(struct usb_hcd *hcd)
766110c425SDavid Lechner {
77c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
78c7a4f9f3SAxel Haslam 
79c7a4f9f3SAxel Haslam 	phy_power_off(da8xx_ohci->usb11_phy);
80c7a4f9f3SAxel Haslam 	phy_exit(da8xx_ohci->usb11_phy);
81c7a4f9f3SAxel Haslam 	clk_disable_unprepare(da8xx_ohci->usb11_clk);
82efe7daf2SSergei Shtylyov }
83efe7daf2SSergei Shtylyov 
84*f3c56fb3SAxel Haslam static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on)
85*f3c56fb3SAxel Haslam {
86*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
87*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
88*f3c56fb3SAxel Haslam 
89*f3c56fb3SAxel Haslam 	if (hub && hub->set_power)
90*f3c56fb3SAxel Haslam 		return hub->set_power(1, on);
91*f3c56fb3SAxel Haslam 
92*f3c56fb3SAxel Haslam 	return 0;
93*f3c56fb3SAxel Haslam }
94*f3c56fb3SAxel Haslam 
95*f3c56fb3SAxel Haslam static int ohci_da8xx_get_power(struct usb_hcd *hcd)
96*f3c56fb3SAxel Haslam {
97*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
98*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
99*f3c56fb3SAxel Haslam 
100*f3c56fb3SAxel Haslam 	if (hub && hub->get_power)
101*f3c56fb3SAxel Haslam 		return hub->get_power(1);
102*f3c56fb3SAxel Haslam 
103*f3c56fb3SAxel Haslam 	return 1;
104*f3c56fb3SAxel Haslam }
105*f3c56fb3SAxel Haslam 
106*f3c56fb3SAxel Haslam static int ohci_da8xx_get_oci(struct usb_hcd *hcd)
107*f3c56fb3SAxel Haslam {
108*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
109*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
110*f3c56fb3SAxel Haslam 
111*f3c56fb3SAxel Haslam 	if (hub && hub->get_oci)
112*f3c56fb3SAxel Haslam 		return hub->get_oci(1);
113*f3c56fb3SAxel Haslam 
114*f3c56fb3SAxel Haslam 	return 0;
115*f3c56fb3SAxel Haslam }
116*f3c56fb3SAxel Haslam 
117*f3c56fb3SAxel Haslam static int ohci_da8xx_has_set_power(struct usb_hcd *hcd)
118*f3c56fb3SAxel Haslam {
119*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
120*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
121*f3c56fb3SAxel Haslam 
122*f3c56fb3SAxel Haslam 	if (hub && hub->set_power)
123*f3c56fb3SAxel Haslam 		return 1;
124*f3c56fb3SAxel Haslam 
125*f3c56fb3SAxel Haslam 	return 0;
126*f3c56fb3SAxel Haslam }
127*f3c56fb3SAxel Haslam 
128*f3c56fb3SAxel Haslam static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
129*f3c56fb3SAxel Haslam {
130*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
131*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
132*f3c56fb3SAxel Haslam 
133*f3c56fb3SAxel Haslam 	if (hub && hub->get_oci)
134*f3c56fb3SAxel Haslam 		return 1;
135*f3c56fb3SAxel Haslam 
136*f3c56fb3SAxel Haslam 	return 0;
137*f3c56fb3SAxel Haslam }
138*f3c56fb3SAxel Haslam 
139*f3c56fb3SAxel Haslam static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
140*f3c56fb3SAxel Haslam {
141*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
142*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
143*f3c56fb3SAxel Haslam 
144*f3c56fb3SAxel Haslam 	if (hub && hub->potpgt)
145*f3c56fb3SAxel Haslam 		return 1;
146*f3c56fb3SAxel Haslam 
147*f3c56fb3SAxel Haslam 	return 0;
148*f3c56fb3SAxel Haslam }
149*f3c56fb3SAxel Haslam 
150efe7daf2SSergei Shtylyov /*
151efe7daf2SSergei Shtylyov  * Handle the port over-current indicator change.
152efe7daf2SSergei Shtylyov  */
153efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
154efe7daf2SSergei Shtylyov 				    unsigned port)
155efe7daf2SSergei Shtylyov {
156efe7daf2SSergei Shtylyov 	ocic_mask |= 1 << port;
157efe7daf2SSergei Shtylyov 
158efe7daf2SSergei Shtylyov 	/* Once over-current is detected, the port needs to be powered down */
159efe7daf2SSergei Shtylyov 	if (hub->get_oci(port) > 0)
160efe7daf2SSergei Shtylyov 		hub->set_power(port, 0);
161efe7daf2SSergei Shtylyov }
162efe7daf2SSergei Shtylyov 
163*f3c56fb3SAxel Haslam static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
164*f3c56fb3SAxel Haslam {
165*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
166*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
167*f3c56fb3SAxel Haslam 
168*f3c56fb3SAxel Haslam 	if (hub && hub->ocic_notify)
169*f3c56fb3SAxel Haslam 		return hub->ocic_notify(ohci_da8xx_ocic_handler);
170*f3c56fb3SAxel Haslam 
171*f3c56fb3SAxel Haslam 	return 0;
172*f3c56fb3SAxel Haslam }
173*f3c56fb3SAxel Haslam 
174*f3c56fb3SAxel Haslam static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd)
175*f3c56fb3SAxel Haslam {
176*f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
177*f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
178*f3c56fb3SAxel Haslam 
179*f3c56fb3SAxel Haslam 	if (hub && hub->ocic_notify)
180*f3c56fb3SAxel Haslam 		hub->ocic_notify(NULL);
181*f3c56fb3SAxel Haslam }
182*f3c56fb3SAxel Haslam 
1836c21caa3SManjunath Goudar static int ohci_da8xx_reset(struct usb_hcd *hcd)
184efe7daf2SSergei Shtylyov {
185efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
186d4f09e28SJingoo Han 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
187efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci		= hcd_to_ohci(hcd);
188efe7daf2SSergei Shtylyov 	int result;
189efe7daf2SSergei Shtylyov 	u32 rh_a;
190efe7daf2SSergei Shtylyov 
191efe7daf2SSergei Shtylyov 	dev_dbg(dev, "starting USB controller\n");
192efe7daf2SSergei Shtylyov 
193c7a4f9f3SAxel Haslam 	result = ohci_da8xx_enable(hcd);
1946110c425SDavid Lechner 	if (result < 0)
1956110c425SDavid Lechner 		return result;
196efe7daf2SSergei Shtylyov 
197efe7daf2SSergei Shtylyov 	/*
198efe7daf2SSergei Shtylyov 	 * DA8xx only have 1 port connected to the pins but the HC root hub
199efe7daf2SSergei Shtylyov 	 * register A reports 2 ports, thus we'll have to override it...
200efe7daf2SSergei Shtylyov 	 */
201efe7daf2SSergei Shtylyov 	ohci->num_ports = 1;
202efe7daf2SSergei Shtylyov 
2036c21caa3SManjunath Goudar 	result = ohci_setup(hcd);
2046110c425SDavid Lechner 	if (result < 0) {
205c7a4f9f3SAxel Haslam 		ohci_da8xx_disable(hcd);
206efe7daf2SSergei Shtylyov 		return result;
2076110c425SDavid Lechner 	}
208efe7daf2SSergei Shtylyov 
209efe7daf2SSergei Shtylyov 	/*
210efe7daf2SSergei Shtylyov 	 * Since we're providing a board-specific root hub port power control
211efe7daf2SSergei Shtylyov 	 * and over-current reporting, we have to override the HC root hub A
212efe7daf2SSergei Shtylyov 	 * register's default value, so that ohci_hub_control() could return
213efe7daf2SSergei Shtylyov 	 * the correct hub descriptor...
214efe7daf2SSergei Shtylyov 	 */
215efe7daf2SSergei Shtylyov 	rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
216*f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_set_power(hcd)) {
217efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NPS;
218efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_PSM;
219efe7daf2SSergei Shtylyov 	}
220*f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_oci(hcd)) {
221efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NOCP;
222efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_OCPM;
223efe7daf2SSergei Shtylyov 	}
224*f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_potpgt(hcd)) {
225efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_POTPGT;
226efe7daf2SSergei Shtylyov 		rh_a |= hub->potpgt << 24;
227*f3c56fb3SAxel Haslam 	}
228efe7daf2SSergei Shtylyov 	ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
229efe7daf2SSergei Shtylyov 
230efe7daf2SSergei Shtylyov 	return result;
231efe7daf2SSergei Shtylyov }
232efe7daf2SSergei Shtylyov 
233efe7daf2SSergei Shtylyov /*
234efe7daf2SSergei Shtylyov  * Update the status data from the hub with the over-current indicator change.
235efe7daf2SSergei Shtylyov  */
236efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
237efe7daf2SSergei Shtylyov {
2386c21caa3SManjunath Goudar 	int length		= orig_ohci_hub_status_data(hcd, buf);
239efe7daf2SSergei Shtylyov 
240efe7daf2SSergei Shtylyov 	/* See if we have OCIC bit set on port 1 */
241efe7daf2SSergei Shtylyov 	if (ocic_mask & (1 << 1)) {
242efe7daf2SSergei Shtylyov 		dev_dbg(hcd->self.controller, "over-current indicator change "
243efe7daf2SSergei Shtylyov 			"on port 1\n");
244efe7daf2SSergei Shtylyov 
245efe7daf2SSergei Shtylyov 		if (!length)
246efe7daf2SSergei Shtylyov 			length = 1;
247efe7daf2SSergei Shtylyov 
248efe7daf2SSergei Shtylyov 		buf[0] |= 1 << 1;
249efe7daf2SSergei Shtylyov 	}
250efe7daf2SSergei Shtylyov 	return length;
251efe7daf2SSergei Shtylyov }
252efe7daf2SSergei Shtylyov 
253efe7daf2SSergei Shtylyov /*
254efe7daf2SSergei Shtylyov  * Look at the control requests to the root hub and see if we need to override.
255efe7daf2SSergei Shtylyov  */
256efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
257efe7daf2SSergei Shtylyov 				  u16 wIndex, char *buf, u16 wLength)
258efe7daf2SSergei Shtylyov {
259efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
260efe7daf2SSergei Shtylyov 	int temp;
261efe7daf2SSergei Shtylyov 
262efe7daf2SSergei Shtylyov 	switch (typeReq) {
263efe7daf2SSergei Shtylyov 	case GetPortStatus:
264efe7daf2SSergei Shtylyov 		/* Check the port number */
265efe7daf2SSergei Shtylyov 		if (wIndex != 1)
266efe7daf2SSergei Shtylyov 			break;
267efe7daf2SSergei Shtylyov 
268efe7daf2SSergei Shtylyov 		dev_dbg(dev, "GetPortStatus(%u)\n", wIndex);
269efe7daf2SSergei Shtylyov 
270efe7daf2SSergei Shtylyov 		temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
271efe7daf2SSergei Shtylyov 
272efe7daf2SSergei Shtylyov 		/* The port power status (PPS) bit defaults to 1 */
273*f3c56fb3SAxel Haslam 		if (!ohci_da8xx_get_power(hcd))
274efe7daf2SSergei Shtylyov 			temp &= ~RH_PS_PPS;
275efe7daf2SSergei Shtylyov 
276efe7daf2SSergei Shtylyov 		/* The port over-current indicator (POCI) bit is always 0 */
277*f3c56fb3SAxel Haslam 		if (ohci_da8xx_get_oci(hcd) > 0)
278efe7daf2SSergei Shtylyov 			temp |=  RH_PS_POCI;
279efe7daf2SSergei Shtylyov 
280efe7daf2SSergei Shtylyov 		/* The over-current indicator change (OCIC) bit is 0 too */
281efe7daf2SSergei Shtylyov 		if (ocic_mask & (1 << wIndex))
282efe7daf2SSergei Shtylyov 			temp |=  RH_PS_OCIC;
283efe7daf2SSergei Shtylyov 
284efe7daf2SSergei Shtylyov 		put_unaligned(cpu_to_le32(temp), (__le32 *)buf);
285efe7daf2SSergei Shtylyov 		return 0;
286efe7daf2SSergei Shtylyov 	case SetPortFeature:
287efe7daf2SSergei Shtylyov 		temp = 1;
288efe7daf2SSergei Shtylyov 		goto check_port;
289efe7daf2SSergei Shtylyov 	case ClearPortFeature:
290efe7daf2SSergei Shtylyov 		temp = 0;
291efe7daf2SSergei Shtylyov 
292efe7daf2SSergei Shtylyov check_port:
293efe7daf2SSergei Shtylyov 		/* Check the port number */
294efe7daf2SSergei Shtylyov 		if (wIndex != 1)
295efe7daf2SSergei Shtylyov 			break;
296efe7daf2SSergei Shtylyov 
297efe7daf2SSergei Shtylyov 		switch (wValue) {
298efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_POWER:
299efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
300efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex, "POWER");
301efe7daf2SSergei Shtylyov 
302*f3c56fb3SAxel Haslam 			return ohci_da8xx_set_power(hcd, temp) ? -EPIPE : 0;
303efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_C_OVER_CURRENT:
304efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
305efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex,
306efe7daf2SSergei Shtylyov 				"C_OVER_CURRENT");
307efe7daf2SSergei Shtylyov 
308efe7daf2SSergei Shtylyov 			if (temp)
309efe7daf2SSergei Shtylyov 				ocic_mask |= 1 << wIndex;
310efe7daf2SSergei Shtylyov 			else
311efe7daf2SSergei Shtylyov 				ocic_mask &= ~(1 << wIndex);
312efe7daf2SSergei Shtylyov 			return 0;
313efe7daf2SSergei Shtylyov 		}
314efe7daf2SSergei Shtylyov 	}
315efe7daf2SSergei Shtylyov 
3166c21caa3SManjunath Goudar 	return orig_ohci_hub_control(hcd, typeReq, wValue,
3176c21caa3SManjunath Goudar 			wIndex, buf, wLength);
318efe7daf2SSergei Shtylyov }
319efe7daf2SSergei Shtylyov 
320efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/
321efe7daf2SSergei Shtylyov 
3226c21caa3SManjunath Goudar static int ohci_da8xx_probe(struct platform_device *pdev)
323efe7daf2SSergei Shtylyov {
324c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci;
325efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd;
326efe7daf2SSergei Shtylyov 	struct resource *mem;
327efe7daf2SSergei Shtylyov 	int error, irq;
3286c21caa3SManjunath Goudar 	hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev,
3296c21caa3SManjunath Goudar 				dev_name(&pdev->dev));
330644db166SJingoo Han 	if (!hcd)
331644db166SJingoo Han 		return -ENOMEM;
332efe7daf2SSergei Shtylyov 
333c7a4f9f3SAxel Haslam 	da8xx_ohci = to_da8xx_ohci(hcd);
334c7a4f9f3SAxel Haslam 
335c7a4f9f3SAxel Haslam 	da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11");
336c7a4f9f3SAxel Haslam 	if (IS_ERR(da8xx_ohci->usb11_clk)) {
337c7a4f9f3SAxel Haslam 		error = PTR_ERR(da8xx_ohci->usb11_clk);
338c7a4f9f3SAxel Haslam 		if (error != -EPROBE_DEFER)
339c7a4f9f3SAxel Haslam 			dev_err(&pdev->dev, "Failed to get clock.\n");
340c7a4f9f3SAxel Haslam 		goto err;
341c7a4f9f3SAxel Haslam 	}
342c7a4f9f3SAxel Haslam 
343c7a4f9f3SAxel Haslam 	da8xx_ohci->usb11_phy = devm_phy_get(&pdev->dev, "usb-phy");
344c7a4f9f3SAxel Haslam 	if (IS_ERR(da8xx_ohci->usb11_phy)) {
345c7a4f9f3SAxel Haslam 		error = PTR_ERR(da8xx_ohci->usb11_phy);
346c7a4f9f3SAxel Haslam 		if (error != -EPROBE_DEFER)
347c7a4f9f3SAxel Haslam 			dev_err(&pdev->dev, "Failed to get phy.\n");
348c7a4f9f3SAxel Haslam 		goto err;
349c7a4f9f3SAxel Haslam 	}
350c7a4f9f3SAxel Haslam 
351efe7daf2SSergei Shtylyov 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
352644db166SJingoo Han 	hcd->regs = devm_ioremap_resource(&pdev->dev, mem);
353644db166SJingoo Han 	if (IS_ERR(hcd->regs)) {
354644db166SJingoo Han 		error = PTR_ERR(hcd->regs);
355644db166SJingoo Han 		goto err;
356efe7daf2SSergei Shtylyov 	}
35754891d74SVarka Bhadram 	hcd->rsrc_start = mem->start;
35854891d74SVarka Bhadram 	hcd->rsrc_len = resource_size(mem);
359efe7daf2SSergei Shtylyov 
360efe7daf2SSergei Shtylyov 	irq = platform_get_irq(pdev, 0);
361efe7daf2SSergei Shtylyov 	if (irq < 0) {
362efe7daf2SSergei Shtylyov 		error = -ENODEV;
363644db166SJingoo Han 		goto err;
364efe7daf2SSergei Shtylyov 	}
3656c21caa3SManjunath Goudar 
366b5dd18d8SYong Zhang 	error = usb_add_hcd(hcd, irq, 0);
367efe7daf2SSergei Shtylyov 	if (error)
368644db166SJingoo Han 		goto err;
369efe7daf2SSergei Shtylyov 
3703c9740a1SPeter Chen 	device_wakeup_enable(hcd->self.controller);
3713c9740a1SPeter Chen 
372*f3c56fb3SAxel Haslam 	error = ohci_da8xx_register_notify(hcd);
373*f3c56fb3SAxel Haslam 	if (error)
374*f3c56fb3SAxel Haslam 		goto err_remove_hcd;
375efe7daf2SSergei Shtylyov 
376*f3c56fb3SAxel Haslam 	return 0;
377*f3c56fb3SAxel Haslam 
378*f3c56fb3SAxel Haslam err_remove_hcd:
379efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
380644db166SJingoo Han err:
381efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
382efe7daf2SSergei Shtylyov 	return error;
383efe7daf2SSergei Shtylyov }
384efe7daf2SSergei Shtylyov 
3856c21caa3SManjunath Goudar static int ohci_da8xx_remove(struct platform_device *pdev)
386efe7daf2SSergei Shtylyov {
3876c21caa3SManjunath Goudar 	struct usb_hcd	*hcd = platform_get_drvdata(pdev);
388efe7daf2SSergei Shtylyov 
389*f3c56fb3SAxel Haslam 	ohci_da8xx_unregister_notify(hcd);
390efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
391efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
392efe7daf2SSergei Shtylyov 
393efe7daf2SSergei Shtylyov 	return 0;
394efe7daf2SSergei Shtylyov }
395efe7daf2SSergei Shtylyov 
396efe7daf2SSergei Shtylyov #ifdef CONFIG_PM
397933bb1f0SMajunath Goudar static int ohci_da8xx_suspend(struct platform_device *pdev,
398933bb1f0SMajunath Goudar 				pm_message_t message)
399efe7daf2SSergei Shtylyov {
400933bb1f0SMajunath Goudar 	struct usb_hcd	*hcd	= platform_get_drvdata(pdev);
401efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
402933bb1f0SMajunath Goudar 	bool		do_wakeup	= device_may_wakeup(&pdev->dev);
403933bb1f0SMajunath Goudar 	int		ret;
404933bb1f0SMajunath Goudar 
405efe7daf2SSergei Shtylyov 
406efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
407efe7daf2SSergei Shtylyov 		msleep(5);
408efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
409efe7daf2SSergei Shtylyov 
410933bb1f0SMajunath Goudar 	ret = ohci_suspend(hcd, do_wakeup);
411933bb1f0SMajunath Goudar 	if (ret)
412933bb1f0SMajunath Goudar 		return ret;
413933bb1f0SMajunath Goudar 
414c7a4f9f3SAxel Haslam 	ohci_da8xx_disable(hcd);
415efe7daf2SSergei Shtylyov 	hcd->state = HC_STATE_SUSPENDED;
416933bb1f0SMajunath Goudar 
417933bb1f0SMajunath Goudar 	return ret;
418efe7daf2SSergei Shtylyov }
419efe7daf2SSergei Shtylyov 
420efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev)
421efe7daf2SSergei Shtylyov {
422efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd	= platform_get_drvdata(dev);
423efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
4246110c425SDavid Lechner 	int ret;
425efe7daf2SSergei Shtylyov 
426efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
427efe7daf2SSergei Shtylyov 		msleep(5);
428efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
429efe7daf2SSergei Shtylyov 
430c7a4f9f3SAxel Haslam 	ret = ohci_da8xx_enable(hcd);
4316110c425SDavid Lechner 	if (ret)
4326110c425SDavid Lechner 		return ret;
4336110c425SDavid Lechner 
434efe7daf2SSergei Shtylyov 	dev->dev.power.power_state = PMSG_ON;
435efe7daf2SSergei Shtylyov 	usb_hcd_resume_root_hub(hcd);
4366110c425SDavid Lechner 
437efe7daf2SSergei Shtylyov 	return 0;
438efe7daf2SSergei Shtylyov }
439efe7daf2SSergei Shtylyov #endif
440efe7daf2SSergei Shtylyov 
4416c21caa3SManjunath Goudar static const struct ohci_driver_overrides da8xx_overrides __initconst = {
4426c21caa3SManjunath Goudar 	.reset		 = ohci_da8xx_reset,
443c7a4f9f3SAxel Haslam 	.extra_priv_size = sizeof(struct da8xx_ohci_hcd),
4446c21caa3SManjunath Goudar };
4456c21caa3SManjunath Goudar 
446efe7daf2SSergei Shtylyov /*
447efe7daf2SSergei Shtylyov  * Driver definition to register with platform structure.
448efe7daf2SSergei Shtylyov  */
449efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = {
4506c21caa3SManjunath Goudar 	.probe		= ohci_da8xx_probe,
4516c21caa3SManjunath Goudar 	.remove		= ohci_da8xx_remove,
452efe7daf2SSergei Shtylyov 	.shutdown 	= usb_hcd_platform_shutdown,
453efe7daf2SSergei Shtylyov #ifdef	CONFIG_PM
454efe7daf2SSergei Shtylyov 	.suspend	= ohci_da8xx_suspend,
455efe7daf2SSergei Shtylyov 	.resume		= ohci_da8xx_resume,
456efe7daf2SSergei Shtylyov #endif
457efe7daf2SSergei Shtylyov 	.driver		= {
4586c21caa3SManjunath Goudar 		.name	= DRV_NAME,
459efe7daf2SSergei Shtylyov 	},
460efe7daf2SSergei Shtylyov };
461ab59ac01SJan Luebbe 
4626c21caa3SManjunath Goudar static int __init ohci_da8xx_init(void)
4636c21caa3SManjunath Goudar {
4646c21caa3SManjunath Goudar 
4656c21caa3SManjunath Goudar 	if (usb_disabled())
4666c21caa3SManjunath Goudar 		return -ENODEV;
4676c21caa3SManjunath Goudar 
4686c21caa3SManjunath Goudar 	pr_info("%s: " DRIVER_DESC "\n", DRV_NAME);
4696c21caa3SManjunath Goudar 	ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides);
4706c21caa3SManjunath Goudar 
4716c21caa3SManjunath Goudar 	/*
4726c21caa3SManjunath Goudar 	 * The Davinci da8xx HW has some unusual quirks, which require
4736c21caa3SManjunath Goudar 	 * da8xx-specific workarounds. We override certain hc_driver
4746c21caa3SManjunath Goudar 	 * functions here to achieve that. We explicitly do not enhance
4756c21caa3SManjunath Goudar 	 * ohci_driver_overrides to allow this more easily, since this
4766c21caa3SManjunath Goudar 	 * is an unusual case, and we don't want to encourage others to
4776c21caa3SManjunath Goudar 	 * override these functions by making it too easy.
4786c21caa3SManjunath Goudar 	 */
4796c21caa3SManjunath Goudar 
4806c21caa3SManjunath Goudar 	orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control;
4816c21caa3SManjunath Goudar 	orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data;
4826c21caa3SManjunath Goudar 
4836c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_status_data     = ohci_da8xx_hub_status_data;
4846c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_control         = ohci_da8xx_hub_control;
4856c21caa3SManjunath Goudar 
4866c21caa3SManjunath Goudar 	return platform_driver_register(&ohci_hcd_da8xx_driver);
4876c21caa3SManjunath Goudar }
4886c21caa3SManjunath Goudar module_init(ohci_da8xx_init);
4896c21caa3SManjunath Goudar 
4906c21caa3SManjunath Goudar static void __exit ohci_da8xx_exit(void)
4916c21caa3SManjunath Goudar {
4926c21caa3SManjunath Goudar 	platform_driver_unregister(&ohci_hcd_da8xx_driver);
4936c21caa3SManjunath Goudar }
4946c21caa3SManjunath Goudar module_exit(ohci_da8xx_exit);
4956c21caa3SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC);
4966c21caa3SManjunath Goudar MODULE_LICENSE("GPL");
4976c21caa3SManjunath Goudar MODULE_ALIAS("platform:" DRV_NAME);
498