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