1/*
2 *  Bus Glue for the built-in EHCI 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_ehci_platform.h>
16
17static int ehci_rt3883_init(struct usb_hcd *hcd)
18{
19	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
20	int ret;
21
22	ehci->caps = hcd->regs;
23	ehci->regs = hcd->regs +
24		     HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
25	dbg_hcs_params(ehci, "reset");
26	dbg_hcc_params(ehci, "reset");
27
28	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
29	ehci->sbrn = 0x20;
30
31	ehci_reset(ehci);
32
33	ret = ehci_init(hcd);
34	if (ret)
35		return ret;
36
37	ehci_port_power(ehci, 0);
38
39	return 0;
40}
41
42static const struct hc_driver ehci_rt3883_hc_driver = {
43	.description		= hcd_name,
44	.product_desc		= "Ralink RT3883 built-in EHCI controller",
45	.hcd_priv_size		= sizeof(struct ehci_hcd),
46	.irq			= ehci_irq,
47	.flags			= HCD_MEMORY | HCD_USB2,
48
49	.reset			= ehci_rt3883_init,
50	.start			= ehci_run,
51	.stop			= ehci_stop,
52	.shutdown		= ehci_shutdown,
53
54	.urb_enqueue		= ehci_urb_enqueue,
55	.urb_dequeue		= ehci_urb_dequeue,
56	.endpoint_disable	= ehci_endpoint_disable,
57	.endpoint_reset		= ehci_endpoint_reset,
58
59	.get_frame_number	= ehci_get_frame,
60
61	.hub_status_data	= ehci_hub_status_data,
62	.hub_control		= ehci_hub_control,
63	.relinquish_port	= ehci_relinquish_port,
64	.port_handed_over	= ehci_port_handed_over,
65
66	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
67};
68
69static int ehci_rt3883_probe(struct platform_device *pdev)
70{
71	struct rt3883_ehci_platform_data *pdata;
72	struct usb_hcd *hcd;
73	struct resource *res;
74	int irq;
75	int ret;
76
77	if (usb_disabled())
78		return -ENODEV;
79
80	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
81	if (!res) {
82		dev_dbg(&pdev->dev, "no IRQ specified for %s\n",
83			dev_name(&pdev->dev));
84		return -ENODEV;
85	}
86	irq = res->start;
87
88	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
89	if (!res) {
90		dev_dbg(&pdev->dev, "no base address specified for %s\n",
91			dev_name(&pdev->dev));
92		return -ENODEV;
93	}
94
95	hcd = usb_create_hcd(&ehci_rt3883_hc_driver, &pdev->dev,
96			     dev_name(&pdev->dev));
97	if (!hcd)
98		return -ENOMEM;
99
100	hcd->rsrc_start	= res->start;
101	hcd->rsrc_len	= res->end - res->start + 1;
102
103	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
104		dev_dbg(&pdev->dev, "controller already in use\n");
105		ret = -EBUSY;
106		goto err_put_hcd;
107	}
108
109	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
110	if (!hcd->regs) {
111		dev_dbg(&pdev->dev, "error mapping memory\n");
112		ret = -EFAULT;
113		goto err_release_region;
114	}
115
116	pdata = pdev->dev.platform_data;
117	if (pdata && pdata->start_hw)
118		pdata->start_hw();
119
120	ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
121	if (ret)
122		goto err_iounmap;
123
124	return 0;
125
126err_iounmap:
127	iounmap(hcd->regs);
128
129err_release_region:
130	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
131err_put_hcd:
132	usb_put_hcd(hcd);
133	return ret;
134}
135
136static int ehci_rt3883_remove(struct platform_device *pdev)
137{
138	struct usb_hcd *hcd = platform_get_drvdata(pdev);
139	struct rt3883_ehci_platform_data *pdata;
140
141	usb_remove_hcd(hcd);
142	iounmap(hcd->regs);
143	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
144	usb_put_hcd(hcd);
145
146	pdata = pdev->dev.platform_data;
147	if (pdata && pdata->stop_hw)
148		pdata->stop_hw();
149
150	return 0;
151}
152
153static struct platform_driver ehci_rt3883_driver = {
154	.probe		= ehci_rt3883_probe,
155	.remove		= ehci_rt3883_remove,
156	.driver = {
157		.owner	= THIS_MODULE,
158		.name	= "rt3883-ehci",
159	}
160};
161
162MODULE_ALIAS("platform:rt3883-ehci");
163