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 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 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 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"); 345