aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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
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')
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/host/ehci-atmel.c230
-rw-r--r--drivers/usb/host/ehci-hcd.c5
3 files changed, 236 insertions, 0 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 93cee3e5ca58..ebd7237230e3 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -60,6 +60,7 @@ config USB_ARCH_HAS_EHCI
60 default y if SOC_AU1200 60 default y if SOC_AU1200
61 default y if ARCH_IXP4XX 61 default y if ARCH_IXP4XX
62 default y if ARCH_W90X900 62 default y if ARCH_W90X900
63 default y if ARCH_AT91SAM9G45
63 default PCI 64 default PCI
64 65
65# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. 66# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
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};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 5fbc67c5a913..c227ba46d6f3 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1136,6 +1136,11 @@ MODULE_LICENSE ("GPL");
1136#define PLATFORM_DRIVER ehci_hcd_w90x900_driver 1136#define PLATFORM_DRIVER ehci_hcd_w90x900_driver
1137#endif 1137#endif
1138 1138
1139#ifdef CONFIG_ARCH_AT91
1140#include "ehci-atmel.c"
1141#define PLATFORM_DRIVER ehci_atmel_driver
1142#endif
1143
1139#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ 1144#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
1140 !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) 1145 !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER)
1141#error "missing bus glue for ehci-hcd" 1146#error "missing bus glue for ehci-hcd"