]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/usb/host/ehci-s5p.c
USB: ehci-s5p: Add phy driver support
[karo-tx-linux.git] / drivers / usb / host / ehci-s5p.c
1 /*
2  * SAMSUNG S5P USB HOST EHCI Controller
3  *
4  * Copyright (C) 2011 Samsung Electronics Co.Ltd
5  * Author: Jingoo Han <jg1.han@samsung.com>
6  * Author: Joonyoung Shim <jy0922.shim@samsung.com>
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  *
13  */
14
15 #include <linux/clk.h>
16 #include <linux/of.h>
17 #include <linux/platform_device.h>
18 #include <linux/of_gpio.h>
19 #include <linux/platform_data/usb-ehci-s5p.h>
20 #include <linux/usb/phy.h>
21 #include <linux/usb/samsung_usb_phy.h>
22 #include <plat/usb-phy.h>
23
24 #define EHCI_INSNREG00(base)                    (base + 0x90)
25 #define EHCI_INSNREG00_ENA_INCR16               (0x1 << 25)
26 #define EHCI_INSNREG00_ENA_INCR8                (0x1 << 24)
27 #define EHCI_INSNREG00_ENA_INCR4                (0x1 << 23)
28 #define EHCI_INSNREG00_ENA_INCRX_ALIGN          (0x1 << 22)
29 #define EHCI_INSNREG00_ENABLE_DMA_BURST \
30         (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
31          EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
32
33 struct s5p_ehci_hcd {
34         struct device *dev;
35         struct usb_hcd *hcd;
36         struct clk *clk;
37         struct usb_phy *phy;
38         struct usb_otg *otg;
39         struct s5p_ehci_platdata *pdata;
40 };
41
42 static const struct hc_driver s5p_ehci_hc_driver = {
43         .description            = hcd_name,
44         .product_desc           = "S5P EHCI Host Controller",
45         .hcd_priv_size          = sizeof(struct ehci_hcd),
46
47         .irq                    = ehci_irq,
48         .flags                  = HCD_MEMORY | HCD_USB2,
49
50         .reset                  = ehci_setup,
51         .start                  = ehci_run,
52         .stop                   = ehci_stop,
53         .shutdown               = ehci_shutdown,
54
55         .get_frame_number       = ehci_get_frame,
56
57         .urb_enqueue            = ehci_urb_enqueue,
58         .urb_dequeue            = ehci_urb_dequeue,
59         .endpoint_disable       = ehci_endpoint_disable,
60         .endpoint_reset         = ehci_endpoint_reset,
61
62         .hub_status_data        = ehci_hub_status_data,
63         .hub_control            = ehci_hub_control,
64         .bus_suspend            = ehci_bus_suspend,
65         .bus_resume             = ehci_bus_resume,
66
67         .relinquish_port        = ehci_relinquish_port,
68         .port_handed_over       = ehci_port_handed_over,
69
70         .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
71 };
72
73 static void s5p_ehci_phy_enable(struct s5p_ehci_hcd *s5p_ehci)
74 {
75         struct platform_device *pdev = to_platform_device(s5p_ehci->dev);
76
77         if (s5p_ehci->phy)
78                 usb_phy_init(s5p_ehci->phy);
79         else if (s5p_ehci->pdata->phy_init)
80                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
81 }
82
83 static void s5p_ehci_phy_disable(struct s5p_ehci_hcd *s5p_ehci)
84 {
85         struct platform_device *pdev = to_platform_device(s5p_ehci->dev);
86
87         if (s5p_ehci->phy)
88                 usb_phy_shutdown(s5p_ehci->phy);
89         else if (s5p_ehci->pdata->phy_exit)
90                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
91 }
92
93 static void s5p_setup_vbus_gpio(struct platform_device *pdev)
94 {
95         int err;
96         int gpio;
97
98         if (!pdev->dev.of_node)
99                 return;
100
101         gpio = of_get_named_gpio(pdev->dev.of_node,
102                         "samsung,vbus-gpio", 0);
103         if (!gpio_is_valid(gpio))
104                 return;
105
106         err = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ehci_vbus_gpio");
107         if (err)
108                 dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio);
109 }
110
111 static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32);
112
113 static int s5p_ehci_probe(struct platform_device *pdev)
114 {
115         struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
116         struct s5p_ehci_hcd *s5p_ehci;
117         struct usb_hcd *hcd;
118         struct ehci_hcd *ehci;
119         struct resource *res;
120         struct usb_phy *phy;
121         int irq;
122         int err;
123
124         /*
125          * Right now device-tree probed devices don't get dma_mask set.
126          * Since shared usb code relies on it, set it here for now.
127          * Once we move to full device tree support this will vanish off.
128          */
129         if (!pdev->dev.dma_mask)
130                 pdev->dev.dma_mask = &ehci_s5p_dma_mask;
131         if (!pdev->dev.coherent_dma_mask)
132                 pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
133
134         s5p_setup_vbus_gpio(pdev);
135
136         s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd),
137                                 GFP_KERNEL);
138         if (!s5p_ehci)
139                 return -ENOMEM;
140
141         phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
142         if (IS_ERR_OR_NULL(phy)) {
143                 /* Fallback to pdata */
144                 if (!pdata) {
145                         dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
146                         return -EPROBE_DEFER;
147                 } else {
148                         s5p_ehci->pdata = pdata;
149                 }
150         } else {
151                 s5p_ehci->phy = phy;
152                 s5p_ehci->otg = phy->otg;
153         }
154
155         s5p_ehci->dev = &pdev->dev;
156
157         hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev,
158                                         dev_name(&pdev->dev));
159         if (!hcd) {
160                 dev_err(&pdev->dev, "Unable to create HCD\n");
161                 return -ENOMEM;
162         }
163
164         s5p_ehci->hcd = hcd;
165         s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
166
167         if (IS_ERR(s5p_ehci->clk)) {
168                 dev_err(&pdev->dev, "Failed to get usbhost clock\n");
169                 err = PTR_ERR(s5p_ehci->clk);
170                 goto fail_clk;
171         }
172
173         err = clk_prepare_enable(s5p_ehci->clk);
174         if (err)
175                 goto fail_clk;
176
177         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
178         if (!res) {
179                 dev_err(&pdev->dev, "Failed to get I/O memory\n");
180                 err = -ENXIO;
181                 goto fail_io;
182         }
183
184         hcd->rsrc_start = res->start;
185         hcd->rsrc_len = resource_size(res);
186         hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
187         if (!hcd->regs) {
188                 dev_err(&pdev->dev, "Failed to remap I/O memory\n");
189                 err = -ENOMEM;
190                 goto fail_io;
191         }
192
193         irq = platform_get_irq(pdev, 0);
194         if (!irq) {
195                 dev_err(&pdev->dev, "Failed to get IRQ\n");
196                 err = -ENODEV;
197                 goto fail_io;
198         }
199
200         if (s5p_ehci->otg)
201                 s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self);
202
203         s5p_ehci_phy_enable(s5p_ehci);
204
205         ehci = hcd_to_ehci(hcd);
206         ehci->caps = hcd->regs;
207
208         /* DMA burst Enable */
209         writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
210
211         err = usb_add_hcd(hcd, irq, IRQF_SHARED);
212         if (err) {
213                 dev_err(&pdev->dev, "Failed to add USB HCD\n");
214                 goto fail_add_hcd;
215         }
216
217         platform_set_drvdata(pdev, s5p_ehci);
218
219         return 0;
220
221 fail_add_hcd:
222         s5p_ehci_phy_disable(s5p_ehci);
223 fail_io:
224         clk_disable_unprepare(s5p_ehci->clk);
225 fail_clk:
226         usb_put_hcd(hcd);
227         return err;
228 }
229
230 static int s5p_ehci_remove(struct platform_device *pdev)
231 {
232         struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
233         struct usb_hcd *hcd = s5p_ehci->hcd;
234
235         usb_remove_hcd(hcd);
236
237         if (s5p_ehci->otg)
238                 s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self);
239
240         s5p_ehci_phy_disable(s5p_ehci);
241
242         clk_disable_unprepare(s5p_ehci->clk);
243
244         usb_put_hcd(hcd);
245
246         return 0;
247 }
248
249 static void s5p_ehci_shutdown(struct platform_device *pdev)
250 {
251         struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
252         struct usb_hcd *hcd = s5p_ehci->hcd;
253
254         if (hcd->driver->shutdown)
255                 hcd->driver->shutdown(hcd);
256 }
257
258 #ifdef CONFIG_PM
259 static int s5p_ehci_suspend(struct device *dev)
260 {
261         struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
262         struct usb_hcd *hcd = s5p_ehci->hcd;
263         bool do_wakeup = device_may_wakeup(dev);
264         int rc;
265
266         rc = ehci_suspend(hcd, do_wakeup);
267
268         if (s5p_ehci->otg)
269                 s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self);
270
271         s5p_ehci_phy_disable(s5p_ehci);
272
273         clk_disable_unprepare(s5p_ehci->clk);
274
275         return rc;
276 }
277
278 static int s5p_ehci_resume(struct device *dev)
279 {
280         struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
281         struct usb_hcd *hcd = s5p_ehci->hcd;
282
283         clk_prepare_enable(s5p_ehci->clk);
284
285         if (s5p_ehci->otg)
286                 s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self);
287
288         s5p_ehci_phy_enable(s5p_ehci);
289
290         /* DMA burst Enable */
291         writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
292
293         ehci_resume(hcd, false);
294         return 0;
295 }
296 #else
297 #define s5p_ehci_suspend        NULL
298 #define s5p_ehci_resume         NULL
299 #endif
300
301 static const struct dev_pm_ops s5p_ehci_pm_ops = {
302         .suspend        = s5p_ehci_suspend,
303         .resume         = s5p_ehci_resume,
304 };
305
306 #ifdef CONFIG_OF
307 static const struct of_device_id exynos_ehci_match[] = {
308         { .compatible = "samsung,exynos-ehci" },
309         {},
310 };
311 MODULE_DEVICE_TABLE(of, exynos_ehci_match);
312 #endif
313
314 static struct platform_driver s5p_ehci_driver = {
315         .probe          = s5p_ehci_probe,
316         .remove         = s5p_ehci_remove,
317         .shutdown       = s5p_ehci_shutdown,
318         .driver = {
319                 .name   = "s5p-ehci",
320                 .owner  = THIS_MODULE,
321                 .pm     = &s5p_ehci_pm_ops,
322                 .of_match_table = of_match_ptr(exynos_ehci_match),
323         }
324 };
325
326 MODULE_ALIAS("platform:s5p-ehci");