]> Pileus Git - ~andy/linux/blob - drivers/usb/host/ehci-spear.c
USB: Add DT probing support to ehci-spear and ohci-spear
[~andy/linux] / drivers / usb / host / ehci-spear.c
1 /*
2 * Driver for EHCI HCD on SPEAR SOC
3 *
4 * Copyright (C) 2010 ST Micro Electronics,
5 * Deepak Sikri <deepak.sikri@st.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/jiffies.h>
16 #include <linux/of.h>
17 #include <linux/platform_device.h>
18 #include <linux/pm.h>
19
20 struct spear_ehci {
21         struct ehci_hcd ehci;
22         struct clk *clk;
23 };
24
25 #define to_spear_ehci(hcd)      (struct spear_ehci *)hcd_to_ehci(hcd)
26
27 static void spear_start_ehci(struct spear_ehci *ehci)
28 {
29         clk_enable(ehci->clk);
30 }
31
32 static void spear_stop_ehci(struct spear_ehci *ehci)
33 {
34         clk_disable(ehci->clk);
35 }
36
37 static int ehci_spear_setup(struct usb_hcd *hcd)
38 {
39         struct ehci_hcd *ehci = hcd_to_ehci(hcd);
40         int retval = 0;
41
42         /* registers start at offset 0x0 */
43         ehci->caps = hcd->regs;
44         ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci,
45                                 &ehci->caps->hc_capbase));
46         /* cache this readonly data; minimize chip reads */
47         ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
48         retval = ehci_halt(ehci);
49         if (retval)
50                 return retval;
51
52         retval = ehci_init(hcd);
53         if (retval)
54                 return retval;
55
56         ehci_reset(ehci);
57         ehci_port_power(ehci, 0);
58
59         return retval;
60 }
61
62 static const struct hc_driver ehci_spear_hc_driver = {
63         .description                    = hcd_name,
64         .product_desc                   = "SPEAr EHCI",
65         .hcd_priv_size                  = sizeof(struct spear_ehci),
66
67         /* generic hardware linkage */
68         .irq                            = ehci_irq,
69         .flags                          = HCD_MEMORY | HCD_USB2,
70
71         /* basic lifecycle operations */
72         .reset                          = ehci_spear_setup,
73         .start                          = ehci_run,
74         .stop                           = ehci_stop,
75         .shutdown                       = ehci_shutdown,
76
77         /* managing i/o requests and associated device resources */
78         .urb_enqueue                    = ehci_urb_enqueue,
79         .urb_dequeue                    = ehci_urb_dequeue,
80         .endpoint_disable               = ehci_endpoint_disable,
81         .endpoint_reset                 = ehci_endpoint_reset,
82
83         /* scheduling support */
84         .get_frame_number               = ehci_get_frame,
85
86         /* root hub support */
87         .hub_status_data                = ehci_hub_status_data,
88         .hub_control                    = ehci_hub_control,
89         .bus_suspend                    = ehci_bus_suspend,
90         .bus_resume                     = ehci_bus_resume,
91         .relinquish_port                = ehci_relinquish_port,
92         .port_handed_over               = ehci_port_handed_over,
93         .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
94 };
95
96 #ifdef CONFIG_PM
97 static int ehci_spear_drv_suspend(struct device *dev)
98 {
99         struct usb_hcd *hcd = dev_get_drvdata(dev);
100         struct ehci_hcd *ehci = hcd_to_ehci(hcd);
101         unsigned long flags;
102         int rc = 0;
103
104         if (time_before(jiffies, ehci->next_statechange))
105                 msleep(10);
106
107         /*
108          * Root hub was already suspended. Disable irq emission and mark HW
109          * unaccessible. The PM and USB cores make sure that the root hub is
110          * either suspended or stopped.
111          */
112         spin_lock_irqsave(&ehci->lock, flags);
113         ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
114         ehci_writel(ehci, 0, &ehci->regs->intr_enable);
115         ehci_readl(ehci, &ehci->regs->intr_enable);
116         spin_unlock_irqrestore(&ehci->lock, flags);
117
118         return rc;
119 }
120
121 static int ehci_spear_drv_resume(struct device *dev)
122 {
123         struct usb_hcd *hcd = dev_get_drvdata(dev);
124         struct ehci_hcd *ehci = hcd_to_ehci(hcd);
125
126         if (time_before(jiffies, ehci->next_statechange))
127                 msleep(100);
128
129         if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
130                 int mask = INTR_MASK;
131
132                 ehci_prepare_ports_for_controller_resume(ehci);
133
134                 if (!hcd->self.root_hub->do_remote_wakeup)
135                         mask &= ~STS_PCD;
136
137                 ehci_writel(ehci, mask, &ehci->regs->intr_enable);
138                 ehci_readl(ehci, &ehci->regs->intr_enable);
139                 return 0;
140         }
141
142         usb_root_hub_lost_power(hcd->self.root_hub);
143
144         /*
145          * Else reset, to cope with power loss or flush-to-storage style
146          * "resume" having let BIOS kick in during reboot.
147          */
148         ehci_halt(ehci);
149         ehci_reset(ehci);
150
151         /* emptying the schedule aborts any urbs */
152         spin_lock_irq(&ehci->lock);
153         if (ehci->reclaim)
154                 end_unlink_async(ehci);
155
156         ehci_work(ehci);
157         spin_unlock_irq(&ehci->lock);
158
159         ehci_writel(ehci, ehci->command, &ehci->regs->command);
160         ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
161         ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
162
163         /* here we "know" root ports should always stay powered */
164         ehci_port_power(ehci, 1);
165         return 0;
166 }
167 #endif /* CONFIG_PM */
168
169 static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend,
170                 ehci_spear_drv_resume);
171
172 static u64 spear_ehci_dma_mask = DMA_BIT_MASK(32);
173
174 static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
175 {
176         struct usb_hcd *hcd ;
177         struct spear_ehci *ehci;
178         struct resource *res;
179         struct clk *usbh_clk;
180         const struct hc_driver *driver = &ehci_spear_hc_driver;
181         int irq, retval;
182         char clk_name[20] = "usbh_clk";
183         static int instance = -1;
184
185         if (usb_disabled())
186                 return -ENODEV;
187
188         irq = platform_get_irq(pdev, 0);
189         if (irq < 0) {
190                 retval = irq;
191                 goto fail_irq_get;
192         }
193
194         /*
195          * Right now device-tree probed devices don't get dma_mask set.
196          * Since shared usb code relies on it, set it here for now.
197          * Once we have dma capability bindings this can go away.
198          */
199         if (!pdev->dev.dma_mask)
200                 pdev->dev.dma_mask = &spear_ehci_dma_mask;
201
202         /*
203          * Increment the device instance, when probing via device-tree
204          */
205         if (pdev->id < 0)
206                 instance++;
207         else
208                 instance = pdev->id;
209         sprintf(clk_name, "usbh.%01d_clk", instance);
210
211         usbh_clk = clk_get(NULL, clk_name);
212         if (IS_ERR(usbh_clk)) {
213                 dev_err(&pdev->dev, "Error getting interface clock\n");
214                 retval = PTR_ERR(usbh_clk);
215                 goto fail_get_usbh_clk;
216         }
217
218         hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
219         if (!hcd) {
220                 retval = -ENOMEM;
221                 goto fail_create_hcd;
222         }
223
224         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
225         if (!res) {
226                 retval = -ENODEV;
227                 goto fail_request_resource;
228         }
229
230         hcd->rsrc_start = res->start;
231         hcd->rsrc_len = resource_size(res);
232         if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
233                                 driver->description)) {
234                 retval = -EBUSY;
235                 goto fail_request_resource;
236         }
237
238         hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
239         if (hcd->regs == NULL) {
240                 dev_dbg(&pdev->dev, "error mapping memory\n");
241                 retval = -ENOMEM;
242                 goto fail_ioremap;
243         }
244
245         ehci = (struct spear_ehci *)hcd_to_ehci(hcd);
246         ehci->clk = usbh_clk;
247
248         spear_start_ehci(ehci);
249         retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
250         if (retval)
251                 goto fail_add_hcd;
252
253         return retval;
254
255 fail_add_hcd:
256         spear_stop_ehci(ehci);
257         iounmap(hcd->regs);
258 fail_ioremap:
259         release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
260 fail_request_resource:
261         usb_put_hcd(hcd);
262 fail_create_hcd:
263         clk_put(usbh_clk);
264 fail_get_usbh_clk:
265 fail_irq_get:
266         dev_err(&pdev->dev, "init fail, %d\n", retval);
267
268         return retval ;
269 }
270
271 static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
272 {
273         struct usb_hcd *hcd = platform_get_drvdata(pdev);
274         struct spear_ehci *ehci_p = to_spear_ehci(hcd);
275
276         if (!hcd)
277                 return 0;
278         if (in_interrupt())
279                 BUG();
280         usb_remove_hcd(hcd);
281
282         if (ehci_p->clk)
283                 spear_stop_ehci(ehci_p);
284         iounmap(hcd->regs);
285         release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
286         usb_put_hcd(hcd);
287
288         if (ehci_p->clk)
289                 clk_put(ehci_p->clk);
290
291         return 0;
292 }
293
294 static struct of_device_id spear_ehci_id_table[] __devinitdata = {
295         { .compatible = "st,spear600-ehci", },
296         { },
297 };
298
299 static struct platform_driver spear_ehci_hcd_driver = {
300         .probe          = spear_ehci_hcd_drv_probe,
301         .remove         = spear_ehci_hcd_drv_remove,
302         .shutdown       = usb_hcd_platform_shutdown,
303         .driver         = {
304                 .name = "spear-ehci",
305                 .bus = &platform_bus_type,
306                 .pm = &ehci_spear_pm_ops,
307                 .of_match_table = of_match_ptr(spear_ehci_id_table),
308         }
309 };
310
311 MODULE_ALIAS("platform:spear-ehci");