xref: /openbmc/linux/drivers/usb/host/ohci-da8xx.c (revision c844ff74b04fe83245c908a8c92f0a7620b25c4c)
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>
23*c844ff74SAxel Haslam #include <linux/regulator/consumer.h>
246c21caa3SManjunath Goudar #include <linux/usb.h>
256c21caa3SManjunath Goudar #include <linux/usb/hcd.h>
266c21caa3SManjunath Goudar #include <asm/unaligned.h>
27efe7daf2SSergei Shtylyov 
286c21caa3SManjunath Goudar #include "ohci.h"
296c21caa3SManjunath Goudar 
306c21caa3SManjunath Goudar #define DRIVER_DESC "DA8XX"
31eacae5d2SAxel Haslam #define DRV_NAME "ohci-da8xx"
326c21caa3SManjunath Goudar 
336c21caa3SManjunath Goudar static struct hc_driver __read_mostly ohci_da8xx_hc_driver;
346c21caa3SManjunath Goudar 
356c21caa3SManjunath Goudar static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
366c21caa3SManjunath Goudar 			u16 wValue, u16 wIndex, char *buf, u16 wLength);
376c21caa3SManjunath Goudar static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
38efe7daf2SSergei Shtylyov 
39c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd {
40*c844ff74SAxel Haslam 	struct usb_hcd *hcd;
41c7a4f9f3SAxel Haslam 	struct clk *usb11_clk;
42c7a4f9f3SAxel Haslam 	struct phy *usb11_phy;
43*c844ff74SAxel Haslam 	struct regulator *vbus_reg;
44*c844ff74SAxel Haslam 	struct notifier_block nb;
45*c844ff74SAxel Haslam 	unsigned int reg_enabled;
46c7a4f9f3SAxel Haslam };
47c7a4f9f3SAxel Haslam 
48c7a4f9f3SAxel Haslam #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
49efe7daf2SSergei Shtylyov 
50efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */
51efe7daf2SSergei Shtylyov static volatile u16 ocic_mask;
52efe7daf2SSergei Shtylyov 
53c7a4f9f3SAxel Haslam static int ohci_da8xx_enable(struct usb_hcd *hcd)
54efe7daf2SSergei Shtylyov {
55c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
566110c425SDavid Lechner 	int ret;
57efe7daf2SSergei Shtylyov 
58c7a4f9f3SAxel Haslam 	ret = clk_prepare_enable(da8xx_ohci->usb11_clk);
596110c425SDavid Lechner 	if (ret)
606110c425SDavid Lechner 		return ret;
61efe7daf2SSergei Shtylyov 
62c7a4f9f3SAxel Haslam 	ret = phy_init(da8xx_ohci->usb11_phy);
636110c425SDavid Lechner 	if (ret)
646110c425SDavid Lechner 		goto err_phy_init;
65efe7daf2SSergei Shtylyov 
66c7a4f9f3SAxel Haslam 	ret = phy_power_on(da8xx_ohci->usb11_phy);
676110c425SDavid Lechner 	if (ret)
686110c425SDavid Lechner 		goto err_phy_power_on;
69efe7daf2SSergei Shtylyov 
706110c425SDavid Lechner 	return 0;
716110c425SDavid Lechner 
726110c425SDavid Lechner err_phy_power_on:
73c7a4f9f3SAxel Haslam 	phy_exit(da8xx_ohci->usb11_phy);
746110c425SDavid Lechner err_phy_init:
75c7a4f9f3SAxel Haslam 	clk_disable_unprepare(da8xx_ohci->usb11_clk);
766110c425SDavid Lechner 
776110c425SDavid Lechner 	return ret;
78efe7daf2SSergei Shtylyov }
79efe7daf2SSergei Shtylyov 
80c7a4f9f3SAxel Haslam static void ohci_da8xx_disable(struct usb_hcd *hcd)
816110c425SDavid Lechner {
82c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
83c7a4f9f3SAxel Haslam 
84c7a4f9f3SAxel Haslam 	phy_power_off(da8xx_ohci->usb11_phy);
85c7a4f9f3SAxel Haslam 	phy_exit(da8xx_ohci->usb11_phy);
86c7a4f9f3SAxel Haslam 	clk_disable_unprepare(da8xx_ohci->usb11_clk);
87efe7daf2SSergei Shtylyov }
88efe7daf2SSergei Shtylyov 
89f3c56fb3SAxel Haslam static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on)
90f3c56fb3SAxel Haslam {
91*c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
92f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
93f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
94*c844ff74SAxel Haslam 	int ret;
95f3c56fb3SAxel Haslam 
96f3c56fb3SAxel Haslam 	if (hub && hub->set_power)
97f3c56fb3SAxel Haslam 		return hub->set_power(1, on);
98f3c56fb3SAxel Haslam 
99*c844ff74SAxel Haslam 	if (!da8xx_ohci->vbus_reg)
100*c844ff74SAxel Haslam 		return 0;
101*c844ff74SAxel Haslam 
102*c844ff74SAxel Haslam 	if (on && !da8xx_ohci->reg_enabled) {
103*c844ff74SAxel Haslam 		ret = regulator_enable(da8xx_ohci->vbus_reg);
104*c844ff74SAxel Haslam 		if (ret) {
105*c844ff74SAxel Haslam 			dev_err(dev, "Failed to enable regulator: %d\n", ret);
106*c844ff74SAxel Haslam 			return ret;
107*c844ff74SAxel Haslam 		}
108*c844ff74SAxel Haslam 		da8xx_ohci->reg_enabled = 1;
109*c844ff74SAxel Haslam 
110*c844ff74SAxel Haslam 	} else if (!on && da8xx_ohci->reg_enabled) {
111*c844ff74SAxel Haslam 		ret = regulator_disable(da8xx_ohci->vbus_reg);
112*c844ff74SAxel Haslam 		if (ret) {
113*c844ff74SAxel Haslam 			dev_err(dev, "Failed  to disable regulator: %d\n", ret);
114*c844ff74SAxel Haslam 			return ret;
115*c844ff74SAxel Haslam 		}
116*c844ff74SAxel Haslam 		da8xx_ohci->reg_enabled = 0;
117*c844ff74SAxel Haslam 	}
118*c844ff74SAxel Haslam 
119f3c56fb3SAxel Haslam 	return 0;
120f3c56fb3SAxel Haslam }
121f3c56fb3SAxel Haslam 
122f3c56fb3SAxel Haslam static int ohci_da8xx_get_power(struct usb_hcd *hcd)
123f3c56fb3SAxel Haslam {
124*c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
125f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
126f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
127f3c56fb3SAxel Haslam 
128f3c56fb3SAxel Haslam 	if (hub && hub->get_power)
129f3c56fb3SAxel Haslam 		return hub->get_power(1);
130f3c56fb3SAxel Haslam 
131*c844ff74SAxel Haslam 	if (da8xx_ohci->vbus_reg)
132*c844ff74SAxel Haslam 		return regulator_is_enabled(da8xx_ohci->vbus_reg);
133*c844ff74SAxel Haslam 
134f3c56fb3SAxel Haslam 	return 1;
135f3c56fb3SAxel Haslam }
136f3c56fb3SAxel Haslam 
137f3c56fb3SAxel Haslam static int ohci_da8xx_get_oci(struct usb_hcd *hcd)
138f3c56fb3SAxel Haslam {
139*c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
140f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
141f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
142*c844ff74SAxel Haslam 	unsigned int flags;
143*c844ff74SAxel Haslam 	int ret;
144f3c56fb3SAxel Haslam 
145f3c56fb3SAxel Haslam 	if (hub && hub->get_oci)
146f3c56fb3SAxel Haslam 		return hub->get_oci(1);
147f3c56fb3SAxel Haslam 
148*c844ff74SAxel Haslam 	if (!da8xx_ohci->vbus_reg)
149*c844ff74SAxel Haslam 		return 0;
150*c844ff74SAxel Haslam 
151*c844ff74SAxel Haslam 	ret = regulator_get_error_flags(da8xx_ohci->vbus_reg, &flags);
152*c844ff74SAxel Haslam 	if (ret)
153*c844ff74SAxel Haslam 		return ret;
154*c844ff74SAxel Haslam 
155*c844ff74SAxel Haslam 	if (flags & REGULATOR_ERROR_OVER_CURRENT)
156*c844ff74SAxel Haslam 		return 1;
157*c844ff74SAxel Haslam 
158f3c56fb3SAxel Haslam 	return 0;
159f3c56fb3SAxel Haslam }
160f3c56fb3SAxel Haslam 
161f3c56fb3SAxel Haslam static int ohci_da8xx_has_set_power(struct usb_hcd *hcd)
162f3c56fb3SAxel Haslam {
163*c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
164f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
165f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
166f3c56fb3SAxel Haslam 
167f3c56fb3SAxel Haslam 	if (hub && hub->set_power)
168f3c56fb3SAxel Haslam 		return 1;
169f3c56fb3SAxel Haslam 
170*c844ff74SAxel Haslam 	if (da8xx_ohci->vbus_reg)
171*c844ff74SAxel Haslam 		return 1;
172*c844ff74SAxel Haslam 
173f3c56fb3SAxel Haslam 	return 0;
174f3c56fb3SAxel Haslam }
175f3c56fb3SAxel Haslam 
176f3c56fb3SAxel Haslam static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
177f3c56fb3SAxel Haslam {
178*c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
179f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
180f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
181f3c56fb3SAxel Haslam 
182f3c56fb3SAxel Haslam 	if (hub && hub->get_oci)
183f3c56fb3SAxel Haslam 		return 1;
184f3c56fb3SAxel Haslam 
185*c844ff74SAxel Haslam 	if (da8xx_ohci->vbus_reg)
186*c844ff74SAxel Haslam 		return 1;
187*c844ff74SAxel Haslam 
188f3c56fb3SAxel Haslam 	return 0;
189f3c56fb3SAxel Haslam }
190f3c56fb3SAxel Haslam 
191f3c56fb3SAxel Haslam static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
192f3c56fb3SAxel Haslam {
193f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
194f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
195f3c56fb3SAxel Haslam 
196f3c56fb3SAxel Haslam 	if (hub && hub->potpgt)
197f3c56fb3SAxel Haslam 		return 1;
198f3c56fb3SAxel Haslam 
199f3c56fb3SAxel Haslam 	return 0;
200f3c56fb3SAxel Haslam }
201f3c56fb3SAxel Haslam 
202efe7daf2SSergei Shtylyov /*
203efe7daf2SSergei Shtylyov  * Handle the port over-current indicator change.
204efe7daf2SSergei Shtylyov  */
205efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
206efe7daf2SSergei Shtylyov 				    unsigned port)
207efe7daf2SSergei Shtylyov {
208efe7daf2SSergei Shtylyov 	ocic_mask |= 1 << port;
209efe7daf2SSergei Shtylyov 
210efe7daf2SSergei Shtylyov 	/* Once over-current is detected, the port needs to be powered down */
211efe7daf2SSergei Shtylyov 	if (hub->get_oci(port) > 0)
212efe7daf2SSergei Shtylyov 		hub->set_power(port, 0);
213efe7daf2SSergei Shtylyov }
214efe7daf2SSergei Shtylyov 
215*c844ff74SAxel Haslam static int ohci_da8xx_regulator_event(struct notifier_block *nb,
216*c844ff74SAxel Haslam 				unsigned long event, void *data)
217f3c56fb3SAxel Haslam {
218*c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci =
219*c844ff74SAxel Haslam 				container_of(nb, struct da8xx_ohci_hcd, nb);
220f3c56fb3SAxel Haslam 
221*c844ff74SAxel Haslam 	if (event & REGULATOR_EVENT_OVER_CURRENT) {
222*c844ff74SAxel Haslam 		ocic_mask |= 1 << 1;
223*c844ff74SAxel Haslam 		ohci_da8xx_set_power(da8xx_ohci->hcd, 0);
224*c844ff74SAxel Haslam 	}
225f3c56fb3SAxel Haslam 
226f3c56fb3SAxel Haslam 	return 0;
227f3c56fb3SAxel Haslam }
228f3c56fb3SAxel Haslam 
229*c844ff74SAxel Haslam static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
230*c844ff74SAxel Haslam {
231*c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
232*c844ff74SAxel Haslam 	struct device *dev		= hcd->self.controller;
233*c844ff74SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
234*c844ff74SAxel Haslam 	int ret = 0;
235*c844ff74SAxel Haslam 
236*c844ff74SAxel Haslam 	if (hub && hub->ocic_notify) {
237*c844ff74SAxel Haslam 		ret = hub->ocic_notify(ohci_da8xx_ocic_handler);
238*c844ff74SAxel Haslam 	} else if (da8xx_ohci->vbus_reg) {
239*c844ff74SAxel Haslam 		da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event;
240*c844ff74SAxel Haslam 		ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg,
241*c844ff74SAxel Haslam 						&da8xx_ohci->nb);
242*c844ff74SAxel Haslam 	}
243*c844ff74SAxel Haslam 
244*c844ff74SAxel Haslam 	if (ret)
245*c844ff74SAxel Haslam 		dev_err(dev, "Failed to register notifier: %d\n", ret);
246*c844ff74SAxel Haslam 
247*c844ff74SAxel Haslam 	return ret;
248*c844ff74SAxel Haslam }
249*c844ff74SAxel Haslam 
250f3c56fb3SAxel Haslam static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd)
251f3c56fb3SAxel Haslam {
252f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
253f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
254f3c56fb3SAxel Haslam 
255f3c56fb3SAxel Haslam 	if (hub && hub->ocic_notify)
256f3c56fb3SAxel Haslam 		hub->ocic_notify(NULL);
257f3c56fb3SAxel Haslam }
258f3c56fb3SAxel Haslam 
2596c21caa3SManjunath Goudar static int ohci_da8xx_reset(struct usb_hcd *hcd)
260efe7daf2SSergei Shtylyov {
261efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
262d4f09e28SJingoo Han 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
263efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci		= hcd_to_ohci(hcd);
264efe7daf2SSergei Shtylyov 	int result;
265efe7daf2SSergei Shtylyov 	u32 rh_a;
266efe7daf2SSergei Shtylyov 
267efe7daf2SSergei Shtylyov 	dev_dbg(dev, "starting USB controller\n");
268efe7daf2SSergei Shtylyov 
269c7a4f9f3SAxel Haslam 	result = ohci_da8xx_enable(hcd);
2706110c425SDavid Lechner 	if (result < 0)
2716110c425SDavid Lechner 		return result;
272efe7daf2SSergei Shtylyov 
273efe7daf2SSergei Shtylyov 	/*
274efe7daf2SSergei Shtylyov 	 * DA8xx only have 1 port connected to the pins but the HC root hub
275efe7daf2SSergei Shtylyov 	 * register A reports 2 ports, thus we'll have to override it...
276efe7daf2SSergei Shtylyov 	 */
277efe7daf2SSergei Shtylyov 	ohci->num_ports = 1;
278efe7daf2SSergei Shtylyov 
2796c21caa3SManjunath Goudar 	result = ohci_setup(hcd);
2806110c425SDavid Lechner 	if (result < 0) {
281c7a4f9f3SAxel Haslam 		ohci_da8xx_disable(hcd);
282efe7daf2SSergei Shtylyov 		return result;
2836110c425SDavid Lechner 	}
284efe7daf2SSergei Shtylyov 
285efe7daf2SSergei Shtylyov 	/*
286efe7daf2SSergei Shtylyov 	 * Since we're providing a board-specific root hub port power control
287efe7daf2SSergei Shtylyov 	 * and over-current reporting, we have to override the HC root hub A
288efe7daf2SSergei Shtylyov 	 * register's default value, so that ohci_hub_control() could return
289efe7daf2SSergei Shtylyov 	 * the correct hub descriptor...
290efe7daf2SSergei Shtylyov 	 */
291efe7daf2SSergei Shtylyov 	rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
292f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_set_power(hcd)) {
293efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NPS;
294efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_PSM;
295efe7daf2SSergei Shtylyov 	}
296f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_oci(hcd)) {
297efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NOCP;
298efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_OCPM;
299efe7daf2SSergei Shtylyov 	}
300f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_potpgt(hcd)) {
301efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_POTPGT;
302efe7daf2SSergei Shtylyov 		rh_a |= hub->potpgt << 24;
303f3c56fb3SAxel Haslam 	}
304efe7daf2SSergei Shtylyov 	ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
305efe7daf2SSergei Shtylyov 
306efe7daf2SSergei Shtylyov 	return result;
307efe7daf2SSergei Shtylyov }
308efe7daf2SSergei Shtylyov 
309efe7daf2SSergei Shtylyov /*
310efe7daf2SSergei Shtylyov  * Update the status data from the hub with the over-current indicator change.
311efe7daf2SSergei Shtylyov  */
312efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
313efe7daf2SSergei Shtylyov {
3146c21caa3SManjunath Goudar 	int length		= orig_ohci_hub_status_data(hcd, buf);
315efe7daf2SSergei Shtylyov 
316efe7daf2SSergei Shtylyov 	/* See if we have OCIC bit set on port 1 */
317efe7daf2SSergei Shtylyov 	if (ocic_mask & (1 << 1)) {
318efe7daf2SSergei Shtylyov 		dev_dbg(hcd->self.controller, "over-current indicator change "
319efe7daf2SSergei Shtylyov 			"on port 1\n");
320efe7daf2SSergei Shtylyov 
321efe7daf2SSergei Shtylyov 		if (!length)
322efe7daf2SSergei Shtylyov 			length = 1;
323efe7daf2SSergei Shtylyov 
324efe7daf2SSergei Shtylyov 		buf[0] |= 1 << 1;
325efe7daf2SSergei Shtylyov 	}
326efe7daf2SSergei Shtylyov 	return length;
327efe7daf2SSergei Shtylyov }
328efe7daf2SSergei Shtylyov 
329efe7daf2SSergei Shtylyov /*
330efe7daf2SSergei Shtylyov  * Look at the control requests to the root hub and see if we need to override.
331efe7daf2SSergei Shtylyov  */
332efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
333efe7daf2SSergei Shtylyov 				  u16 wIndex, char *buf, u16 wLength)
334efe7daf2SSergei Shtylyov {
335efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
336efe7daf2SSergei Shtylyov 	int temp;
337efe7daf2SSergei Shtylyov 
338efe7daf2SSergei Shtylyov 	switch (typeReq) {
339efe7daf2SSergei Shtylyov 	case GetPortStatus:
340efe7daf2SSergei Shtylyov 		/* Check the port number */
341efe7daf2SSergei Shtylyov 		if (wIndex != 1)
342efe7daf2SSergei Shtylyov 			break;
343efe7daf2SSergei Shtylyov 
344efe7daf2SSergei Shtylyov 		dev_dbg(dev, "GetPortStatus(%u)\n", wIndex);
345efe7daf2SSergei Shtylyov 
346efe7daf2SSergei Shtylyov 		temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
347efe7daf2SSergei Shtylyov 
348efe7daf2SSergei Shtylyov 		/* The port power status (PPS) bit defaults to 1 */
349f3c56fb3SAxel Haslam 		if (!ohci_da8xx_get_power(hcd))
350efe7daf2SSergei Shtylyov 			temp &= ~RH_PS_PPS;
351efe7daf2SSergei Shtylyov 
352efe7daf2SSergei Shtylyov 		/* The port over-current indicator (POCI) bit is always 0 */
353f3c56fb3SAxel Haslam 		if (ohci_da8xx_get_oci(hcd) > 0)
354efe7daf2SSergei Shtylyov 			temp |=  RH_PS_POCI;
355efe7daf2SSergei Shtylyov 
356efe7daf2SSergei Shtylyov 		/* The over-current indicator change (OCIC) bit is 0 too */
357efe7daf2SSergei Shtylyov 		if (ocic_mask & (1 << wIndex))
358efe7daf2SSergei Shtylyov 			temp |=  RH_PS_OCIC;
359efe7daf2SSergei Shtylyov 
360efe7daf2SSergei Shtylyov 		put_unaligned(cpu_to_le32(temp), (__le32 *)buf);
361efe7daf2SSergei Shtylyov 		return 0;
362efe7daf2SSergei Shtylyov 	case SetPortFeature:
363efe7daf2SSergei Shtylyov 		temp = 1;
364efe7daf2SSergei Shtylyov 		goto check_port;
365efe7daf2SSergei Shtylyov 	case ClearPortFeature:
366efe7daf2SSergei Shtylyov 		temp = 0;
367efe7daf2SSergei Shtylyov 
368efe7daf2SSergei Shtylyov check_port:
369efe7daf2SSergei Shtylyov 		/* Check the port number */
370efe7daf2SSergei Shtylyov 		if (wIndex != 1)
371efe7daf2SSergei Shtylyov 			break;
372efe7daf2SSergei Shtylyov 
373efe7daf2SSergei Shtylyov 		switch (wValue) {
374efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_POWER:
375efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
376efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex, "POWER");
377efe7daf2SSergei Shtylyov 
378f3c56fb3SAxel Haslam 			return ohci_da8xx_set_power(hcd, temp) ? -EPIPE : 0;
379efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_C_OVER_CURRENT:
380efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
381efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex,
382efe7daf2SSergei Shtylyov 				"C_OVER_CURRENT");
383efe7daf2SSergei Shtylyov 
384efe7daf2SSergei Shtylyov 			if (temp)
385efe7daf2SSergei Shtylyov 				ocic_mask |= 1 << wIndex;
386efe7daf2SSergei Shtylyov 			else
387efe7daf2SSergei Shtylyov 				ocic_mask &= ~(1 << wIndex);
388efe7daf2SSergei Shtylyov 			return 0;
389efe7daf2SSergei Shtylyov 		}
390efe7daf2SSergei Shtylyov 	}
391efe7daf2SSergei Shtylyov 
3926c21caa3SManjunath Goudar 	return orig_ohci_hub_control(hcd, typeReq, wValue,
3936c21caa3SManjunath Goudar 			wIndex, buf, wLength);
394efe7daf2SSergei Shtylyov }
395efe7daf2SSergei Shtylyov 
396efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/
397efe7daf2SSergei Shtylyov 
3986c21caa3SManjunath Goudar static int ohci_da8xx_probe(struct platform_device *pdev)
399efe7daf2SSergei Shtylyov {
400c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci;
401efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd;
402efe7daf2SSergei Shtylyov 	struct resource *mem;
403efe7daf2SSergei Shtylyov 	int error, irq;
4046c21caa3SManjunath Goudar 	hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev,
4056c21caa3SManjunath Goudar 				dev_name(&pdev->dev));
406644db166SJingoo Han 	if (!hcd)
407644db166SJingoo Han 		return -ENOMEM;
408efe7daf2SSergei Shtylyov 
409c7a4f9f3SAxel Haslam 	da8xx_ohci = to_da8xx_ohci(hcd);
410*c844ff74SAxel Haslam 	da8xx_ohci->hcd = hcd;
411c7a4f9f3SAxel Haslam 
412c7a4f9f3SAxel Haslam 	da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11");
413c7a4f9f3SAxel Haslam 	if (IS_ERR(da8xx_ohci->usb11_clk)) {
414c7a4f9f3SAxel Haslam 		error = PTR_ERR(da8xx_ohci->usb11_clk);
415c7a4f9f3SAxel Haslam 		if (error != -EPROBE_DEFER)
416c7a4f9f3SAxel Haslam 			dev_err(&pdev->dev, "Failed to get clock.\n");
417c7a4f9f3SAxel Haslam 		goto err;
418c7a4f9f3SAxel Haslam 	}
419c7a4f9f3SAxel Haslam 
420c7a4f9f3SAxel Haslam 	da8xx_ohci->usb11_phy = devm_phy_get(&pdev->dev, "usb-phy");
421c7a4f9f3SAxel Haslam 	if (IS_ERR(da8xx_ohci->usb11_phy)) {
422c7a4f9f3SAxel Haslam 		error = PTR_ERR(da8xx_ohci->usb11_phy);
423c7a4f9f3SAxel Haslam 		if (error != -EPROBE_DEFER)
424c7a4f9f3SAxel Haslam 			dev_err(&pdev->dev, "Failed to get phy.\n");
425c7a4f9f3SAxel Haslam 		goto err;
426c7a4f9f3SAxel Haslam 	}
427c7a4f9f3SAxel Haslam 
428*c844ff74SAxel Haslam 	da8xx_ohci->vbus_reg = devm_regulator_get_optional(&pdev->dev, "vbus");
429*c844ff74SAxel Haslam 	if (IS_ERR(da8xx_ohci->vbus_reg)) {
430*c844ff74SAxel Haslam 		error = PTR_ERR(da8xx_ohci->vbus_reg);
431*c844ff74SAxel Haslam 		if (error == -ENODEV) {
432*c844ff74SAxel Haslam 			da8xx_ohci->vbus_reg = NULL;
433*c844ff74SAxel Haslam 		} else if (error == -EPROBE_DEFER) {
434*c844ff74SAxel Haslam 			goto err;
435*c844ff74SAxel Haslam 		} else {
436*c844ff74SAxel Haslam 			dev_err(&pdev->dev, "Failed to get regulator\n");
437*c844ff74SAxel Haslam 			goto err;
438*c844ff74SAxel Haslam 		}
439*c844ff74SAxel Haslam 	}
440*c844ff74SAxel Haslam 
441efe7daf2SSergei Shtylyov 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
442644db166SJingoo Han 	hcd->regs = devm_ioremap_resource(&pdev->dev, mem);
443644db166SJingoo Han 	if (IS_ERR(hcd->regs)) {
444644db166SJingoo Han 		error = PTR_ERR(hcd->regs);
445644db166SJingoo Han 		goto err;
446efe7daf2SSergei Shtylyov 	}
44754891d74SVarka Bhadram 	hcd->rsrc_start = mem->start;
44854891d74SVarka Bhadram 	hcd->rsrc_len = resource_size(mem);
449efe7daf2SSergei Shtylyov 
450efe7daf2SSergei Shtylyov 	irq = platform_get_irq(pdev, 0);
451efe7daf2SSergei Shtylyov 	if (irq < 0) {
452efe7daf2SSergei Shtylyov 		error = -ENODEV;
453644db166SJingoo Han 		goto err;
454efe7daf2SSergei Shtylyov 	}
4556c21caa3SManjunath Goudar 
456b5dd18d8SYong Zhang 	error = usb_add_hcd(hcd, irq, 0);
457efe7daf2SSergei Shtylyov 	if (error)
458644db166SJingoo Han 		goto err;
459efe7daf2SSergei Shtylyov 
4603c9740a1SPeter Chen 	device_wakeup_enable(hcd->self.controller);
4613c9740a1SPeter Chen 
462f3c56fb3SAxel Haslam 	error = ohci_da8xx_register_notify(hcd);
463f3c56fb3SAxel Haslam 	if (error)
464f3c56fb3SAxel Haslam 		goto err_remove_hcd;
465efe7daf2SSergei Shtylyov 
466f3c56fb3SAxel Haslam 	return 0;
467f3c56fb3SAxel Haslam 
468f3c56fb3SAxel Haslam err_remove_hcd:
469efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
470644db166SJingoo Han err:
471efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
472efe7daf2SSergei Shtylyov 	return error;
473efe7daf2SSergei Shtylyov }
474efe7daf2SSergei Shtylyov 
4756c21caa3SManjunath Goudar static int ohci_da8xx_remove(struct platform_device *pdev)
476efe7daf2SSergei Shtylyov {
4776c21caa3SManjunath Goudar 	struct usb_hcd	*hcd = platform_get_drvdata(pdev);
478efe7daf2SSergei Shtylyov 
479f3c56fb3SAxel Haslam 	ohci_da8xx_unregister_notify(hcd);
480efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
481efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
482efe7daf2SSergei Shtylyov 
483efe7daf2SSergei Shtylyov 	return 0;
484efe7daf2SSergei Shtylyov }
485efe7daf2SSergei Shtylyov 
486efe7daf2SSergei Shtylyov #ifdef CONFIG_PM
487933bb1f0SMajunath Goudar static int ohci_da8xx_suspend(struct platform_device *pdev,
488933bb1f0SMajunath Goudar 				pm_message_t message)
489efe7daf2SSergei Shtylyov {
490933bb1f0SMajunath Goudar 	struct usb_hcd	*hcd	= platform_get_drvdata(pdev);
491efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
492933bb1f0SMajunath Goudar 	bool		do_wakeup	= device_may_wakeup(&pdev->dev);
493933bb1f0SMajunath Goudar 	int		ret;
494933bb1f0SMajunath Goudar 
495efe7daf2SSergei Shtylyov 
496efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
497efe7daf2SSergei Shtylyov 		msleep(5);
498efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
499efe7daf2SSergei Shtylyov 
500933bb1f0SMajunath Goudar 	ret = ohci_suspend(hcd, do_wakeup);
501933bb1f0SMajunath Goudar 	if (ret)
502933bb1f0SMajunath Goudar 		return ret;
503933bb1f0SMajunath Goudar 
504c7a4f9f3SAxel Haslam 	ohci_da8xx_disable(hcd);
505efe7daf2SSergei Shtylyov 	hcd->state = HC_STATE_SUSPENDED;
506933bb1f0SMajunath Goudar 
507933bb1f0SMajunath Goudar 	return ret;
508efe7daf2SSergei Shtylyov }
509efe7daf2SSergei Shtylyov 
510efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev)
511efe7daf2SSergei Shtylyov {
512efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd	= platform_get_drvdata(dev);
513efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
5146110c425SDavid Lechner 	int ret;
515efe7daf2SSergei Shtylyov 
516efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
517efe7daf2SSergei Shtylyov 		msleep(5);
518efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
519efe7daf2SSergei Shtylyov 
520c7a4f9f3SAxel Haslam 	ret = ohci_da8xx_enable(hcd);
5216110c425SDavid Lechner 	if (ret)
5226110c425SDavid Lechner 		return ret;
5236110c425SDavid Lechner 
524efe7daf2SSergei Shtylyov 	dev->dev.power.power_state = PMSG_ON;
525efe7daf2SSergei Shtylyov 	usb_hcd_resume_root_hub(hcd);
5266110c425SDavid Lechner 
527efe7daf2SSergei Shtylyov 	return 0;
528efe7daf2SSergei Shtylyov }
529efe7daf2SSergei Shtylyov #endif
530efe7daf2SSergei Shtylyov 
5316c21caa3SManjunath Goudar static const struct ohci_driver_overrides da8xx_overrides __initconst = {
5326c21caa3SManjunath Goudar 	.reset		 = ohci_da8xx_reset,
533c7a4f9f3SAxel Haslam 	.extra_priv_size = sizeof(struct da8xx_ohci_hcd),
5346c21caa3SManjunath Goudar };
5356c21caa3SManjunath Goudar 
536efe7daf2SSergei Shtylyov /*
537efe7daf2SSergei Shtylyov  * Driver definition to register with platform structure.
538efe7daf2SSergei Shtylyov  */
539efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = {
5406c21caa3SManjunath Goudar 	.probe		= ohci_da8xx_probe,
5416c21caa3SManjunath Goudar 	.remove		= ohci_da8xx_remove,
542efe7daf2SSergei Shtylyov 	.shutdown 	= usb_hcd_platform_shutdown,
543efe7daf2SSergei Shtylyov #ifdef	CONFIG_PM
544efe7daf2SSergei Shtylyov 	.suspend	= ohci_da8xx_suspend,
545efe7daf2SSergei Shtylyov 	.resume		= ohci_da8xx_resume,
546efe7daf2SSergei Shtylyov #endif
547efe7daf2SSergei Shtylyov 	.driver		= {
5486c21caa3SManjunath Goudar 		.name	= DRV_NAME,
549efe7daf2SSergei Shtylyov 	},
550efe7daf2SSergei Shtylyov };
551ab59ac01SJan Luebbe 
5526c21caa3SManjunath Goudar static int __init ohci_da8xx_init(void)
5536c21caa3SManjunath Goudar {
5546c21caa3SManjunath Goudar 
5556c21caa3SManjunath Goudar 	if (usb_disabled())
5566c21caa3SManjunath Goudar 		return -ENODEV;
5576c21caa3SManjunath Goudar 
5586c21caa3SManjunath Goudar 	pr_info("%s: " DRIVER_DESC "\n", DRV_NAME);
5596c21caa3SManjunath Goudar 	ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides);
5606c21caa3SManjunath Goudar 
5616c21caa3SManjunath Goudar 	/*
5626c21caa3SManjunath Goudar 	 * The Davinci da8xx HW has some unusual quirks, which require
5636c21caa3SManjunath Goudar 	 * da8xx-specific workarounds. We override certain hc_driver
5646c21caa3SManjunath Goudar 	 * functions here to achieve that. We explicitly do not enhance
5656c21caa3SManjunath Goudar 	 * ohci_driver_overrides to allow this more easily, since this
5666c21caa3SManjunath Goudar 	 * is an unusual case, and we don't want to encourage others to
5676c21caa3SManjunath Goudar 	 * override these functions by making it too easy.
5686c21caa3SManjunath Goudar 	 */
5696c21caa3SManjunath Goudar 
5706c21caa3SManjunath Goudar 	orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control;
5716c21caa3SManjunath Goudar 	orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data;
5726c21caa3SManjunath Goudar 
5736c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_status_data     = ohci_da8xx_hub_status_data;
5746c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_control         = ohci_da8xx_hub_control;
5756c21caa3SManjunath Goudar 
5766c21caa3SManjunath Goudar 	return platform_driver_register(&ohci_hcd_da8xx_driver);
5776c21caa3SManjunath Goudar }
5786c21caa3SManjunath Goudar module_init(ohci_da8xx_init);
5796c21caa3SManjunath Goudar 
5806c21caa3SManjunath Goudar static void __exit ohci_da8xx_exit(void)
5816c21caa3SManjunath Goudar {
5826c21caa3SManjunath Goudar 	platform_driver_unregister(&ohci_hcd_da8xx_driver);
5836c21caa3SManjunath Goudar }
5846c21caa3SManjunath Goudar module_exit(ohci_da8xx_exit);
5856c21caa3SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC);
5866c21caa3SManjunath Goudar MODULE_LICENSE("GPL");
5876c21caa3SManjunath Goudar MODULE_ALIAS("platform:" DRV_NAME);
588