]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/usb/gadget/udc/snps_udc_plat.c
Merge tag 'driver-core-4.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / drivers / usb / gadget / udc / snps_udc_plat.c
1 /*
2  * snps_udc_plat.c - Synopsys UDC Platform Driver
3  *
4  * Copyright (C) 2016 Broadcom
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation version 2.
9  *
10  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11  * kind, whether express or implied; without even the implied warranty
12  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #include <linux/extcon.h>
17 #include <linux/of_address.h>
18 #include <linux/of_irq.h>
19 #include <linux/of_gpio.h>
20 #include <linux/platform_device.h>
21 #include <linux/phy/phy.h>
22 #include <linux/module.h>
23 #include <linux/dmapool.h>
24 #include <linux/interrupt.h>
25 #include <linux/moduleparam.h>
26 #include "amd5536udc.h"
27
28 /* description */
29 #define UDC_MOD_DESCRIPTION     "Synopsys UDC platform driver"
30
31 static void start_udc(struct udc *udc)
32 {
33         if (udc->driver) {
34                 dev_info(udc->dev, "Connecting...\n");
35                 udc_enable_dev_setup_interrupts(udc);
36                 udc_basic_init(udc);
37                 udc->connected = 1;
38         }
39 }
40
41 static void stop_udc(struct udc *udc)
42 {
43         int tmp;
44         u32 reg;
45
46         spin_lock(&udc->lock);
47
48         /* Flush the receieve fifo */
49         reg = readl(&udc->regs->ctl);
50         reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
51         writel(reg, &udc->regs->ctl);
52
53         reg = readl(&udc->regs->ctl);
54         reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
55         writel(reg, &udc->regs->ctl);
56         dev_dbg(udc->dev, "ep rx queue flushed\n");
57
58         /* Mask interrupts. Required more so when the
59          * UDC is connected to a DRD phy.
60          */
61         udc_mask_unused_interrupts(udc);
62
63         /* Disconnect gadget driver */
64         if (udc->driver) {
65                 spin_unlock(&udc->lock);
66                 udc->driver->disconnect(&udc->gadget);
67                 spin_lock(&udc->lock);
68
69                 /* empty queues */
70                 for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
71                         empty_req_queue(&udc->ep[tmp]);
72         }
73         udc->connected = 0;
74
75         spin_unlock(&udc->lock);
76         dev_info(udc->dev, "Device disconnected\n");
77 }
78
79 static void udc_drd_work(struct work_struct *work)
80 {
81         struct udc *udc;
82
83         udc = container_of(to_delayed_work(work),
84                            struct udc, drd_work);
85
86         if (udc->conn_type) {
87                 dev_dbg(udc->dev, "idle -> device\n");
88                 start_udc(udc);
89         } else {
90                 dev_dbg(udc->dev, "device -> idle\n");
91                 stop_udc(udc);
92         }
93 }
94
95 static int usbd_connect_notify(struct notifier_block *self,
96                                unsigned long event, void *ptr)
97 {
98         struct udc *udc = container_of(self, struct udc, nb);
99
100         dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
101
102         udc->conn_type = event;
103
104         schedule_delayed_work(&udc->drd_work, 0);
105
106         return NOTIFY_OK;
107 }
108
109 static int udc_plat_probe(struct platform_device *pdev)
110 {
111         struct device *dev = &pdev->dev;
112         struct resource *res;
113         struct udc *udc;
114         int ret;
115
116         udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
117         if (!udc)
118                 return -ENOMEM;
119
120         spin_lock_init(&udc->lock);
121         udc->dev = dev;
122
123         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
124         udc->virt_addr = devm_ioremap_resource(dev, res);
125         if (IS_ERR(udc->regs))
126                 return PTR_ERR(udc->regs);
127
128         /* udc csr registers base */
129         udc->csr = udc->virt_addr + UDC_CSR_ADDR;
130
131         /* dev registers base */
132         udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
133
134         /* ep registers base */
135         udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
136
137         /* fifo's base */
138         udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
139         udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
140
141         udc->phys_addr = (unsigned long)res->start;
142
143         udc->irq = irq_of_parse_and_map(dev->of_node, 0);
144         if (udc->irq <= 0) {
145                 dev_err(dev, "Can't parse and map interrupt\n");
146                 return -EINVAL;
147         }
148
149         udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
150         if (IS_ERR(udc->udc_phy)) {
151                 dev_err(dev, "Failed to obtain phy from device tree\n");
152                 return PTR_ERR(udc->udc_phy);
153         }
154
155         ret = phy_init(udc->udc_phy);
156         if (ret) {
157                 dev_err(dev, "UDC phy init failed");
158                 return ret;
159         }
160
161         ret = phy_power_on(udc->udc_phy);
162         if (ret) {
163                 dev_err(dev, "UDC phy power on failed");
164                 phy_exit(udc->udc_phy);
165                 return ret;
166         }
167
168         /* Register for extcon if supported */
169         if (of_get_property(dev->of_node, "extcon", NULL)) {
170                 udc->edev = extcon_get_edev_by_phandle(dev, 0);
171                 if (IS_ERR(udc->edev)) {
172                         if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
173                                 return -EPROBE_DEFER;
174                         dev_err(dev, "Invalid or missing extcon\n");
175                         ret = PTR_ERR(udc->edev);
176                         goto exit_phy;
177                 }
178
179                 udc->nb.notifier_call = usbd_connect_notify;
180                 ret = extcon_register_notifier(udc->edev, EXTCON_USB,
181                                                &udc->nb);
182                 if (ret < 0) {
183                         dev_err(dev, "Can't register extcon device\n");
184                         goto exit_phy;
185                 }
186
187                 ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
188                 if (ret < 0) {
189                         dev_err(dev, "Can't get cable state\n");
190                         goto exit_extcon;
191                 } else if (ret) {
192                         udc->conn_type = ret;
193                 }
194                 INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
195         }
196
197         /* init dma pools */
198         if (use_dma) {
199                 ret = init_dma_pools(udc);
200                 if (ret != 0)
201                         goto exit_extcon;
202         }
203
204         ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
205                                "snps-udc", udc);
206         if (ret < 0) {
207                 dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
208                 goto exit_dma;
209         }
210
211         platform_set_drvdata(pdev, udc);
212         udc->chiprev = UDC_BCM_REV;
213
214         if (udc_probe(udc)) {
215                 ret = -ENODEV;
216                 goto exit_dma;
217         }
218         dev_info(dev, "Synopsys UDC platform driver probe successful\n");
219
220         return 0;
221
222 exit_dma:
223         if (use_dma)
224                 free_dma_pools(udc);
225 exit_extcon:
226         if (udc->edev)
227                 extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
228 exit_phy:
229         if (udc->udc_phy) {
230                 phy_power_off(udc->udc_phy);
231                 phy_exit(udc->udc_phy);
232         }
233         return ret;
234 }
235
236 static int udc_plat_remove(struct platform_device *pdev)
237 {
238         struct udc *dev;
239
240         dev = platform_get_drvdata(pdev);
241
242         usb_del_gadget_udc(&dev->gadget);
243         /* gadget driver must not be registered */
244         if (WARN_ON(dev->driver))
245                 return 0;
246
247         /* dma pool cleanup */
248         free_dma_pools(dev);
249
250         udc_remove(dev);
251
252         platform_set_drvdata(pdev, NULL);
253
254         if (dev->drd_wq) {
255                 flush_workqueue(dev->drd_wq);
256                 destroy_workqueue(dev->drd_wq);
257         }
258
259         phy_power_off(dev->udc_phy);
260         phy_exit(dev->udc_phy);
261         extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
262
263         dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
264
265         return 0;
266 }
267
268 #ifdef CONFIG_PM_SLEEP
269 static int udc_plat_suspend(struct device *dev)
270 {
271         struct udc *udc;
272
273         udc = dev_get_drvdata(dev);
274         stop_udc(udc);
275
276         if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
277                 dev_dbg(udc->dev, "device -> idle\n");
278                 stop_udc(udc);
279         }
280         phy_power_off(udc->udc_phy);
281         phy_exit(udc->udc_phy);
282
283         return 0;
284 }
285
286 static int udc_plat_resume(struct device *dev)
287 {
288         struct udc *udc;
289         int ret;
290
291         udc = dev_get_drvdata(dev);
292
293         ret = phy_init(udc->udc_phy);
294         if (ret) {
295                 dev_err(udc->dev, "UDC phy init failure");
296                 return ret;
297         }
298
299         ret = phy_power_on(udc->udc_phy);
300         if (ret) {
301                 dev_err(udc->dev, "UDC phy power on failure");
302                 phy_exit(udc->udc_phy);
303                 return ret;
304         }
305
306         if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
307                 dev_dbg(udc->dev, "idle -> device\n");
308                 start_udc(udc);
309         }
310
311         return 0;
312 }
313 static const struct dev_pm_ops udc_plat_pm_ops = {
314         .suspend        = udc_plat_suspend,
315         .resume         = udc_plat_resume,
316 };
317 #endif
318
319 #if defined(CONFIG_OF)
320 static const struct of_device_id of_udc_match[] = {
321         { .compatible = "brcm,ns2-udc", },
322         { .compatible = "brcm,cygnus-udc", },
323         { .compatible = "brcm,iproc-udc", },
324         { }
325 };
326 MODULE_DEVICE_TABLE(of, of_udc_match);
327 #endif
328
329 static struct platform_driver udc_plat_driver = {
330         .probe          = udc_plat_probe,
331         .remove         = udc_plat_remove,
332         .driver         = {
333                 .name   = "snps-udc-plat",
334                 .of_match_table = of_match_ptr(of_udc_match),
335 #ifdef CONFIG_PM_SLEEP
336                 .pm     = &udc_plat_pm_ops,
337 #endif
338         },
339 };
340 module_platform_driver(udc_plat_driver);
341
342 MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
343 MODULE_AUTHOR("Broadcom");
344 MODULE_LICENSE("GPL v2");