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