2 * snps_udc_plat.c - Synopsys UDC Platform Driver
4 * Copyright (C) 2016 Broadcom
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.
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.
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"
29 #define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver"
31 void start_udc(struct udc *udc)
34 dev_info(udc->dev, "Connecting...\n");
35 udc_enable_dev_setup_interrupts(udc);
41 void stop_udc(struct udc *udc)
46 spin_lock(&udc->lock);
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);
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");
58 /* Mask interrupts. Required more so when the
59 * UDC is connected to a DRD phy.
61 udc_mask_unused_interrupts(udc);
63 /* Disconnect gadget driver */
65 spin_unlock(&udc->lock);
66 udc->driver->disconnect(&udc->gadget);
67 spin_lock(&udc->lock);
70 for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
71 empty_req_queue(&udc->ep[tmp]);
75 spin_unlock(&udc->lock);
76 dev_info(udc->dev, "Device disconnected\n");
79 void udc_drd_work(struct work_struct *work)
83 udc = container_of(to_delayed_work(work),
84 struct udc, drd_work);
87 dev_dbg(udc->dev, "idle -> device\n");
90 dev_dbg(udc->dev, "device -> idle\n");
95 static int usbd_connect_notify(struct notifier_block *self,
96 unsigned long event, void *ptr)
98 struct udc *udc = container_of(self, struct udc, nb);
100 dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
102 udc->conn_type = event;
104 schedule_delayed_work(&udc->drd_work, 0);
109 static int udc_plat_probe(struct platform_device *pdev)
111 struct device *dev = &pdev->dev;
112 struct resource *res;
116 udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
120 spin_lock_init(&udc->lock);
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);
128 /* udc csr registers base */
129 udc->csr = udc->virt_addr + UDC_CSR_ADDR;
131 /* dev registers base */
132 udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
134 /* ep registers base */
135 udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
138 udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
139 udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
141 udc->phys_addr = (unsigned long)res->start;
143 udc->irq = irq_of_parse_and_map(dev->of_node, 0);
145 dev_err(dev, "Can't parse and map interrupt\n");
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);
155 ret = phy_init(udc->udc_phy);
157 dev_err(dev, "UDC phy init failed");
161 ret = phy_power_on(udc->udc_phy);
163 dev_err(dev, "UDC phy power on failed");
164 phy_exit(udc->udc_phy);
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);
179 udc->nb.notifier_call = usbd_connect_notify;
180 ret = extcon_register_notifier(udc->edev, EXTCON_USB,
183 dev_err(dev, "Can't register extcon device\n");
187 ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
189 dev_err(dev, "Can't get cable state\n");
192 udc->conn_type = ret;
194 INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
199 ret = init_dma_pools(udc);
204 ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
207 dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
211 platform_set_drvdata(pdev, udc);
212 udc->chiprev = UDC_BCM_REV;
214 if (udc_probe(udc)) {
218 dev_info(dev, "Synopsys UDC platform driver probe successful\n");
227 extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
230 phy_power_off(udc->udc_phy);
231 phy_exit(udc->udc_phy);
236 static int udc_plat_remove(struct platform_device *pdev)
240 dev = platform_get_drvdata(pdev);
242 usb_del_gadget_udc(&dev->gadget);
243 /* gadget driver must not be registered */
244 if (WARN_ON(dev->driver))
247 /* dma pool cleanup */
252 platform_set_drvdata(pdev, NULL);
255 flush_workqueue(dev->drd_wq);
256 destroy_workqueue(dev->drd_wq);
259 phy_power_off(dev->udc_phy);
260 phy_exit(dev->udc_phy);
261 extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
263 dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
268 #ifdef CONFIG_PM_SLEEP
269 static int udc_plat_suspend(struct device *dev)
273 udc = dev_get_drvdata(dev);
276 if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
277 dev_dbg(udc->dev, "device -> idle\n");
280 phy_power_off(udc->udc_phy);
281 phy_exit(udc->udc_phy);
286 static int udc_plat_resume(struct device *dev)
291 udc = dev_get_drvdata(dev);
293 ret = phy_init(udc->udc_phy);
295 dev_err(udc->dev, "UDC phy init failure");
299 ret = phy_power_on(udc->udc_phy);
301 dev_err(udc->dev, "UDC phy power on failure");
302 phy_exit(udc->udc_phy);
306 if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
307 dev_dbg(udc->dev, "idle -> device\n");
313 static const struct dev_pm_ops udc_plat_pm_ops = {
314 .suspend = udc_plat_suspend,
315 .resume = udc_plat_resume,
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", },
326 MODULE_DEVICE_TABLE(of, of_udc_match);
329 static struct platform_driver udc_plat_driver = {
330 .probe = udc_plat_probe,
331 .remove = udc_plat_remove,
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,
340 module_platform_driver(udc_plat_driver);
342 MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
343 MODULE_AUTHOR("Broadcom");
344 MODULE_LICENSE("GPL v2");