]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/usb/gadget/udc/snps_udc_plat.c
Merge tag 'for-linus-20170825' of git://git.infradead.org/linux-mtd
[mirror_ubuntu-artful-kernel.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");