xref: /openbmc/linux/drivers/usb/host/ohci-da8xx.c (revision 6c21caa333f98e9adb93be5f01f5a4041c0d9256)
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 
14*6c21caa3SManjunath Goudar #include <linux/clk.h>
15*6c21caa3SManjunath Goudar #include <linux/io.h>
16efe7daf2SSergei Shtylyov #include <linux/interrupt.h>
17efe7daf2SSergei Shtylyov #include <linux/jiffies.h>
18*6c21caa3SManjunath Goudar #include <linux/kernel.h>
19*6c21caa3SManjunath 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>
23*6c21caa3SManjunath Goudar #include <linux/usb.h>
24*6c21caa3SManjunath Goudar #include <linux/usb/hcd.h>
25*6c21caa3SManjunath Goudar #include <asm/unaligned.h>
26efe7daf2SSergei Shtylyov 
27*6c21caa3SManjunath Goudar #include "ohci.h"
28*6c21caa3SManjunath Goudar 
29*6c21caa3SManjunath Goudar #define DRIVER_DESC "DA8XX"
30*6c21caa3SManjunath Goudar #define DRV_NAME "ohci"
31*6c21caa3SManjunath Goudar 
32*6c21caa3SManjunath Goudar static struct hc_driver __read_mostly ohci_da8xx_hc_driver;
33*6c21caa3SManjunath Goudar 
34*6c21caa3SManjunath Goudar static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
35*6c21caa3SManjunath Goudar 			u16 wValue, u16 wIndex, char *buf, u16 wLength);
36*6c21caa3SManjunath Goudar static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
37efe7daf2SSergei Shtylyov 
38efe7daf2SSergei Shtylyov static struct clk *usb11_clk;
396110c425SDavid Lechner static struct phy *usb11_phy;
40efe7daf2SSergei Shtylyov 
41efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */
42efe7daf2SSergei Shtylyov static volatile u16 ocic_mask;
43efe7daf2SSergei Shtylyov 
446110c425SDavid Lechner static int ohci_da8xx_enable(void)
45efe7daf2SSergei Shtylyov {
466110c425SDavid Lechner 	int ret;
47efe7daf2SSergei Shtylyov 
486110c425SDavid Lechner 	ret = clk_prepare_enable(usb11_clk);
496110c425SDavid Lechner 	if (ret)
506110c425SDavid Lechner 		return ret;
51efe7daf2SSergei Shtylyov 
526110c425SDavid Lechner 	ret = phy_init(usb11_phy);
536110c425SDavid Lechner 	if (ret)
546110c425SDavid Lechner 		goto err_phy_init;
55efe7daf2SSergei Shtylyov 
566110c425SDavid Lechner 	ret = phy_power_on(usb11_phy);
576110c425SDavid Lechner 	if (ret)
586110c425SDavid Lechner 		goto err_phy_power_on;
59efe7daf2SSergei Shtylyov 
606110c425SDavid Lechner 	return 0;
616110c425SDavid Lechner 
626110c425SDavid Lechner err_phy_power_on:
636110c425SDavid Lechner 	phy_exit(usb11_phy);
646110c425SDavid Lechner err_phy_init:
656110c425SDavid Lechner 	clk_disable_unprepare(usb11_clk);
666110c425SDavid Lechner 
676110c425SDavid Lechner 	return ret;
68efe7daf2SSergei Shtylyov }
69efe7daf2SSergei Shtylyov 
706110c425SDavid Lechner static void ohci_da8xx_disable(void)
716110c425SDavid Lechner {
726110c425SDavid Lechner 	phy_power_off(usb11_phy);
736110c425SDavid Lechner 	phy_exit(usb11_phy);
746110c425SDavid Lechner 	clk_disable_unprepare(usb11_clk);
75efe7daf2SSergei Shtylyov }
76efe7daf2SSergei Shtylyov 
77efe7daf2SSergei Shtylyov /*
78efe7daf2SSergei Shtylyov  * Handle the port over-current indicator change.
79efe7daf2SSergei Shtylyov  */
80efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
81efe7daf2SSergei Shtylyov 				    unsigned port)
82efe7daf2SSergei Shtylyov {
83efe7daf2SSergei Shtylyov 	ocic_mask |= 1 << port;
84efe7daf2SSergei Shtylyov 
85efe7daf2SSergei Shtylyov 	/* Once over-current is detected, the port needs to be powered down */
86efe7daf2SSergei Shtylyov 	if (hub->get_oci(port) > 0)
87efe7daf2SSergei Shtylyov 		hub->set_power(port, 0);
88efe7daf2SSergei Shtylyov }
89efe7daf2SSergei Shtylyov 
90*6c21caa3SManjunath Goudar static int ohci_da8xx_reset(struct usb_hcd *hcd)
91efe7daf2SSergei Shtylyov {
92efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
93d4f09e28SJingoo Han 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
94efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci		= hcd_to_ohci(hcd);
95efe7daf2SSergei Shtylyov 	int result;
96efe7daf2SSergei Shtylyov 	u32 rh_a;
97efe7daf2SSergei Shtylyov 
98efe7daf2SSergei Shtylyov 	dev_dbg(dev, "starting USB controller\n");
99efe7daf2SSergei Shtylyov 
1006110c425SDavid Lechner 	result = ohci_da8xx_enable();
1016110c425SDavid Lechner 	if (result < 0)
1026110c425SDavid Lechner 		return result;
103efe7daf2SSergei Shtylyov 
104efe7daf2SSergei Shtylyov 	/*
105efe7daf2SSergei Shtylyov 	 * DA8xx only have 1 port connected to the pins but the HC root hub
106efe7daf2SSergei Shtylyov 	 * register A reports 2 ports, thus we'll have to override it...
107efe7daf2SSergei Shtylyov 	 */
108efe7daf2SSergei Shtylyov 	ohci->num_ports = 1;
109efe7daf2SSergei Shtylyov 
110*6c21caa3SManjunath Goudar 	result = ohci_setup(hcd);
1116110c425SDavid Lechner 	if (result < 0) {
1126110c425SDavid Lechner 		ohci_da8xx_disable();
113efe7daf2SSergei Shtylyov 		return result;
1146110c425SDavid Lechner 	}
115efe7daf2SSergei Shtylyov 
116efe7daf2SSergei Shtylyov 	/*
117efe7daf2SSergei Shtylyov 	 * Since we're providing a board-specific root hub port power control
118efe7daf2SSergei Shtylyov 	 * and over-current reporting, we have to override the HC root hub A
119efe7daf2SSergei Shtylyov 	 * register's default value, so that ohci_hub_control() could return
120efe7daf2SSergei Shtylyov 	 * the correct hub descriptor...
121efe7daf2SSergei Shtylyov 	 */
122efe7daf2SSergei Shtylyov 	rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
123efe7daf2SSergei Shtylyov 	if (hub->set_power) {
124efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NPS;
125efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_PSM;
126efe7daf2SSergei Shtylyov 	}
127efe7daf2SSergei Shtylyov 	if (hub->get_oci) {
128efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NOCP;
129efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_OCPM;
130efe7daf2SSergei Shtylyov 	}
131efe7daf2SSergei Shtylyov 	rh_a &= ~RH_A_POTPGT;
132efe7daf2SSergei Shtylyov 	rh_a |= hub->potpgt << 24;
133efe7daf2SSergei Shtylyov 	ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
134efe7daf2SSergei Shtylyov 
135efe7daf2SSergei Shtylyov 	return result;
136efe7daf2SSergei Shtylyov }
137efe7daf2SSergei Shtylyov 
138efe7daf2SSergei Shtylyov /*
139efe7daf2SSergei Shtylyov  * Update the status data from the hub with the over-current indicator change.
140efe7daf2SSergei Shtylyov  */
141efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
142efe7daf2SSergei Shtylyov {
143*6c21caa3SManjunath Goudar 	int length		= orig_ohci_hub_status_data(hcd, buf);
144efe7daf2SSergei Shtylyov 
145efe7daf2SSergei Shtylyov 	/* See if we have OCIC bit set on port 1 */
146efe7daf2SSergei Shtylyov 	if (ocic_mask & (1 << 1)) {
147efe7daf2SSergei Shtylyov 		dev_dbg(hcd->self.controller, "over-current indicator change "
148efe7daf2SSergei Shtylyov 			"on port 1\n");
149efe7daf2SSergei Shtylyov 
150efe7daf2SSergei Shtylyov 		if (!length)
151efe7daf2SSergei Shtylyov 			length = 1;
152efe7daf2SSergei Shtylyov 
153efe7daf2SSergei Shtylyov 		buf[0] |= 1 << 1;
154efe7daf2SSergei Shtylyov 	}
155efe7daf2SSergei Shtylyov 	return length;
156efe7daf2SSergei Shtylyov }
157efe7daf2SSergei Shtylyov 
158efe7daf2SSergei Shtylyov /*
159efe7daf2SSergei Shtylyov  * Look at the control requests to the root hub and see if we need to override.
160efe7daf2SSergei Shtylyov  */
161efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
162efe7daf2SSergei Shtylyov 				  u16 wIndex, char *buf, u16 wLength)
163efe7daf2SSergei Shtylyov {
164efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
165d4f09e28SJingoo Han 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
166efe7daf2SSergei Shtylyov 	int temp;
167efe7daf2SSergei Shtylyov 
168efe7daf2SSergei Shtylyov 	switch (typeReq) {
169efe7daf2SSergei Shtylyov 	case GetPortStatus:
170efe7daf2SSergei Shtylyov 		/* Check the port number */
171efe7daf2SSergei Shtylyov 		if (wIndex != 1)
172efe7daf2SSergei Shtylyov 			break;
173efe7daf2SSergei Shtylyov 
174efe7daf2SSergei Shtylyov 		dev_dbg(dev, "GetPortStatus(%u)\n", wIndex);
175efe7daf2SSergei Shtylyov 
176efe7daf2SSergei Shtylyov 		temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
177efe7daf2SSergei Shtylyov 
178efe7daf2SSergei Shtylyov 		/* The port power status (PPS) bit defaults to 1 */
179efe7daf2SSergei Shtylyov 		if (hub->get_power && hub->get_power(wIndex) == 0)
180efe7daf2SSergei Shtylyov 			temp &= ~RH_PS_PPS;
181efe7daf2SSergei Shtylyov 
182efe7daf2SSergei Shtylyov 		/* The port over-current indicator (POCI) bit is always 0 */
183efe7daf2SSergei Shtylyov 		if (hub->get_oci && hub->get_oci(wIndex) > 0)
184efe7daf2SSergei Shtylyov 			temp |=  RH_PS_POCI;
185efe7daf2SSergei Shtylyov 
186efe7daf2SSergei Shtylyov 		/* The over-current indicator change (OCIC) bit is 0 too */
187efe7daf2SSergei Shtylyov 		if (ocic_mask & (1 << wIndex))
188efe7daf2SSergei Shtylyov 			temp |=  RH_PS_OCIC;
189efe7daf2SSergei Shtylyov 
190efe7daf2SSergei Shtylyov 		put_unaligned(cpu_to_le32(temp), (__le32 *)buf);
191efe7daf2SSergei Shtylyov 		return 0;
192efe7daf2SSergei Shtylyov 	case SetPortFeature:
193efe7daf2SSergei Shtylyov 		temp = 1;
194efe7daf2SSergei Shtylyov 		goto check_port;
195efe7daf2SSergei Shtylyov 	case ClearPortFeature:
196efe7daf2SSergei Shtylyov 		temp = 0;
197efe7daf2SSergei Shtylyov 
198efe7daf2SSergei Shtylyov check_port:
199efe7daf2SSergei Shtylyov 		/* Check the port number */
200efe7daf2SSergei Shtylyov 		if (wIndex != 1)
201efe7daf2SSergei Shtylyov 			break;
202efe7daf2SSergei Shtylyov 
203efe7daf2SSergei Shtylyov 		switch (wValue) {
204efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_POWER:
205efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
206efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex, "POWER");
207efe7daf2SSergei Shtylyov 
208efe7daf2SSergei Shtylyov 			if (!hub->set_power)
209efe7daf2SSergei Shtylyov 				return -EPIPE;
210efe7daf2SSergei Shtylyov 
211efe7daf2SSergei Shtylyov 			return hub->set_power(wIndex, temp) ? -EPIPE : 0;
212efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_C_OVER_CURRENT:
213efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
214efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex,
215efe7daf2SSergei Shtylyov 				"C_OVER_CURRENT");
216efe7daf2SSergei Shtylyov 
217efe7daf2SSergei Shtylyov 			if (temp)
218efe7daf2SSergei Shtylyov 				ocic_mask |= 1 << wIndex;
219efe7daf2SSergei Shtylyov 			else
220efe7daf2SSergei Shtylyov 				ocic_mask &= ~(1 << wIndex);
221efe7daf2SSergei Shtylyov 			return 0;
222efe7daf2SSergei Shtylyov 		}
223efe7daf2SSergei Shtylyov 	}
224efe7daf2SSergei Shtylyov 
225*6c21caa3SManjunath Goudar 	return orig_ohci_hub_control(hcd, typeReq, wValue,
226*6c21caa3SManjunath Goudar 			wIndex, buf, wLength);
227efe7daf2SSergei Shtylyov }
228efe7daf2SSergei Shtylyov 
229efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/
230efe7daf2SSergei Shtylyov 
231*6c21caa3SManjunath Goudar static int ohci_da8xx_probe(struct platform_device *pdev)
232efe7daf2SSergei Shtylyov {
233d4f09e28SJingoo Han 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(&pdev->dev);
234efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd;
235efe7daf2SSergei Shtylyov 	struct resource *mem;
236efe7daf2SSergei Shtylyov 	int error, irq;
237efe7daf2SSergei Shtylyov 
238efe7daf2SSergei Shtylyov 	if (hub == NULL)
239efe7daf2SSergei Shtylyov 		return -ENODEV;
240efe7daf2SSergei Shtylyov 
241644db166SJingoo Han 	usb11_clk = devm_clk_get(&pdev->dev, "usb11");
2426110c425SDavid Lechner 	if (IS_ERR(usb11_clk)) {
2436110c425SDavid Lechner 		if (PTR_ERR(usb11_clk) != -EPROBE_DEFER)
2446110c425SDavid Lechner 			dev_err(&pdev->dev, "Failed to get clock.\n");
245efe7daf2SSergei Shtylyov 		return PTR_ERR(usb11_clk);
2466110c425SDavid Lechner 	}
247efe7daf2SSergei Shtylyov 
2486110c425SDavid Lechner 	usb11_phy = devm_phy_get(&pdev->dev, "usb-phy");
2496110c425SDavid Lechner 	if (IS_ERR(usb11_phy)) {
2506110c425SDavid Lechner 		if (PTR_ERR(usb11_phy) != -EPROBE_DEFER)
2516110c425SDavid Lechner 			dev_err(&pdev->dev, "Failed to get phy.\n");
2526110c425SDavid Lechner 		return PTR_ERR(usb11_phy);
2536110c425SDavid Lechner 	}
254efe7daf2SSergei Shtylyov 
255*6c21caa3SManjunath Goudar 	hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev,
256*6c21caa3SManjunath Goudar 				dev_name(&pdev->dev));
257644db166SJingoo Han 	if (!hcd)
258644db166SJingoo Han 		return -ENOMEM;
259efe7daf2SSergei Shtylyov 
260efe7daf2SSergei Shtylyov 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
261644db166SJingoo Han 	hcd->regs = devm_ioremap_resource(&pdev->dev, mem);
262644db166SJingoo Han 	if (IS_ERR(hcd->regs)) {
263644db166SJingoo Han 		error = PTR_ERR(hcd->regs);
2646110c425SDavid Lechner 		dev_err(&pdev->dev, "failed to map ohci.\n");
265644db166SJingoo Han 		goto err;
266efe7daf2SSergei Shtylyov 	}
26754891d74SVarka Bhadram 	hcd->rsrc_start = mem->start;
26854891d74SVarka Bhadram 	hcd->rsrc_len = resource_size(mem);
269efe7daf2SSergei Shtylyov 
270efe7daf2SSergei Shtylyov 	irq = platform_get_irq(pdev, 0);
271efe7daf2SSergei Shtylyov 	if (irq < 0) {
272efe7daf2SSergei Shtylyov 		error = -ENODEV;
273644db166SJingoo Han 		goto err;
274efe7daf2SSergei Shtylyov 	}
275*6c21caa3SManjunath Goudar 
276b5dd18d8SYong Zhang 	error = usb_add_hcd(hcd, irq, 0);
277efe7daf2SSergei Shtylyov 	if (error)
278644db166SJingoo Han 		goto err;
279efe7daf2SSergei Shtylyov 
2803c9740a1SPeter Chen 	device_wakeup_enable(hcd->self.controller);
2813c9740a1SPeter Chen 
282efe7daf2SSergei Shtylyov 	if (hub->ocic_notify) {
283efe7daf2SSergei Shtylyov 		error = hub->ocic_notify(ohci_da8xx_ocic_handler);
284efe7daf2SSergei Shtylyov 		if (!error)
285efe7daf2SSergei Shtylyov 			return 0;
286efe7daf2SSergei Shtylyov 	}
287efe7daf2SSergei Shtylyov 
288efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
289644db166SJingoo Han err:
290efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
291efe7daf2SSergei Shtylyov 	return error;
292efe7daf2SSergei Shtylyov }
293efe7daf2SSergei Shtylyov 
294*6c21caa3SManjunath Goudar static int ohci_da8xx_remove(struct platform_device *pdev)
295efe7daf2SSergei Shtylyov {
296*6c21caa3SManjunath Goudar 	struct usb_hcd	*hcd = platform_get_drvdata(pdev);
297d4f09e28SJingoo Han 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(&pdev->dev);
298efe7daf2SSergei Shtylyov 
299efe7daf2SSergei Shtylyov 	hub->ocic_notify(NULL);
300efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
301efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
302efe7daf2SSergei Shtylyov 
303efe7daf2SSergei Shtylyov 	return 0;
304efe7daf2SSergei Shtylyov }
305efe7daf2SSergei Shtylyov 
306efe7daf2SSergei Shtylyov #ifdef CONFIG_PM
307933bb1f0SMajunath Goudar static int ohci_da8xx_suspend(struct platform_device *pdev,
308933bb1f0SMajunath Goudar 				pm_message_t message)
309efe7daf2SSergei Shtylyov {
310933bb1f0SMajunath Goudar 	struct usb_hcd	*hcd	= platform_get_drvdata(pdev);
311efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
312933bb1f0SMajunath Goudar 	bool		do_wakeup	= device_may_wakeup(&pdev->dev);
313933bb1f0SMajunath Goudar 	int		ret;
314933bb1f0SMajunath Goudar 
315efe7daf2SSergei Shtylyov 
316efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
317efe7daf2SSergei Shtylyov 		msleep(5);
318efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
319efe7daf2SSergei Shtylyov 
320933bb1f0SMajunath Goudar 	ret = ohci_suspend(hcd, do_wakeup);
321933bb1f0SMajunath Goudar 	if (ret)
322933bb1f0SMajunath Goudar 		return ret;
323933bb1f0SMajunath Goudar 
3246110c425SDavid Lechner 	ohci_da8xx_disable();
325efe7daf2SSergei Shtylyov 	hcd->state = HC_STATE_SUSPENDED;
326933bb1f0SMajunath Goudar 
327933bb1f0SMajunath Goudar 	return ret;
328efe7daf2SSergei Shtylyov }
329efe7daf2SSergei Shtylyov 
330efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev)
331efe7daf2SSergei Shtylyov {
332efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd	= platform_get_drvdata(dev);
333efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
3346110c425SDavid Lechner 	int ret;
335efe7daf2SSergei Shtylyov 
336efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
337efe7daf2SSergei Shtylyov 		msleep(5);
338efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
339efe7daf2SSergei Shtylyov 
3406110c425SDavid Lechner 	ret = ohci_da8xx_enable();
3416110c425SDavid Lechner 	if (ret)
3426110c425SDavid Lechner 		return ret;
3436110c425SDavid Lechner 
344efe7daf2SSergei Shtylyov 	dev->dev.power.power_state = PMSG_ON;
345efe7daf2SSergei Shtylyov 	usb_hcd_resume_root_hub(hcd);
3466110c425SDavid Lechner 
347efe7daf2SSergei Shtylyov 	return 0;
348efe7daf2SSergei Shtylyov }
349efe7daf2SSergei Shtylyov #endif
350efe7daf2SSergei Shtylyov 
351*6c21caa3SManjunath Goudar static const struct ohci_driver_overrides da8xx_overrides __initconst = {
352*6c21caa3SManjunath Goudar 	.reset		= ohci_da8xx_reset,
353*6c21caa3SManjunath Goudar };
354*6c21caa3SManjunath Goudar 
355efe7daf2SSergei Shtylyov /*
356efe7daf2SSergei Shtylyov  * Driver definition to register with platform structure.
357efe7daf2SSergei Shtylyov  */
358efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = {
359*6c21caa3SManjunath Goudar 	.probe		= ohci_da8xx_probe,
360*6c21caa3SManjunath Goudar 	.remove		= ohci_da8xx_remove,
361efe7daf2SSergei Shtylyov 	.shutdown 	= usb_hcd_platform_shutdown,
362efe7daf2SSergei Shtylyov #ifdef	CONFIG_PM
363efe7daf2SSergei Shtylyov 	.suspend	= ohci_da8xx_suspend,
364efe7daf2SSergei Shtylyov 	.resume		= ohci_da8xx_resume,
365efe7daf2SSergei Shtylyov #endif
366efe7daf2SSergei Shtylyov 	.driver		= {
367*6c21caa3SManjunath Goudar 		.name	= DRV_NAME,
368efe7daf2SSergei Shtylyov 	},
369efe7daf2SSergei Shtylyov };
370ab59ac01SJan Luebbe 
371*6c21caa3SManjunath Goudar static int __init ohci_da8xx_init(void)
372*6c21caa3SManjunath Goudar {
373*6c21caa3SManjunath Goudar 
374*6c21caa3SManjunath Goudar 	if (usb_disabled())
375*6c21caa3SManjunath Goudar 		return -ENODEV;
376*6c21caa3SManjunath Goudar 
377*6c21caa3SManjunath Goudar 	pr_info("%s: " DRIVER_DESC "\n", DRV_NAME);
378*6c21caa3SManjunath Goudar 	ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides);
379*6c21caa3SManjunath Goudar 
380*6c21caa3SManjunath Goudar 	/*
381*6c21caa3SManjunath Goudar 	 * The Davinci da8xx HW has some unusual quirks, which require
382*6c21caa3SManjunath Goudar 	 * da8xx-specific workarounds. We override certain hc_driver
383*6c21caa3SManjunath Goudar 	 * functions here to achieve that. We explicitly do not enhance
384*6c21caa3SManjunath Goudar 	 * ohci_driver_overrides to allow this more easily, since this
385*6c21caa3SManjunath Goudar 	 * is an unusual case, and we don't want to encourage others to
386*6c21caa3SManjunath Goudar 	 * override these functions by making it too easy.
387*6c21caa3SManjunath Goudar 	 */
388*6c21caa3SManjunath Goudar 
389*6c21caa3SManjunath Goudar 	orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control;
390*6c21caa3SManjunath Goudar 	orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data;
391*6c21caa3SManjunath Goudar 
392*6c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_status_data     = ohci_da8xx_hub_status_data;
393*6c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_control         = ohci_da8xx_hub_control;
394*6c21caa3SManjunath Goudar 
395*6c21caa3SManjunath Goudar 	return platform_driver_register(&ohci_hcd_da8xx_driver);
396*6c21caa3SManjunath Goudar }
397*6c21caa3SManjunath Goudar module_init(ohci_da8xx_init);
398*6c21caa3SManjunath Goudar 
399*6c21caa3SManjunath Goudar static void __exit ohci_da8xx_exit(void)
400*6c21caa3SManjunath Goudar {
401*6c21caa3SManjunath Goudar 	platform_driver_unregister(&ohci_hcd_da8xx_driver);
402*6c21caa3SManjunath Goudar }
403*6c21caa3SManjunath Goudar module_exit(ohci_da8xx_exit);
404*6c21caa3SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC);
405*6c21caa3SManjunath Goudar MODULE_LICENSE("GPL");
406*6c21caa3SManjunath Goudar MODULE_ALIAS("platform:" DRV_NAME);
407