blob: 6b7350b52419fc57e925081e21ea9757b6e33ef5 [file] [log] [blame]
Andrew Victor39a269c2006-01-22 10:32:13 -08001/*
2 * OHCI HCD (Host Controller Driver) for USB.
3 *
4 * Copyright (C) 2004 SAN People (Pty) Ltd.
5 * Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.org>
6 *
7 * AT91RM9200 Bus Glue
8 *
9 * Based on fragments of 2.4 driver by Rick Bronson.
10 * Based on ohci-omap.c
11 *
12 * This file is licenced under the GPL.
13 */
14
15#include <linux/clk.h>
16#include <linux/platform_device.h>
17
18#include <asm/mach-types.h>
19#include <asm/hardware.h>
20#include <asm/arch/board.h>
21
22#ifndef CONFIG_ARCH_AT91RM9200
David Brownell68ba61b2006-04-02 20:26:21 -080023#error "CONFIG_ARCH_AT91RM9200 must be defined."
Andrew Victor39a269c2006-01-22 10:32:13 -080024#endif
25
26/* interface and function clocks */
27static struct clk *iclk, *fclk;
28
29extern int usb_disabled(void);
30
31/*-------------------------------------------------------------------------*/
32
33static void at91_start_hc(struct platform_device *pdev)
34{
35 struct usb_hcd *hcd = platform_get_drvdata(pdev);
36 struct ohci_regs __iomem *regs = hcd->regs;
37
38 dev_dbg(&pdev->dev, "starting AT91RM9200 OHCI USB Controller\n");
39
40 /*
41 * Start the USB clocks.
42 */
43 clk_enable(iclk);
44 clk_enable(fclk);
45
46 /*
47 * The USB host controller must remain in reset.
48 */
49 writel(0, &regs->control);
50}
51
52static void at91_stop_hc(struct platform_device *pdev)
53{
54 struct usb_hcd *hcd = platform_get_drvdata(pdev);
55 struct ohci_regs __iomem *regs = hcd->regs;
56
57 dev_dbg(&pdev->dev, "stopping AT91RM9200 OHCI USB Controller\n");
58
59 /*
60 * Put the USB host controller into reset.
61 */
62 writel(0, &regs->control);
63
64 /*
65 * Stop the USB clocks.
66 */
67 clk_disable(fclk);
68 clk_disable(iclk);
69}
70
71
72/*-------------------------------------------------------------------------*/
73
74static int usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
75
76/* configure so an HC device and id are always provided */
77/* always called with process context; sleeping is OK */
78
79
80/**
81 * usb_hcd_at91_probe - initialize AT91RM9200-based HCDs
82 * Context: !in_interrupt()
83 *
84 * Allocates basic resources for this USB host controller, and
85 * then invokes the start() method for the HCD associated with it
86 * through the hotplug entry's driver_data.
Andrew Victor39a269c2006-01-22 10:32:13 -080087 */
88int usb_hcd_at91_probe (const struct hc_driver *driver, struct platform_device *pdev)
89{
90 int retval;
91 struct usb_hcd *hcd = NULL;
92
93 if (pdev->num_resources != 2) {
94 pr_debug("hcd probe: invalid num_resources");
95 return -ENODEV;
96 }
97
98 if ((pdev->resource[0].flags != IORESOURCE_MEM) || (pdev->resource[1].flags != IORESOURCE_IRQ)) {
99 pr_debug("hcd probe: invalid resource type\n");
100 return -ENODEV;
101 }
102
103 hcd = usb_create_hcd(driver, &pdev->dev, "at91rm9200");
104 if (!hcd)
105 return -ENOMEM;
106 hcd->rsrc_start = pdev->resource[0].start;
107 hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
108
109 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
110 pr_debug("request_mem_region failed\n");
111 retval = -EBUSY;
112 goto err1;
113 }
114
115 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
116 if (!hcd->regs) {
117 pr_debug("ioremap failed\n");
118 retval = -EIO;
119 goto err2;
120 }
121
122 iclk = clk_get(&pdev->dev, "ohci_clk");
123 fclk = clk_get(&pdev->dev, "uhpck");
124
125 at91_start_hc(pdev);
126 ohci_hcd_init(hcd_to_ohci(hcd));
127
128 retval = usb_add_hcd(hcd, pdev->resource[1].start, SA_INTERRUPT);
129 if (retval == 0)
130 return retval;
131
132 /* Error handling */
133 at91_stop_hc(pdev);
134
135 clk_put(fclk);
136 clk_put(iclk);
137
138 iounmap(hcd->regs);
139
140 err2:
141 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
142
143 err1:
144 usb_put_hcd(hcd);
145 return retval;
146}
147
148
Andrew Victor39a269c2006-01-22 10:32:13 -0800149/* may be called with controller, bus, and devices active */
150
151/**
152 * usb_hcd_at91_remove - shutdown processing for AT91RM9200-based HCDs
153 * @dev: USB Host Controller being removed
154 * Context: !in_interrupt()
155 *
156 * Reverses the effect of usb_hcd_at91_probe(), first invoking
157 * the HCD's stop() method. It is always called from a thread
158 * context, normally "rmmod", "apmd", or something similar.
159 *
160 */
161static int usb_hcd_at91_remove (struct usb_hcd *hcd, struct platform_device *pdev)
162{
163 usb_remove_hcd(hcd);
164 at91_stop_hc(pdev);
165 iounmap(hcd->regs);
David Brownell68ba61b2006-04-02 20:26:21 -0800166 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
Andrew Victor39a269c2006-01-22 10:32:13 -0800167
David Brownell68ba61b2006-04-02 20:26:21 -0800168 clk_put(fclk);
169 clk_put(iclk);
170 fclk = iclk = NULL;
Andrew Victor39a269c2006-01-22 10:32:13 -0800171
172 dev_set_drvdata(&pdev->dev, NULL);
173 return 0;
174}
175
176/*-------------------------------------------------------------------------*/
177
178static int __devinit
179ohci_at91_start (struct usb_hcd *hcd)
180{
181// struct at91_ohci_data *board = hcd->self.controller->platform_data;
182 struct ohci_hcd *ohci = hcd_to_ohci (hcd);
183 int ret;
184
185 if ((ret = ohci_init(ohci)) < 0)
186 return ret;
187
188 if ((ret = ohci_run(ohci)) < 0) {
189 err("can't start %s", hcd->self.bus_name);
190 ohci_stop(hcd);
191 return ret;
192 }
193// hcd->self.root_hub->maxchild = board->ports;
194 return 0;
195}
196
197/*-------------------------------------------------------------------------*/
198
199static const struct hc_driver ohci_at91_hc_driver = {
200 .description = hcd_name,
201 .product_desc = "AT91RM9200 OHCI",
202 .hcd_priv_size = sizeof(struct ohci_hcd),
203
204 /*
205 * generic hardware linkage
206 */
207 .irq = ohci_irq,
208 .flags = HCD_USB11 | HCD_MEMORY,
209
210 /*
211 * basic lifecycle operations
212 */
213 .start = ohci_at91_start,
214 .stop = ohci_stop,
215
216 /*
217 * managing i/o requests and associated device resources
218 */
219 .urb_enqueue = ohci_urb_enqueue,
220 .urb_dequeue = ohci_urb_dequeue,
221 .endpoint_disable = ohci_endpoint_disable,
222
223 /*
224 * scheduling support
225 */
226 .get_frame_number = ohci_get_frame,
227
228 /*
229 * root hub support
230 */
231 .hub_status_data = ohci_hub_status_data,
232 .hub_control = ohci_hub_control,
233
234#ifdef CONFIG_PM
David Brownell68ba61b2006-04-02 20:26:21 -0800235 .bus_suspend = ohci_bus_suspend,
236 .bus_resume = ohci_bus_resume,
Andrew Victor39a269c2006-01-22 10:32:13 -0800237#endif
238 .start_port_reset = ohci_start_port_reset,
239};
240
241/*-------------------------------------------------------------------------*/
242
243static int ohci_hcd_at91_drv_probe(struct platform_device *dev)
244{
245 return usb_hcd_at91_probe(&ohci_at91_hc_driver, dev);
246}
247
248static int ohci_hcd_at91_drv_remove(struct platform_device *dev)
249{
250 return usb_hcd_at91_remove(platform_get_drvdata(dev), dev);
251}
252
253#ifdef CONFIG_PM
Andrew Victor39a269c2006-01-22 10:32:13 -0800254
David Brownell68ba61b2006-04-02 20:26:21 -0800255/* REVISIT suspend/resume look "too" simple here */
256
257static int
258ohci_hcd_at91_drv_suspend(struct platform_device *dev, pm_message_t mesg)
259{
Andrew Victor39a269c2006-01-22 10:32:13 -0800260 clk_disable(fclk);
David Brownell68ba61b2006-04-02 20:26:21 -0800261 clk_disable(iclk);
Andrew Victor39a269c2006-01-22 10:32:13 -0800262
263 return 0;
264}
265
David Brownell68ba61b2006-04-02 20:26:21 -0800266static int ohci_hcd_at91_drv_resume(struct platform_device *dev)
Andrew Victor39a269c2006-01-22 10:32:13 -0800267{
David Brownell68ba61b2006-04-02 20:26:21 -0800268 clk_enable(iclk);
Andrew Victor39a269c2006-01-22 10:32:13 -0800269 clk_enable(fclk);
270
271 return 0;
272}
273#else
274#define ohci_hcd_at91_drv_suspend NULL
275#define ohci_hcd_at91_drv_resume NULL
276#endif
277
David Brownell68ba61b2006-04-02 20:26:21 -0800278MODULE_ALIAS("at91rm9200-ohci");
279
Andrew Victor39a269c2006-01-22 10:32:13 -0800280static struct platform_driver ohci_hcd_at91_driver = {
281 .probe = ohci_hcd_at91_drv_probe,
282 .remove = ohci_hcd_at91_drv_remove,
283 .suspend = ohci_hcd_at91_drv_suspend,
284 .resume = ohci_hcd_at91_drv_resume,
285 .driver = {
286 .name = "at91rm9200-ohci",
287 .owner = THIS_MODULE,
288 },
289};
290
291static int __init ohci_hcd_at91_init (void)
292{
293 if (usb_disabled())
294 return -ENODEV;
295
296 return platform_driver_register(&ohci_hcd_at91_driver);
297}
298
299static void __exit ohci_hcd_at91_cleanup (void)
300{
301 platform_driver_unregister(&ohci_hcd_at91_driver);
302}
303
304module_init (ohci_hcd_at91_init);
305module_exit (ohci_hcd_at91_cleanup);