1/*
2 *  Bus Glue for the built-in OHCI controller of the Ralink RT3662/RT3883 SoCs
3 *
4 *  Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
5 *
6 *  Parts of this file are based on Ralink's 2.6.21 BSP
7 *
8 *  This program is free software; you can redistribute it and/or modify it
9 *  under the terms of the GNU General Public License version 2 as published
10 *  by the Free Software Foundation.
11 */
12
13#include <linux/platform_device.h>
14#include <asm/mach-ralink/rt3883.h>
15#include <asm/mach-ralink/rt3883_ohci_platform.h>
16
17static int __devinit ohci_rt3883_start(struct usb_hcd *hcd)
18{
19	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
20	int ret;
21
22	ret = ohci_init(ohci);
23	if (ret < 0)
24		return ret;
25
26	ret = ohci_run(ohci);
27	if (ret < 0)
28		goto err;
29
30	return 0;
31
32err:
33	ohci_stop(hcd);
34	return ret;
35}
36
37static const struct hc_driver ohci_rt3883_hc_driver = {
38	.description		= hcd_name,
39	.product_desc		= "Ralink RT3883 built-in OHCI controller",
40	.hcd_priv_size		= sizeof(struct ohci_hcd),
41
42	.irq			= ohci_irq,
43	.flags			= HCD_USB11 | HCD_MEMORY,
44
45	.start			= ohci_rt3883_start,
46	.stop			= ohci_stop,
47	.shutdown		= ohci_shutdown,
48
49	.urb_enqueue		= ohci_urb_enqueue,
50	.urb_dequeue		= ohci_urb_dequeue,
51	.endpoint_disable	= ohci_endpoint_disable,
52
53	/*
54	 * scheduling support
55	 */
56	.get_frame_number	= ohci_get_frame,
57
58	/*
59	 * root hub support
60	 */
61	.hub_status_data	= ohci_hub_status_data,
62	.hub_control		= ohci_hub_control,
63	.start_port_reset	= ohci_start_port_reset,
64};
65
66static int ohci_rt3883_probe(struct platform_device *pdev)
67{
68	struct rt3883_ohci_platform_data *pdata;
69	struct usb_hcd *hcd;
70	struct resource *res;
71	int irq;
72	int ret;
73
74	if (usb_disabled())
75		return -ENODEV;
76
77	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
78	if (!res) {
79		dev_dbg(&pdev->dev, "no IRQ specified for %s\n",
80			dev_name(&pdev->dev));
81		return -ENODEV;
82	}
83	irq = res->start;
84
85	hcd = usb_create_hcd(&ohci_rt3883_hc_driver,
86			     &pdev->dev, dev_name(&pdev->dev));
87	if (!hcd)
88		return -ENOMEM;
89
90	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
91	if (!res) {
92		dev_dbg(&pdev->dev, "no base address specified for %s\n",
93			dev_name(&pdev->dev));
94		ret = -ENODEV;
95		goto err_put_hcd;
96	}
97	hcd->rsrc_start	= res->start;
98	hcd->rsrc_len	= res->end - res->start + 1;
99
100	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
101		dev_dbg(&pdev->dev, "controller already in use\n");
102		ret = -EBUSY;
103		goto err_put_hcd;
104	}
105
106	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
107	if (!hcd->regs) {
108		dev_dbg(&pdev->dev, "error mapping memory\n");
109		ret = -EFAULT;
110		goto err_release_region;
111	}
112
113	pdata = pdev->dev.platform_data;
114	if (pdata && pdata->start_hw)
115		pdata->start_hw();
116
117	ohci_hcd_init(hcd_to_ohci(hcd));
118
119	ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
120	if (ret)
121		goto err_stop_hcd;
122
123	return 0;
124
125err_stop_hcd:
126	iounmap(hcd->regs);
127err_release_region:
128	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
129err_put_hcd:
130	usb_put_hcd(hcd);
131	return ret;
132}
133
134static int ohci_rt3883_remove(struct platform_device *pdev)
135{
136	struct usb_hcd *hcd = platform_get_drvdata(pdev);
137	struct rt3883_ohci_platform_data *pdata;
138
139	usb_remove_hcd(hcd);
140	iounmap(hcd->regs);
141	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
142	usb_put_hcd(hcd);
143
144	pdata = pdev->dev.platform_data;
145	if (pdata && pdata->stop_hw)
146		pdata->stop_hw();
147
148	return 0;
149}
150
151static struct platform_driver ohci_rt3883_driver = {
152	.probe		= ohci_rt3883_probe,
153	.remove		= ohci_rt3883_remove,
154	.shutdown	= usb_hcd_platform_shutdown,
155	.driver		= {
156		.name	= "rt3883-ohci",
157		.owner	= THIS_MODULE,
158	},
159};
160
161MODULE_ALIAS("platform:rt3883-ohci");
162