]>
Commit | Line | Data |
---|---|---|
c8c38de9 | 1 | /* |
7675d6ba | 2 | * Driver for EHCI HCD on SPEAr SOC |
c8c38de9 DS |
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 | ||
c8c38de9 | 14 | #include <linux/clk.h> |
7675d6ba MG |
15 | #include <linux/dma-mapping.h> |
16 | #include <linux/io.h> | |
8c1b3693 | 17 | #include <linux/jiffies.h> |
7675d6ba MG |
18 | #include <linux/kernel.h> |
19 | #include <linux/module.h> | |
56fafb94 | 20 | #include <linux/of.h> |
8c1b3693 DS |
21 | #include <linux/platform_device.h> |
22 | #include <linux/pm.h> | |
7675d6ba MG |
23 | #include <linux/usb.h> |
24 | #include <linux/usb/hcd.h> | |
c8c38de9 | 25 | |
7675d6ba | 26 | #include "ehci.h" |
c8c38de9 | 27 | |
7675d6ba | 28 | #define DRIVER_DESC "EHCI SPEAr driver" |
c8c38de9 | 29 | |
7675d6ba | 30 | static const char hcd_name[] = "SPEAr-ehci"; |
c8c38de9 | 31 | |
7675d6ba MG |
32 | struct spear_ehci { |
33 | struct clk *clk; | |
34 | }; | |
c8c38de9 | 35 | |
7675d6ba | 36 | #define to_spear_ehci(hcd) (struct spear_ehci *)(hcd_to_ehci(hcd)->priv) |
c8c38de9 | 37 | |
7675d6ba | 38 | static struct hc_driver __read_mostly ehci_spear_hc_driver; |
c8c38de9 | 39 | |
ab1f046a | 40 | #ifdef CONFIG_PM_SLEEP |
8c1b3693 DS |
41 | static int ehci_spear_drv_suspend(struct device *dev) |
42 | { | |
43 | struct usb_hcd *hcd = dev_get_drvdata(dev); | |
c5cf9212 AS |
44 | bool do_wakeup = device_may_wakeup(dev); |
45 | ||
46 | return ehci_suspend(hcd, do_wakeup); | |
8c1b3693 DS |
47 | } |
48 | ||
49 | static int ehci_spear_drv_resume(struct device *dev) | |
50 | { | |
51 | struct usb_hcd *hcd = dev_get_drvdata(dev); | |
8c1b3693 | 52 | |
c5cf9212 | 53 | ehci_resume(hcd, false); |
8c1b3693 DS |
54 | return 0; |
55 | } | |
ab1f046a | 56 | #endif /* CONFIG_PM_SLEEP */ |
8c1b3693 DS |
57 | |
58 | static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend, | |
59 | ehci_spear_drv_resume); | |
60 | ||
c8c38de9 DS |
61 | static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) |
62 | { | |
63 | struct usb_hcd *hcd ; | |
7675d6ba | 64 | struct spear_ehci *sehci; |
c8c38de9 DS |
65 | struct resource *res; |
66 | struct clk *usbh_clk; | |
67 | const struct hc_driver *driver = &ehci_spear_hc_driver; | |
c8c38de9 | 68 | int irq, retval; |
c8c38de9 DS |
69 | |
70 | if (usb_disabled()) | |
71 | return -ENODEV; | |
72 | ||
73 | irq = platform_get_irq(pdev, 0); | |
74 | if (irq < 0) { | |
75 | retval = irq; | |
98515e59 | 76 | goto fail; |
c8c38de9 DS |
77 | } |
78 | ||
56fafb94 SR |
79 | /* |
80 | * Right now device-tree probed devices don't get dma_mask set. | |
81 | * Since shared usb code relies on it, set it here for now. | |
82 | * Once we have dma capability bindings this can go away. | |
83 | */ | |
84 | if (!pdev->dev.dma_mask) | |
3b9561e9 SW |
85 | pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; |
86 | if (!pdev->dev.coherent_dma_mask) | |
87 | pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); | |
56fafb94 | 88 | |
98515e59 | 89 | usbh_clk = devm_clk_get(&pdev->dev, NULL); |
c8c38de9 DS |
90 | if (IS_ERR(usbh_clk)) { |
91 | dev_err(&pdev->dev, "Error getting interface clock\n"); | |
92 | retval = PTR_ERR(usbh_clk); | |
98515e59 | 93 | goto fail; |
c8c38de9 DS |
94 | } |
95 | ||
96 | hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); | |
97 | if (!hcd) { | |
98 | retval = -ENOMEM; | |
98515e59 | 99 | goto fail; |
c8c38de9 DS |
100 | } |
101 | ||
102 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
103 | if (!res) { | |
104 | retval = -ENODEV; | |
98515e59 | 105 | goto err_put_hcd; |
c8c38de9 DS |
106 | } |
107 | ||
108 | hcd->rsrc_start = res->start; | |
109 | hcd->rsrc_len = resource_size(res); | |
98515e59 | 110 | if (!devm_request_mem_region(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len, |
c8c38de9 DS |
111 | driver->description)) { |
112 | retval = -EBUSY; | |
98515e59 | 113 | goto err_put_hcd; |
c8c38de9 DS |
114 | } |
115 | ||
98515e59 | 116 | hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len); |
c8c38de9 DS |
117 | if (hcd->regs == NULL) { |
118 | dev_dbg(&pdev->dev, "error mapping memory\n"); | |
119 | retval = -ENOMEM; | |
98515e59 | 120 | goto err_put_hcd; |
c8c38de9 DS |
121 | } |
122 | ||
7675d6ba MG |
123 | sehci = to_spear_ehci(hcd); |
124 | sehci->clk = usbh_clk; | |
125 | ||
126 | /* registers start at offset 0x0 */ | |
127 | hcd_to_ehci(hcd)->caps = hcd->regs; | |
c8c38de9 | 128 | |
7675d6ba | 129 | clk_prepare_enable(sehci->clk); |
b5dd18d8 | 130 | retval = usb_add_hcd(hcd, irq, IRQF_SHARED); |
c8c38de9 | 131 | if (retval) |
98515e59 | 132 | goto err_stop_ehci; |
c8c38de9 DS |
133 | |
134 | return retval; | |
135 | ||
98515e59 | 136 | err_stop_ehci: |
7675d6ba | 137 | clk_disable_unprepare(sehci->clk); |
98515e59 | 138 | err_put_hcd: |
c8c38de9 | 139 | usb_put_hcd(hcd); |
98515e59 | 140 | fail: |
c8c38de9 DS |
141 | dev_err(&pdev->dev, "init fail, %d\n", retval); |
142 | ||
143 | return retval ; | |
144 | } | |
145 | ||
146 | static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) | |
147 | { | |
148 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | |
7675d6ba | 149 | struct spear_ehci *sehci = to_spear_ehci(hcd); |
c8c38de9 DS |
150 | |
151 | if (!hcd) | |
152 | return 0; | |
153 | if (in_interrupt()) | |
154 | BUG(); | |
155 | usb_remove_hcd(hcd); | |
156 | ||
7675d6ba MG |
157 | if (sehci->clk) |
158 | clk_disable_unprepare(sehci->clk); | |
c8c38de9 DS |
159 | usb_put_hcd(hcd); |
160 | ||
c8c38de9 DS |
161 | return 0; |
162 | } | |
163 | ||
d3608b6d | 164 | static struct of_device_id spear_ehci_id_table[] = { |
56fafb94 SR |
165 | { .compatible = "st,spear600-ehci", }, |
166 | { }, | |
167 | }; | |
168 | ||
c8c38de9 DS |
169 | static struct platform_driver spear_ehci_hcd_driver = { |
170 | .probe = spear_ehci_hcd_drv_probe, | |
171 | .remove = spear_ehci_hcd_drv_remove, | |
172 | .shutdown = usb_hcd_platform_shutdown, | |
173 | .driver = { | |
174 | .name = "spear-ehci", | |
8c1b3693 DS |
175 | .bus = &platform_bus_type, |
176 | .pm = &ehci_spear_pm_ops, | |
56fafb94 | 177 | .of_match_table = of_match_ptr(spear_ehci_id_table), |
c8c38de9 DS |
178 | } |
179 | }; | |
180 | ||
7675d6ba MG |
181 | static const struct ehci_driver_overrides spear_overrides __initdata = { |
182 | .extra_priv_size = sizeof(struct spear_ehci), | |
183 | }; | |
184 | ||
185 | static int __init ehci_spear_init(void) | |
186 | { | |
187 | if (usb_disabled()) | |
188 | return -ENODEV; | |
189 | ||
190 | pr_info("%s: " DRIVER_DESC "\n", hcd_name); | |
191 | ||
192 | ehci_init_driver(&ehci_spear_hc_driver, &spear_overrides); | |
193 | return platform_driver_register(&spear_ehci_hcd_driver); | |
194 | } | |
195 | module_init(ehci_spear_init); | |
196 | ||
197 | static void __exit ehci_spear_cleanup(void) | |
198 | { | |
199 | platform_driver_unregister(&spear_ehci_hcd_driver); | |
200 | } | |
201 | module_exit(ehci_spear_cleanup); | |
202 | ||
203 | MODULE_DESCRIPTION(DRIVER_DESC); | |
c8c38de9 | 204 | MODULE_ALIAS("platform:spear-ehci"); |
7675d6ba MG |
205 | MODULE_AUTHOR("Deepak Sikri"); |
206 | MODULE_LICENSE("GPL"); |