aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-atmel.c
diff options
context:
space:
mode:
authorNicolas Ferre <nicolas.ferre@atmel.com>2009-07-27 17:47:40 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-09-23 09:46:30 -0400
commit501c9c0802d9fee05efb300de06c8b3d04f17458 (patch)
tree2e4af8a74eb68defe90e359aaea1813aaae27c34 /drivers/usb/host/ehci-atmel.c
parentc0ad7291aae3f76920bdddbc517e20b8d4338ec2 (diff)
USB: at91: Add USB EHCI driver for at91sam9g45 series
Add host USB High speed driver for at91sam9g45 series. The host driver is an EHCI with its companion OHCI. EHCI is handled by the new ehci-atmel.c whereas the OHCI is always handled by ohci-at91.c. Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Diffstat (limited to 'drivers/usb/host/ehci-atmel.c')
-rw-r--r--drivers/usb/host/ehci-atmel.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
new file mode 100644
index 000000000000..87c1b7c34c0e
--- /dev/null
+++ b/drivers/usb/host/ehci-atmel.c
@@ -0,0 +1,230 @@
1/*
2 * Driver for EHCI UHP on Atmel chips
3 *
4 * Copyright (C) 2009 Atmel Corporation,
5 * Nicolas Ferre <nicolas.ferre@atmel.com>
6 *
7 * Based on various ehci-*.c drivers
8 *
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file COPYING in the main directory of this archive for
11 * more details.
12 */
13
14#include <linux/clk.h>
15#include <linux/platform_device.h>
16
17/* interface and function clocks */
18static struct clk *iclk, *fclk;
19static int clocked;
20
21/*-------------------------------------------------------------------------*/
22
23static void atmel_start_clock(void)
24{
25 clk_enable(iclk);
26 clk_enable(fclk);
27 clocked = 1;
28}
29
30static void atmel_stop_clock(void)
31{
32 clk_disable(fclk);
33 clk_disable(iclk);
34 clocked = 0;
35}
36
37static void atmel_start_ehci(struct platform_device *pdev)
38{
39 dev_dbg(&pdev->dev, "start\n");
40 atmel_start_clock();
41}
42
43static void atmel_stop_ehci(struct platform_device *pdev)
44{
45 dev_dbg(&pdev->dev, "stop\n");
46 atmel_stop_clock();
47}
48
49/*-------------------------------------------------------------------------*/
50
51static int ehci_atmel_setup(struct usb_hcd *hcd)
52{
53 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
54 int retval = 0;
55
56 /* registers start at offset 0x0 */
57 ehci->caps = hcd->regs;
58 ehci->regs = hcd->regs +
59 HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
60 dbg_hcs_params(ehci, "reset");
61 dbg_hcc_params(ehci, "reset");
62
63 /* cache this readonly data; minimize chip reads */
64 ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
65
66 retval = ehci_halt(ehci);
67 if (retval)
68 return retval;
69
70 /* data structure init */
71 retval = ehci_init(hcd);
72 if (retval)
73 return retval;
74
75 ehci->sbrn = 0x20;
76
77 ehci_reset(ehci);
78 ehci_port_power(ehci, 0);
79
80 return retval;
81}
82
83static const struct hc_driver ehci_atmel_hc_driver = {
84 .description = hcd_name,
85 .product_desc = "Atmel EHCI UHP HS",
86 .hcd_priv_size = sizeof(struct ehci_hcd),
87
88 /* generic hardware linkage */
89 .irq = ehci_irq,
90 .flags = HCD_MEMORY | HCD_USB2,
91
92 /* basic lifecycle operations */
93 .reset = ehci_atmel_setup,
94 .start = ehci_run,
95 .stop = ehci_stop,
96 .shutdown = ehci_shutdown,
97
98 /* managing i/o requests and associated device resources */
99 .urb_enqueue = ehci_urb_enqueue,
100 .urb_dequeue = ehci_urb_dequeue,
101 .endpoint_disable = ehci_endpoint_disable,
102
103 /* scheduling support */
104 .get_frame_number = ehci_get_frame,
105
106 /* root hub support */
107 .hub_status_data = ehci_hub_status_data,
108 .hub_control = ehci_hub_control,
109 .bus_suspend = ehci_bus_suspend,
110 .bus_resume = ehci_bus_resume,
111 .relinquish_port = ehci_relinquish_port,
112 .port_handed_over = ehci_port_handed_over,
113};
114
115static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
116{
117 struct usb_hcd *hcd;
118 const struct hc_driver *driver = &ehci_atmel_hc_driver;
119 struct resource *res;
120 int irq;
121 int retval;
122
123 if (usb_disabled())
124 return -ENODEV;
125
126 pr_debug("Initializing Atmel-SoC USB Host Controller\n");
127
128 irq = platform_get_irq(pdev, 0);
129 if (irq <= 0) {
130 dev_err(&pdev->dev,
131 "Found HC with no IRQ. Check %s setup!\n",
132 dev_name(&pdev->dev));
133 retval = -ENODEV;
134 goto fail_create_hcd;
135 }
136
137 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
138 if (!hcd) {
139 retval = -ENOMEM;
140 goto fail_create_hcd;
141 }
142
143 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
144 if (!res) {
145 dev_err(&pdev->dev,
146 "Found HC with no register addr. Check %s setup!\n",
147 dev_name(&pdev->dev));
148 retval = -ENODEV;
149 goto fail_request_resource;
150 }
151 hcd->rsrc_start = res->start;
152 hcd->rsrc_len = res->end - res->start + 1;
153
154 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
155 driver->description)) {
156 dev_dbg(&pdev->dev, "controller already in use\n");
157 retval = -EBUSY;
158 goto fail_request_resource;
159 }
160
161 hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
162 if (hcd->regs == NULL) {
163 dev_dbg(&pdev->dev, "error mapping memory\n");
164 retval = -EFAULT;
165 goto fail_ioremap;
166 }
167
168 iclk = clk_get(&pdev->dev, "ehci_clk");
169 if (IS_ERR(iclk)) {
170 dev_err(&pdev->dev, "Error getting interface clock\n");
171 retval = -ENOENT;
172 goto fail_get_iclk;
173 }
174 fclk = clk_get(&pdev->dev, "uhpck");
175 if (IS_ERR(fclk)) {
176 dev_err(&pdev->dev, "Error getting function clock\n");
177 retval = -ENOENT;
178 goto fail_get_fclk;
179 }
180
181 atmel_start_ehci(pdev);
182
183 retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
184 if (retval)
185 goto fail_add_hcd;
186
187 return retval;
188
189fail_add_hcd:
190 atmel_stop_ehci(pdev);
191 clk_put(fclk);
192fail_get_fclk:
193 clk_put(iclk);
194fail_get_iclk:
195 iounmap(hcd->regs);
196fail_ioremap:
197 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
198fail_request_resource:
199 usb_put_hcd(hcd);
200fail_create_hcd:
201 dev_err(&pdev->dev, "init %s fail, %d\n",
202 dev_name(&pdev->dev), retval);
203
204 return retval;
205}
206
207static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
208{
209 struct usb_hcd *hcd = platform_get_drvdata(pdev);
210
211 ehci_shutdown(hcd);
212 usb_remove_hcd(hcd);
213 iounmap(hcd->regs);
214 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
215 usb_put_hcd(hcd);
216
217 atmel_stop_ehci(pdev);
218 clk_put(fclk);
219 clk_put(iclk);
220 fclk = iclk = NULL;
221
222 return 0;
223}
224
225static struct platform_driver ehci_atmel_driver = {
226 .probe = ehci_atmel_drv_probe,
227 .remove = __exit_p(ehci_atmel_drv_remove),
228 .shutdown = usb_hcd_platform_shutdown,
229 .driver.name = "atmel-ehci",
230};