xref: /openbmc/linux/drivers/usb/host/ohci-da8xx.c (revision 08e46f18b7d04ce4892c7542e89c2de4ae359987)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2efe7daf2SSergei Shtylyov /*
3efe7daf2SSergei Shtylyov  * OHCI HCD (Host Controller Driver) for USB.
4efe7daf2SSergei Shtylyov  *
5efe7daf2SSergei Shtylyov  * TI DA8xx (OMAP-L1x) Bus Glue
6efe7daf2SSergei Shtylyov  *
7efe7daf2SSergei Shtylyov  * Derived from: ohci-omap.c and ohci-s3c2410.c
8efe7daf2SSergei Shtylyov  * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
9efe7daf2SSergei Shtylyov  */
10efe7daf2SSergei Shtylyov 
116c21caa3SManjunath Goudar #include <linux/clk.h>
126c21caa3SManjunath Goudar #include <linux/io.h>
13efe7daf2SSergei Shtylyov #include <linux/interrupt.h>
14efe7daf2SSergei Shtylyov #include <linux/jiffies.h>
156c21caa3SManjunath Goudar #include <linux/kernel.h>
166c21caa3SManjunath Goudar #include <linux/module.h>
17efe7daf2SSergei Shtylyov #include <linux/platform_device.h>
186110c425SDavid Lechner #include <linux/phy/phy.h>
19ec2a0833SArnd Bergmann #include <linux/platform_data/usb-davinci.h>
20c844ff74SAxel Haslam #include <linux/regulator/consumer.h>
216c21caa3SManjunath Goudar #include <linux/usb.h>
226c21caa3SManjunath Goudar #include <linux/usb/hcd.h>
236c21caa3SManjunath Goudar #include <asm/unaligned.h>
24efe7daf2SSergei Shtylyov 
256c21caa3SManjunath Goudar #include "ohci.h"
266c21caa3SManjunath Goudar 
276c21caa3SManjunath Goudar #define DRIVER_DESC "DA8XX"
28eacae5d2SAxel Haslam #define DRV_NAME "ohci-da8xx"
296c21caa3SManjunath Goudar 
306c21caa3SManjunath Goudar static struct hc_driver __read_mostly ohci_da8xx_hc_driver;
316c21caa3SManjunath Goudar 
326c21caa3SManjunath Goudar static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
336c21caa3SManjunath Goudar 			u16 wValue, u16 wIndex, char *buf, u16 wLength);
346c21caa3SManjunath Goudar static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
35efe7daf2SSergei Shtylyov 
36c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd {
37c844ff74SAxel Haslam 	struct usb_hcd *hcd;
38c7a4f9f3SAxel Haslam 	struct clk *usb11_clk;
39c7a4f9f3SAxel Haslam 	struct phy *usb11_phy;
40c844ff74SAxel Haslam 	struct regulator *vbus_reg;
41c844ff74SAxel Haslam 	struct notifier_block nb;
42c844ff74SAxel Haslam 	unsigned int reg_enabled;
43c7a4f9f3SAxel Haslam };
44c7a4f9f3SAxel Haslam 
45c7a4f9f3SAxel Haslam #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
46efe7daf2SSergei Shtylyov 
47efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */
48efe7daf2SSergei Shtylyov static volatile u16 ocic_mask;
49efe7daf2SSergei Shtylyov 
50c7a4f9f3SAxel Haslam static int ohci_da8xx_enable(struct usb_hcd *hcd)
51efe7daf2SSergei Shtylyov {
52c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
536110c425SDavid Lechner 	int ret;
54efe7daf2SSergei Shtylyov 
55c7a4f9f3SAxel Haslam 	ret = clk_prepare_enable(da8xx_ohci->usb11_clk);
566110c425SDavid Lechner 	if (ret)
576110c425SDavid Lechner 		return ret;
58efe7daf2SSergei Shtylyov 
59c7a4f9f3SAxel Haslam 	ret = phy_init(da8xx_ohci->usb11_phy);
606110c425SDavid Lechner 	if (ret)
616110c425SDavid Lechner 		goto err_phy_init;
62efe7daf2SSergei Shtylyov 
63c7a4f9f3SAxel Haslam 	ret = phy_power_on(da8xx_ohci->usb11_phy);
646110c425SDavid Lechner 	if (ret)
656110c425SDavid Lechner 		goto err_phy_power_on;
66efe7daf2SSergei Shtylyov 
676110c425SDavid Lechner 	return 0;
686110c425SDavid Lechner 
696110c425SDavid Lechner err_phy_power_on:
70c7a4f9f3SAxel Haslam 	phy_exit(da8xx_ohci->usb11_phy);
716110c425SDavid Lechner err_phy_init:
72c7a4f9f3SAxel Haslam 	clk_disable_unprepare(da8xx_ohci->usb11_clk);
736110c425SDavid Lechner 
746110c425SDavid Lechner 	return ret;
75efe7daf2SSergei Shtylyov }
76efe7daf2SSergei Shtylyov 
77c7a4f9f3SAxel Haslam static void ohci_da8xx_disable(struct usb_hcd *hcd)
786110c425SDavid Lechner {
79c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
80c7a4f9f3SAxel Haslam 
81c7a4f9f3SAxel Haslam 	phy_power_off(da8xx_ohci->usb11_phy);
82c7a4f9f3SAxel Haslam 	phy_exit(da8xx_ohci->usb11_phy);
83c7a4f9f3SAxel Haslam 	clk_disable_unprepare(da8xx_ohci->usb11_clk);
84efe7daf2SSergei Shtylyov }
85efe7daf2SSergei Shtylyov 
86f3c56fb3SAxel Haslam static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on)
87f3c56fb3SAxel Haslam {
88c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
89f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
90f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
91c844ff74SAxel Haslam 	int ret;
92f3c56fb3SAxel Haslam 
93f3c56fb3SAxel Haslam 	if (hub && hub->set_power)
94f3c56fb3SAxel Haslam 		return hub->set_power(1, on);
95f3c56fb3SAxel Haslam 
96c844ff74SAxel Haslam 	if (!da8xx_ohci->vbus_reg)
97c844ff74SAxel Haslam 		return 0;
98c844ff74SAxel Haslam 
99c844ff74SAxel Haslam 	if (on && !da8xx_ohci->reg_enabled) {
100c844ff74SAxel Haslam 		ret = regulator_enable(da8xx_ohci->vbus_reg);
101c844ff74SAxel Haslam 		if (ret) {
102c844ff74SAxel Haslam 			dev_err(dev, "Failed to enable regulator: %d\n", ret);
103c844ff74SAxel Haslam 			return ret;
104c844ff74SAxel Haslam 		}
105c844ff74SAxel Haslam 		da8xx_ohci->reg_enabled = 1;
106c844ff74SAxel Haslam 
107c844ff74SAxel Haslam 	} else if (!on && da8xx_ohci->reg_enabled) {
108c844ff74SAxel Haslam 		ret = regulator_disable(da8xx_ohci->vbus_reg);
109c844ff74SAxel Haslam 		if (ret) {
110c844ff74SAxel Haslam 			dev_err(dev, "Failed  to disable regulator: %d\n", ret);
111c844ff74SAxel Haslam 			return ret;
112c844ff74SAxel Haslam 		}
113c844ff74SAxel Haslam 		da8xx_ohci->reg_enabled = 0;
114c844ff74SAxel Haslam 	}
115c844ff74SAxel Haslam 
116f3c56fb3SAxel Haslam 	return 0;
117f3c56fb3SAxel Haslam }
118f3c56fb3SAxel Haslam 
119f3c56fb3SAxel Haslam static int ohci_da8xx_get_power(struct usb_hcd *hcd)
120f3c56fb3SAxel Haslam {
121c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
122f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
123f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
124f3c56fb3SAxel Haslam 
125f3c56fb3SAxel Haslam 	if (hub && hub->get_power)
126f3c56fb3SAxel Haslam 		return hub->get_power(1);
127f3c56fb3SAxel Haslam 
128c844ff74SAxel Haslam 	if (da8xx_ohci->vbus_reg)
129c844ff74SAxel Haslam 		return regulator_is_enabled(da8xx_ohci->vbus_reg);
130c844ff74SAxel Haslam 
131f3c56fb3SAxel Haslam 	return 1;
132f3c56fb3SAxel Haslam }
133f3c56fb3SAxel Haslam 
134f3c56fb3SAxel Haslam static int ohci_da8xx_get_oci(struct usb_hcd *hcd)
135f3c56fb3SAxel Haslam {
136c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
137f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
138f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
139c844ff74SAxel Haslam 	unsigned int flags;
140c844ff74SAxel Haslam 	int ret;
141f3c56fb3SAxel Haslam 
142f3c56fb3SAxel Haslam 	if (hub && hub->get_oci)
143f3c56fb3SAxel Haslam 		return hub->get_oci(1);
144f3c56fb3SAxel Haslam 
145c844ff74SAxel Haslam 	if (!da8xx_ohci->vbus_reg)
146c844ff74SAxel Haslam 		return 0;
147c844ff74SAxel Haslam 
148c844ff74SAxel Haslam 	ret = regulator_get_error_flags(da8xx_ohci->vbus_reg, &flags);
149c844ff74SAxel Haslam 	if (ret)
150c844ff74SAxel Haslam 		return ret;
151c844ff74SAxel Haslam 
152c844ff74SAxel Haslam 	if (flags & REGULATOR_ERROR_OVER_CURRENT)
153c844ff74SAxel Haslam 		return 1;
154c844ff74SAxel Haslam 
155f3c56fb3SAxel Haslam 	return 0;
156f3c56fb3SAxel Haslam }
157f3c56fb3SAxel Haslam 
158f3c56fb3SAxel Haslam static int ohci_da8xx_has_set_power(struct usb_hcd *hcd)
159f3c56fb3SAxel Haslam {
160c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
161f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
162f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
163f3c56fb3SAxel Haslam 
164f3c56fb3SAxel Haslam 	if (hub && hub->set_power)
165f3c56fb3SAxel Haslam 		return 1;
166f3c56fb3SAxel Haslam 
167c844ff74SAxel Haslam 	if (da8xx_ohci->vbus_reg)
168c844ff74SAxel Haslam 		return 1;
169c844ff74SAxel Haslam 
170f3c56fb3SAxel Haslam 	return 0;
171f3c56fb3SAxel Haslam }
172f3c56fb3SAxel Haslam 
173f3c56fb3SAxel Haslam static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
174f3c56fb3SAxel Haslam {
175c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
176f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
177f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
178f3c56fb3SAxel Haslam 
179f3c56fb3SAxel Haslam 	if (hub && hub->get_oci)
180f3c56fb3SAxel Haslam 		return 1;
181f3c56fb3SAxel Haslam 
182c844ff74SAxel Haslam 	if (da8xx_ohci->vbus_reg)
183c844ff74SAxel Haslam 		return 1;
184c844ff74SAxel Haslam 
185f3c56fb3SAxel Haslam 	return 0;
186f3c56fb3SAxel Haslam }
187f3c56fb3SAxel Haslam 
188f3c56fb3SAxel Haslam static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
189f3c56fb3SAxel Haslam {
190f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
191f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
192f3c56fb3SAxel Haslam 
193f3c56fb3SAxel Haslam 	if (hub && hub->potpgt)
194f3c56fb3SAxel Haslam 		return 1;
195f3c56fb3SAxel Haslam 
196f3c56fb3SAxel Haslam 	return 0;
197f3c56fb3SAxel Haslam }
198f3c56fb3SAxel Haslam 
199efe7daf2SSergei Shtylyov /*
200efe7daf2SSergei Shtylyov  * Handle the port over-current indicator change.
201efe7daf2SSergei Shtylyov  */
202efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
203efe7daf2SSergei Shtylyov 				    unsigned port)
204efe7daf2SSergei Shtylyov {
205efe7daf2SSergei Shtylyov 	ocic_mask |= 1 << port;
206efe7daf2SSergei Shtylyov 
207efe7daf2SSergei Shtylyov 	/* Once over-current is detected, the port needs to be powered down */
208efe7daf2SSergei Shtylyov 	if (hub->get_oci(port) > 0)
209efe7daf2SSergei Shtylyov 		hub->set_power(port, 0);
210efe7daf2SSergei Shtylyov }
211efe7daf2SSergei Shtylyov 
212c844ff74SAxel Haslam static int ohci_da8xx_regulator_event(struct notifier_block *nb,
213c844ff74SAxel Haslam 				unsigned long event, void *data)
214f3c56fb3SAxel Haslam {
215c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci =
216c844ff74SAxel Haslam 				container_of(nb, struct da8xx_ohci_hcd, nb);
217f3c56fb3SAxel Haslam 
218c844ff74SAxel Haslam 	if (event & REGULATOR_EVENT_OVER_CURRENT) {
219c844ff74SAxel Haslam 		ocic_mask |= 1 << 1;
220c844ff74SAxel Haslam 		ohci_da8xx_set_power(da8xx_ohci->hcd, 0);
221c844ff74SAxel Haslam 	}
222f3c56fb3SAxel Haslam 
223f3c56fb3SAxel Haslam 	return 0;
224f3c56fb3SAxel Haslam }
225f3c56fb3SAxel Haslam 
226c844ff74SAxel Haslam static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
227c844ff74SAxel Haslam {
228c844ff74SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
229c844ff74SAxel Haslam 	struct device *dev		= hcd->self.controller;
230c844ff74SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
231c844ff74SAxel Haslam 	int ret = 0;
232c844ff74SAxel Haslam 
233c844ff74SAxel Haslam 	if (hub && hub->ocic_notify) {
234c844ff74SAxel Haslam 		ret = hub->ocic_notify(ohci_da8xx_ocic_handler);
235c844ff74SAxel Haslam 	} else if (da8xx_ohci->vbus_reg) {
236c844ff74SAxel Haslam 		da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event;
237c844ff74SAxel Haslam 		ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg,
238c844ff74SAxel Haslam 						&da8xx_ohci->nb);
239c844ff74SAxel Haslam 	}
240c844ff74SAxel Haslam 
241c844ff74SAxel Haslam 	if (ret)
242c844ff74SAxel Haslam 		dev_err(dev, "Failed to register notifier: %d\n", ret);
243c844ff74SAxel Haslam 
244c844ff74SAxel Haslam 	return ret;
245c844ff74SAxel Haslam }
246c844ff74SAxel Haslam 
247f3c56fb3SAxel Haslam static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd)
248f3c56fb3SAxel Haslam {
249f3c56fb3SAxel Haslam 	struct device *dev		= hcd->self.controller;
250f3c56fb3SAxel Haslam 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
251f3c56fb3SAxel Haslam 
252f3c56fb3SAxel Haslam 	if (hub && hub->ocic_notify)
253f3c56fb3SAxel Haslam 		hub->ocic_notify(NULL);
254f3c56fb3SAxel Haslam }
255f3c56fb3SAxel Haslam 
2566c21caa3SManjunath Goudar static int ohci_da8xx_reset(struct usb_hcd *hcd)
257efe7daf2SSergei Shtylyov {
258efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
259d4f09e28SJingoo Han 	struct da8xx_ohci_root_hub *hub	= dev_get_platdata(dev);
260efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci		= hcd_to_ohci(hcd);
261efe7daf2SSergei Shtylyov 	int result;
262efe7daf2SSergei Shtylyov 	u32 rh_a;
263efe7daf2SSergei Shtylyov 
264efe7daf2SSergei Shtylyov 	dev_dbg(dev, "starting USB controller\n");
265efe7daf2SSergei Shtylyov 
266c7a4f9f3SAxel Haslam 	result = ohci_da8xx_enable(hcd);
2676110c425SDavid Lechner 	if (result < 0)
2686110c425SDavid Lechner 		return result;
269efe7daf2SSergei Shtylyov 
270efe7daf2SSergei Shtylyov 	/*
271efe7daf2SSergei Shtylyov 	 * DA8xx only have 1 port connected to the pins but the HC root hub
272efe7daf2SSergei Shtylyov 	 * register A reports 2 ports, thus we'll have to override it...
273efe7daf2SSergei Shtylyov 	 */
274efe7daf2SSergei Shtylyov 	ohci->num_ports = 1;
275efe7daf2SSergei Shtylyov 
2766c21caa3SManjunath Goudar 	result = ohci_setup(hcd);
2776110c425SDavid Lechner 	if (result < 0) {
278c7a4f9f3SAxel Haslam 		ohci_da8xx_disable(hcd);
279efe7daf2SSergei Shtylyov 		return result;
2806110c425SDavid Lechner 	}
281efe7daf2SSergei Shtylyov 
282efe7daf2SSergei Shtylyov 	/*
283efe7daf2SSergei Shtylyov 	 * Since we're providing a board-specific root hub port power control
284efe7daf2SSergei Shtylyov 	 * and over-current reporting, we have to override the HC root hub A
285efe7daf2SSergei Shtylyov 	 * register's default value, so that ohci_hub_control() could return
286efe7daf2SSergei Shtylyov 	 * the correct hub descriptor...
287efe7daf2SSergei Shtylyov 	 */
288efe7daf2SSergei Shtylyov 	rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
289f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_set_power(hcd)) {
290efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NPS;
291efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_PSM;
292efe7daf2SSergei Shtylyov 	}
293f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_oci(hcd)) {
294efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_NOCP;
295efe7daf2SSergei Shtylyov 		rh_a |=  RH_A_OCPM;
296efe7daf2SSergei Shtylyov 	}
297f3c56fb3SAxel Haslam 	if (ohci_da8xx_has_potpgt(hcd)) {
298efe7daf2SSergei Shtylyov 		rh_a &= ~RH_A_POTPGT;
299efe7daf2SSergei Shtylyov 		rh_a |= hub->potpgt << 24;
300f3c56fb3SAxel Haslam 	}
301efe7daf2SSergei Shtylyov 	ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
302efe7daf2SSergei Shtylyov 
303efe7daf2SSergei Shtylyov 	return result;
304efe7daf2SSergei Shtylyov }
305efe7daf2SSergei Shtylyov 
306efe7daf2SSergei Shtylyov /*
307efe7daf2SSergei Shtylyov  * Update the status data from the hub with the over-current indicator change.
308efe7daf2SSergei Shtylyov  */
309efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
310efe7daf2SSergei Shtylyov {
3116c21caa3SManjunath Goudar 	int length		= orig_ohci_hub_status_data(hcd, buf);
312efe7daf2SSergei Shtylyov 
313efe7daf2SSergei Shtylyov 	/* See if we have OCIC bit set on port 1 */
314efe7daf2SSergei Shtylyov 	if (ocic_mask & (1 << 1)) {
315efe7daf2SSergei Shtylyov 		dev_dbg(hcd->self.controller, "over-current indicator change "
316efe7daf2SSergei Shtylyov 			"on port 1\n");
317efe7daf2SSergei Shtylyov 
318efe7daf2SSergei Shtylyov 		if (!length)
319efe7daf2SSergei Shtylyov 			length = 1;
320efe7daf2SSergei Shtylyov 
321efe7daf2SSergei Shtylyov 		buf[0] |= 1 << 1;
322efe7daf2SSergei Shtylyov 	}
323efe7daf2SSergei Shtylyov 	return length;
324efe7daf2SSergei Shtylyov }
325efe7daf2SSergei Shtylyov 
326efe7daf2SSergei Shtylyov /*
327efe7daf2SSergei Shtylyov  * Look at the control requests to the root hub and see if we need to override.
328efe7daf2SSergei Shtylyov  */
329efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
330efe7daf2SSergei Shtylyov 				  u16 wIndex, char *buf, u16 wLength)
331efe7daf2SSergei Shtylyov {
332efe7daf2SSergei Shtylyov 	struct device *dev		= hcd->self.controller;
333efe7daf2SSergei Shtylyov 	int temp;
334efe7daf2SSergei Shtylyov 
335efe7daf2SSergei Shtylyov 	switch (typeReq) {
336efe7daf2SSergei Shtylyov 	case GetPortStatus:
337efe7daf2SSergei Shtylyov 		/* Check the port number */
338efe7daf2SSergei Shtylyov 		if (wIndex != 1)
339efe7daf2SSergei Shtylyov 			break;
340efe7daf2SSergei Shtylyov 
341efe7daf2SSergei Shtylyov 		dev_dbg(dev, "GetPortStatus(%u)\n", wIndex);
342efe7daf2SSergei Shtylyov 
343efe7daf2SSergei Shtylyov 		temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
344efe7daf2SSergei Shtylyov 
345efe7daf2SSergei Shtylyov 		/* The port power status (PPS) bit defaults to 1 */
346f3c56fb3SAxel Haslam 		if (!ohci_da8xx_get_power(hcd))
347efe7daf2SSergei Shtylyov 			temp &= ~RH_PS_PPS;
348efe7daf2SSergei Shtylyov 
349efe7daf2SSergei Shtylyov 		/* The port over-current indicator (POCI) bit is always 0 */
350f3c56fb3SAxel Haslam 		if (ohci_da8xx_get_oci(hcd) > 0)
351efe7daf2SSergei Shtylyov 			temp |=  RH_PS_POCI;
352efe7daf2SSergei Shtylyov 
353efe7daf2SSergei Shtylyov 		/* The over-current indicator change (OCIC) bit is 0 too */
354efe7daf2SSergei Shtylyov 		if (ocic_mask & (1 << wIndex))
355efe7daf2SSergei Shtylyov 			temp |=  RH_PS_OCIC;
356efe7daf2SSergei Shtylyov 
357efe7daf2SSergei Shtylyov 		put_unaligned(cpu_to_le32(temp), (__le32 *)buf);
358efe7daf2SSergei Shtylyov 		return 0;
359efe7daf2SSergei Shtylyov 	case SetPortFeature:
360efe7daf2SSergei Shtylyov 		temp = 1;
361efe7daf2SSergei Shtylyov 		goto check_port;
362efe7daf2SSergei Shtylyov 	case ClearPortFeature:
363efe7daf2SSergei Shtylyov 		temp = 0;
364efe7daf2SSergei Shtylyov 
365efe7daf2SSergei Shtylyov check_port:
366efe7daf2SSergei Shtylyov 		/* Check the port number */
367efe7daf2SSergei Shtylyov 		if (wIndex != 1)
368efe7daf2SSergei Shtylyov 			break;
369efe7daf2SSergei Shtylyov 
370efe7daf2SSergei Shtylyov 		switch (wValue) {
371efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_POWER:
372efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
373efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex, "POWER");
374efe7daf2SSergei Shtylyov 
375f3c56fb3SAxel Haslam 			return ohci_da8xx_set_power(hcd, temp) ? -EPIPE : 0;
376efe7daf2SSergei Shtylyov 		case USB_PORT_FEAT_C_OVER_CURRENT:
377efe7daf2SSergei Shtylyov 			dev_dbg(dev, "%sPortFeature(%u): %s\n",
378efe7daf2SSergei Shtylyov 				temp ? "Set" : "Clear", wIndex,
379efe7daf2SSergei Shtylyov 				"C_OVER_CURRENT");
380efe7daf2SSergei Shtylyov 
381efe7daf2SSergei Shtylyov 			if (temp)
382efe7daf2SSergei Shtylyov 				ocic_mask |= 1 << wIndex;
383efe7daf2SSergei Shtylyov 			else
384efe7daf2SSergei Shtylyov 				ocic_mask &= ~(1 << wIndex);
385efe7daf2SSergei Shtylyov 			return 0;
386efe7daf2SSergei Shtylyov 		}
387efe7daf2SSergei Shtylyov 	}
388efe7daf2SSergei Shtylyov 
3896c21caa3SManjunath Goudar 	return orig_ohci_hub_control(hcd, typeReq, wValue,
3906c21caa3SManjunath Goudar 			wIndex, buf, wLength);
391efe7daf2SSergei Shtylyov }
392efe7daf2SSergei Shtylyov 
393efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/
394190534f6SAxel Haslam #ifdef CONFIG_OF
395190534f6SAxel Haslam static const struct of_device_id da8xx_ohci_ids[] = {
396190534f6SAxel Haslam 	{ .compatible = "ti,da830-ohci" },
397190534f6SAxel Haslam 	{ }
398190534f6SAxel Haslam };
399190534f6SAxel Haslam MODULE_DEVICE_TABLE(of, da8xx_ohci_ids);
400190534f6SAxel Haslam #endif
401efe7daf2SSergei Shtylyov 
4026c21caa3SManjunath Goudar static int ohci_da8xx_probe(struct platform_device *pdev)
403efe7daf2SSergei Shtylyov {
404c7a4f9f3SAxel Haslam 	struct da8xx_ohci_hcd *da8xx_ohci;
405efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd;
406efe7daf2SSergei Shtylyov 	struct resource *mem;
407efe7daf2SSergei Shtylyov 	int error, irq;
408*08e46f18SBartosz Golaszewski 
4096c21caa3SManjunath Goudar 	hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev,
4106c21caa3SManjunath Goudar 				dev_name(&pdev->dev));
411644db166SJingoo Han 	if (!hcd)
412644db166SJingoo Han 		return -ENOMEM;
413efe7daf2SSergei Shtylyov 
414c7a4f9f3SAxel Haslam 	da8xx_ohci = to_da8xx_ohci(hcd);
415c844ff74SAxel Haslam 	da8xx_ohci->hcd = hcd;
416c7a4f9f3SAxel Haslam 
417e7f49360SDavid Lechner 	da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, NULL);
418c7a4f9f3SAxel Haslam 	if (IS_ERR(da8xx_ohci->usb11_clk)) {
419c7a4f9f3SAxel Haslam 		error = PTR_ERR(da8xx_ohci->usb11_clk);
420c7a4f9f3SAxel Haslam 		if (error != -EPROBE_DEFER)
421c7a4f9f3SAxel Haslam 			dev_err(&pdev->dev, "Failed to get clock.\n");
422c7a4f9f3SAxel Haslam 		goto err;
423c7a4f9f3SAxel Haslam 	}
424c7a4f9f3SAxel Haslam 
425c7a4f9f3SAxel Haslam 	da8xx_ohci->usb11_phy = devm_phy_get(&pdev->dev, "usb-phy");
426c7a4f9f3SAxel Haslam 	if (IS_ERR(da8xx_ohci->usb11_phy)) {
427c7a4f9f3SAxel Haslam 		error = PTR_ERR(da8xx_ohci->usb11_phy);
428c7a4f9f3SAxel Haslam 		if (error != -EPROBE_DEFER)
429c7a4f9f3SAxel Haslam 			dev_err(&pdev->dev, "Failed to get phy.\n");
430c7a4f9f3SAxel Haslam 		goto err;
431c7a4f9f3SAxel Haslam 	}
432c7a4f9f3SAxel Haslam 
433c844ff74SAxel Haslam 	da8xx_ohci->vbus_reg = devm_regulator_get_optional(&pdev->dev, "vbus");
434c844ff74SAxel Haslam 	if (IS_ERR(da8xx_ohci->vbus_reg)) {
435c844ff74SAxel Haslam 		error = PTR_ERR(da8xx_ohci->vbus_reg);
436c844ff74SAxel Haslam 		if (error == -ENODEV) {
437c844ff74SAxel Haslam 			da8xx_ohci->vbus_reg = NULL;
438c844ff74SAxel Haslam 		} else if (error == -EPROBE_DEFER) {
439c844ff74SAxel Haslam 			goto err;
440c844ff74SAxel Haslam 		} else {
441c844ff74SAxel Haslam 			dev_err(&pdev->dev, "Failed to get regulator\n");
442c844ff74SAxel Haslam 			goto err;
443c844ff74SAxel Haslam 		}
444c844ff74SAxel Haslam 	}
445c844ff74SAxel Haslam 
446efe7daf2SSergei Shtylyov 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
447644db166SJingoo Han 	hcd->regs = devm_ioremap_resource(&pdev->dev, mem);
448644db166SJingoo Han 	if (IS_ERR(hcd->regs)) {
449644db166SJingoo Han 		error = PTR_ERR(hcd->regs);
450644db166SJingoo Han 		goto err;
451efe7daf2SSergei Shtylyov 	}
45254891d74SVarka Bhadram 	hcd->rsrc_start = mem->start;
45354891d74SVarka Bhadram 	hcd->rsrc_len = resource_size(mem);
454efe7daf2SSergei Shtylyov 
455efe7daf2SSergei Shtylyov 	irq = platform_get_irq(pdev, 0);
456efe7daf2SSergei Shtylyov 	if (irq < 0) {
457efe7daf2SSergei Shtylyov 		error = -ENODEV;
458644db166SJingoo Han 		goto err;
459efe7daf2SSergei Shtylyov 	}
4606c21caa3SManjunath Goudar 
461b5dd18d8SYong Zhang 	error = usb_add_hcd(hcd, irq, 0);
462efe7daf2SSergei Shtylyov 	if (error)
463644db166SJingoo Han 		goto err;
464efe7daf2SSergei Shtylyov 
4653c9740a1SPeter Chen 	device_wakeup_enable(hcd->self.controller);
4663c9740a1SPeter Chen 
467f3c56fb3SAxel Haslam 	error = ohci_da8xx_register_notify(hcd);
468f3c56fb3SAxel Haslam 	if (error)
469f3c56fb3SAxel Haslam 		goto err_remove_hcd;
470efe7daf2SSergei Shtylyov 
471f3c56fb3SAxel Haslam 	return 0;
472f3c56fb3SAxel Haslam 
473f3c56fb3SAxel Haslam err_remove_hcd:
474efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
475644db166SJingoo Han err:
476efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
477efe7daf2SSergei Shtylyov 	return error;
478efe7daf2SSergei Shtylyov }
479efe7daf2SSergei Shtylyov 
4806c21caa3SManjunath Goudar static int ohci_da8xx_remove(struct platform_device *pdev)
481efe7daf2SSergei Shtylyov {
4826c21caa3SManjunath Goudar 	struct usb_hcd	*hcd = platform_get_drvdata(pdev);
483efe7daf2SSergei Shtylyov 
484f3c56fb3SAxel Haslam 	ohci_da8xx_unregister_notify(hcd);
485efe7daf2SSergei Shtylyov 	usb_remove_hcd(hcd);
486efe7daf2SSergei Shtylyov 	usb_put_hcd(hcd);
487efe7daf2SSergei Shtylyov 
488efe7daf2SSergei Shtylyov 	return 0;
489efe7daf2SSergei Shtylyov }
490efe7daf2SSergei Shtylyov 
491efe7daf2SSergei Shtylyov #ifdef CONFIG_PM
492933bb1f0SMajunath Goudar static int ohci_da8xx_suspend(struct platform_device *pdev,
493933bb1f0SMajunath Goudar 				pm_message_t message)
494efe7daf2SSergei Shtylyov {
495933bb1f0SMajunath Goudar 	struct usb_hcd	*hcd	= platform_get_drvdata(pdev);
496efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
497933bb1f0SMajunath Goudar 	bool		do_wakeup	= device_may_wakeup(&pdev->dev);
498933bb1f0SMajunath Goudar 	int		ret;
499933bb1f0SMajunath Goudar 
500efe7daf2SSergei Shtylyov 
501efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
502efe7daf2SSergei Shtylyov 		msleep(5);
503efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
504efe7daf2SSergei Shtylyov 
505933bb1f0SMajunath Goudar 	ret = ohci_suspend(hcd, do_wakeup);
506933bb1f0SMajunath Goudar 	if (ret)
507933bb1f0SMajunath Goudar 		return ret;
508933bb1f0SMajunath Goudar 
509c7a4f9f3SAxel Haslam 	ohci_da8xx_disable(hcd);
510efe7daf2SSergei Shtylyov 	hcd->state = HC_STATE_SUSPENDED;
511933bb1f0SMajunath Goudar 
512933bb1f0SMajunath Goudar 	return ret;
513efe7daf2SSergei Shtylyov }
514efe7daf2SSergei Shtylyov 
515efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev)
516efe7daf2SSergei Shtylyov {
517efe7daf2SSergei Shtylyov 	struct usb_hcd	*hcd	= platform_get_drvdata(dev);
518efe7daf2SSergei Shtylyov 	struct ohci_hcd	*ohci	= hcd_to_ohci(hcd);
5196110c425SDavid Lechner 	int ret;
520efe7daf2SSergei Shtylyov 
521efe7daf2SSergei Shtylyov 	if (time_before(jiffies, ohci->next_statechange))
522efe7daf2SSergei Shtylyov 		msleep(5);
523efe7daf2SSergei Shtylyov 	ohci->next_statechange = jiffies;
524efe7daf2SSergei Shtylyov 
525c7a4f9f3SAxel Haslam 	ret = ohci_da8xx_enable(hcd);
5266110c425SDavid Lechner 	if (ret)
5276110c425SDavid Lechner 		return ret;
5286110c425SDavid Lechner 
529640308b7SAxel Haslam 	ohci_resume(hcd, false);
5306110c425SDavid Lechner 
531efe7daf2SSergei Shtylyov 	return 0;
532efe7daf2SSergei Shtylyov }
533efe7daf2SSergei Shtylyov #endif
534efe7daf2SSergei Shtylyov 
5356c21caa3SManjunath Goudar static const struct ohci_driver_overrides da8xx_overrides __initconst = {
5366c21caa3SManjunath Goudar 	.reset		 = ohci_da8xx_reset,
537c7a4f9f3SAxel Haslam 	.extra_priv_size = sizeof(struct da8xx_ohci_hcd),
5386c21caa3SManjunath Goudar };
5396c21caa3SManjunath Goudar 
540efe7daf2SSergei Shtylyov /*
541efe7daf2SSergei Shtylyov  * Driver definition to register with platform structure.
542efe7daf2SSergei Shtylyov  */
543efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = {
5446c21caa3SManjunath Goudar 	.probe		= ohci_da8xx_probe,
5456c21caa3SManjunath Goudar 	.remove		= ohci_da8xx_remove,
546efe7daf2SSergei Shtylyov 	.shutdown 	= usb_hcd_platform_shutdown,
547efe7daf2SSergei Shtylyov #ifdef	CONFIG_PM
548efe7daf2SSergei Shtylyov 	.suspend	= ohci_da8xx_suspend,
549efe7daf2SSergei Shtylyov 	.resume		= ohci_da8xx_resume,
550efe7daf2SSergei Shtylyov #endif
551efe7daf2SSergei Shtylyov 	.driver		= {
5526c21caa3SManjunath Goudar 		.name	= DRV_NAME,
553190534f6SAxel Haslam 		.of_match_table = of_match_ptr(da8xx_ohci_ids),
554efe7daf2SSergei Shtylyov 	},
555efe7daf2SSergei Shtylyov };
556ab59ac01SJan Luebbe 
5576c21caa3SManjunath Goudar static int __init ohci_da8xx_init(void)
5586c21caa3SManjunath Goudar {
5596c21caa3SManjunath Goudar 
5606c21caa3SManjunath Goudar 	if (usb_disabled())
5616c21caa3SManjunath Goudar 		return -ENODEV;
5626c21caa3SManjunath Goudar 
5636c21caa3SManjunath Goudar 	pr_info("%s: " DRIVER_DESC "\n", DRV_NAME);
5646c21caa3SManjunath Goudar 	ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides);
5656c21caa3SManjunath Goudar 
5666c21caa3SManjunath Goudar 	/*
5676c21caa3SManjunath Goudar 	 * The Davinci da8xx HW has some unusual quirks, which require
5686c21caa3SManjunath Goudar 	 * da8xx-specific workarounds. We override certain hc_driver
5696c21caa3SManjunath Goudar 	 * functions here to achieve that. We explicitly do not enhance
5706c21caa3SManjunath Goudar 	 * ohci_driver_overrides to allow this more easily, since this
5716c21caa3SManjunath Goudar 	 * is an unusual case, and we don't want to encourage others to
5726c21caa3SManjunath Goudar 	 * override these functions by making it too easy.
5736c21caa3SManjunath Goudar 	 */
5746c21caa3SManjunath Goudar 
5756c21caa3SManjunath Goudar 	orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control;
5766c21caa3SManjunath Goudar 	orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data;
5776c21caa3SManjunath Goudar 
5786c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_status_data     = ohci_da8xx_hub_status_data;
5796c21caa3SManjunath Goudar 	ohci_da8xx_hc_driver.hub_control         = ohci_da8xx_hub_control;
5806c21caa3SManjunath Goudar 
5816c21caa3SManjunath Goudar 	return platform_driver_register(&ohci_hcd_da8xx_driver);
5826c21caa3SManjunath Goudar }
5836c21caa3SManjunath Goudar module_init(ohci_da8xx_init);
5846c21caa3SManjunath Goudar 
5856c21caa3SManjunath Goudar static void __exit ohci_da8xx_exit(void)
5866c21caa3SManjunath Goudar {
5876c21caa3SManjunath Goudar 	platform_driver_unregister(&ohci_hcd_da8xx_driver);
5886c21caa3SManjunath Goudar }
5896c21caa3SManjunath Goudar module_exit(ohci_da8xx_exit);
5906c21caa3SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC);
5916c21caa3SManjunath Goudar MODULE_LICENSE("GPL");
5926c21caa3SManjunath Goudar MODULE_ALIAS("platform:" DRV_NAME);
593