1 From 4365ef4ae6c7c08950ac34c47f7beaece2dc48ea Mon Sep 17 00:00:00 2001
2 From: Lars-Peter Clausen <lars@metafoo.de>
3 Date: Sun, 1 Aug 2010 21:13:26 +0200
4 Subject: [PATCH] USB: Add JZ4740 OHCI support
6 Add OHCI glue code for JZ4740 SoCs OHCI module.
8 Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
9 Cc: Greg Kroah-Hartman <gregkh@suse.de>
10 Cc: David Brownell <dbrownell@users.sourceforge.net>
11 Cc: linux-usb@vger.kernel.org
12 Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
13 Cc: linux-mips@linux-mips.org
14 Cc: linux-kernel@vger.kernel.org
15 Patchwork: https://patchwork.linux-mips.org/patch/1411/
16 Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
18 drivers/usb/Kconfig | 1 +
19 drivers/usb/host/ohci-hcd.c | 5 +
20 drivers/usb/host/ohci-jz4740.c | 276 ++++++++++++++++++++++++++++++++++++++++
21 3 files changed, 282 insertions(+), 0 deletions(-)
22 create mode 100644 drivers/usb/host/ohci-jz4740.c
24 --- a/drivers/usb/Kconfig
25 +++ b/drivers/usb/Kconfig
26 @@ -46,6 +46,7 @@ config USB_ARCH_HAS_OHCI
27 default y if PPC_MPC52xx
29 default y if SOC_AU1X00
30 + default y if MACH_JZ4740
32 default y if CPU_SUBTYPE_SH7720
33 default y if CPU_SUBTYPE_SH7721
34 --- a/drivers/usb/host/ohci-hcd.c
35 +++ b/drivers/usb/host/ohci-hcd.c
36 @@ -1095,6 +1095,11 @@ MODULE_LICENSE ("GPL");
37 #define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver
40 +#ifdef CONFIG_MACH_JZ4740
41 +#include "ohci-jz4740.c"
42 +#define PLATFORM_DRIVER ohci_hcd_jz4740_driver
45 #if !defined(PCI_DRIVER) && \
46 !defined(PLATFORM_DRIVER) && \
47 !defined(OMAP1_PLATFORM_DRIVER) && \
49 +++ b/drivers/usb/host/ohci-jz4740.c
52 + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
54 + * This program is free software; you can redistribute it and/or modify it
55 + * under the terms of the GNU General Public License as published by the
56 + * Free Software Foundation; either version 2 of the License, or (at your
57 + * option) any later version.
59 + * You should have received a copy of the GNU General Public License along
60 + * with this program; if not, write to the Free Software Foundation, Inc.,
61 + * 675 Mass Ave, Cambridge, MA 02139, USA.
65 +#include <linux/platform_device.h>
66 +#include <linux/clk.h>
67 +#include <linux/regulator/consumer.h>
69 +struct jz4740_ohci_hcd {
70 + struct ohci_hcd ohci_hcd;
72 + struct regulator *vbus;
77 +static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
79 + return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
82 +static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
84 + return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
87 +static int ohci_jz4740_start(struct usb_hcd *hcd)
89 + struct ohci_hcd *ohci = hcd_to_ohci(hcd);
92 + ret = ohci_init(ohci);
96 + ohci->num_ports = 1;
98 + ret = ohci_run(ohci);
100 + dev_err(hcd->self.controller, "Can not start %s",
101 + hcd->self.bus_name);
108 +static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
113 + if (!jz4740_ohci->vbus)
116 + if (enabled && !jz4740_ohci->vbus_enabled) {
117 + ret = regulator_enable(jz4740_ohci->vbus);
119 + dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
120 + "Could not power vbus\n");
121 + } else if (!enabled && jz4740_ohci->vbus_enabled) {
122 + ret = regulator_disable(jz4740_ohci->vbus);
126 + jz4740_ohci->vbus_enabled = enabled;
131 +static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
132 + u16 wIndex, char *buf, u16 wLength)
134 + struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
138 + case SetHubFeature:
139 + if (wValue == USB_PORT_FEAT_POWER)
140 + ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
142 + case ClearHubFeature:
143 + if (wValue == USB_PORT_FEAT_POWER)
144 + ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
151 + return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
155 +static const struct hc_driver ohci_jz4740_hc_driver = {
156 + .description = hcd_name,
157 + .product_desc = "JZ4740 OHCI",
158 + .hcd_priv_size = sizeof(struct jz4740_ohci_hcd),
161 + * generic hardware linkage
164 + .flags = HCD_USB11 | HCD_MEMORY,
167 + * basic lifecycle operations
169 + .start = ohci_jz4740_start,
171 + .shutdown = ohci_shutdown,
174 + * managing i/o requests and associated device resources
176 + .urb_enqueue = ohci_urb_enqueue,
177 + .urb_dequeue = ohci_urb_dequeue,
178 + .endpoint_disable = ohci_endpoint_disable,
181 + * scheduling support
183 + .get_frame_number = ohci_get_frame,
188 + .hub_status_data = ohci_hub_status_data,
189 + .hub_control = ohci_jz4740_hub_control,
191 + .bus_suspend = ohci_bus_suspend,
192 + .bus_resume = ohci_bus_resume,
194 + .start_port_reset = ohci_start_port_reset,
198 +static __devinit int jz4740_ohci_probe(struct platform_device *pdev)
201 + struct usb_hcd *hcd;
202 + struct jz4740_ohci_hcd *jz4740_ohci;
203 + struct resource *res;
206 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
209 + dev_err(&pdev->dev, "Failed to get platform resource\n");
213 + irq = platform_get_irq(pdev, 0);
215 + dev_err(&pdev->dev, "Failed to get platform irq\n");
219 + hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
221 + dev_err(&pdev->dev, "Failed to create hcd.\n");
225 + jz4740_ohci = hcd_to_jz4740_hcd(hcd);
227 + res = request_mem_region(res->start, resource_size(res), hcd_name);
229 + dev_err(&pdev->dev, "Failed to request mem region.\n");
234 + hcd->rsrc_start = res->start;
235 + hcd->rsrc_len = resource_size(res);
236 + hcd->regs = ioremap(res->start, resource_size(res));
239 + dev_err(&pdev->dev, "Failed to ioremap registers.\n");
241 + goto err_release_mem;
244 + jz4740_ohci->clk = clk_get(&pdev->dev, "uhc");
245 + if (IS_ERR(jz4740_ohci->clk)) {
246 + ret = PTR_ERR(jz4740_ohci->clk);
247 + dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
251 + jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus");
252 + if (IS_ERR(jz4740_ohci->vbus))
253 + jz4740_ohci->vbus = NULL;
256 + clk_set_rate(jz4740_ohci->clk, 48000000);
257 + clk_enable(jz4740_ohci->clk);
258 + if (jz4740_ohci->vbus)
259 + ohci_jz4740_set_vbus_power(jz4740_ohci, true);
261 + platform_set_drvdata(pdev, hcd);
263 + ohci_hcd_init(hcd_to_ohci(hcd));
265 + ret = usb_add_hcd(hcd, irq, 0);
267 + dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
274 + platform_set_drvdata(pdev, NULL);
275 + if (jz4740_ohci->vbus) {
276 + regulator_disable(jz4740_ohci->vbus);
277 + regulator_put(jz4740_ohci->vbus);
279 + clk_disable(jz4740_ohci->clk);
281 + clk_put(jz4740_ohci->clk);
283 + iounmap(hcd->regs);
285 + release_mem_region(res->start, resource_size(res));
292 +static __devexit int jz4740_ohci_remove(struct platform_device *pdev)
294 + struct usb_hcd *hcd = platform_get_drvdata(pdev);
295 + struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
297 + usb_remove_hcd(hcd);
299 + platform_set_drvdata(pdev, NULL);
301 + if (jz4740_ohci->vbus) {
302 + regulator_disable(jz4740_ohci->vbus);
303 + regulator_put(jz4740_ohci->vbus);
306 + clk_disable(jz4740_ohci->clk);
307 + clk_put(jz4740_ohci->clk);
309 + iounmap(hcd->regs);
310 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
317 +static struct platform_driver ohci_hcd_jz4740_driver = {
318 + .probe = jz4740_ohci_probe,
319 + .remove = __devexit_p(jz4740_ohci_remove),
321 + .name = "jz4740-ohci",
322 + .owner = THIS_MODULE,
326 +MODULE_ALIAS("platfrom:jz4740-ohci");