diff options
-rw-r--r-- | drivers/usb/gadget/udc/Kconfig | 16 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/amd5536udc.h | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/snps_udc_core.c | 54 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/snps_udc_plat.c | 344 |
5 files changed, 409 insertions, 20 deletions
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 1c14c283cc47..e5d3ba9a8604 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig | |||
@@ -256,7 +256,7 @@ config USB_MV_U3D | |||
256 | controller, which support super speed USB peripheral. | 256 | controller, which support super speed USB peripheral. |
257 | 257 | ||
258 | config USB_SNP_CORE | 258 | config USB_SNP_CORE |
259 | depends on USB_AMD5536UDC | 259 | depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT) |
260 | tristate | 260 | tristate |
261 | help | 261 | help |
262 | This enables core driver support for Synopsys USB 2.0 Device | 262 | This enables core driver support for Synopsys USB 2.0 Device |
@@ -269,6 +269,20 @@ config USB_SNP_CORE | |||
269 | This IP is different to the High Speed OTG IP that can be enabled | 269 | This IP is different to the High Speed OTG IP that can be enabled |
270 | by selecting USB_DWC2 or USB_DWC3 options. | 270 | by selecting USB_DWC2 or USB_DWC3 options. |
271 | 271 | ||
272 | config USB_SNP_UDC_PLAT | ||
273 | tristate "Synopsys USB 2.0 Device controller" | ||
274 | depends on (USB_GADGET && OF) | ||
275 | select USB_GADGET_DUALSPEED | ||
276 | select USB_SNP_CORE | ||
277 | default ARCH_BCM_IPROC | ||
278 | help | ||
279 | This adds Platform Device support for Synopsys Designware core | ||
280 | AHB subsystem USB2.0 Device Controller (UDC). | ||
281 | |||
282 | This driver works with UDCs integrated into Broadcom's Northstar2 | ||
283 | and Cygnus SoCs. | ||
284 | |||
285 | If unsure, say N. | ||
272 | # | 286 | # |
273 | # Controllers available in both integrated and discrete versions | 287 | # Controllers available in both integrated and discrete versions |
274 | # | 288 | # |
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 4f4fd626b9ff..ea9e1c7f1923 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile | |||
@@ -37,4 +37,5 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o | |||
37 | obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o | 37 | obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o |
38 | obj-$(CONFIG_USB_GR_UDC) += gr_udc.o | 38 | obj-$(CONFIG_USB_GR_UDC) += gr_udc.o |
39 | obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o | 39 | obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o |
40 | obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o | ||
40 | obj-$(CONFIG_USB_BDC_UDC) += bdc/ | 41 | obj-$(CONFIG_USB_BDC_UDC) += bdc/ |
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index 91aae23b7370..4fe22d432af2 100644 --- a/drivers/usb/gadget/udc/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h | |||
@@ -16,6 +16,7 @@ | |||
16 | /* debug control */ | 16 | /* debug control */ |
17 | /* #define UDC_VERBOSE */ | 17 | /* #define UDC_VERBOSE */ |
18 | 18 | ||
19 | #include <linux/extcon.h> | ||
19 | #include <linux/usb/ch9.h> | 20 | #include <linux/usb/ch9.h> |
20 | #include <linux/usb/gadget.h> | 21 | #include <linux/usb/gadget.h> |
21 | 22 | ||
@@ -28,6 +29,9 @@ | |||
28 | #define UDC_HSA0_REV 1 | 29 | #define UDC_HSA0_REV 1 |
29 | #define UDC_HSB1_REV 2 | 30 | #define UDC_HSB1_REV 2 |
30 | 31 | ||
32 | /* Broadcom chip rev. */ | ||
33 | #define UDC_BCM_REV 10 | ||
34 | |||
31 | /* | 35 | /* |
32 | * SETUP usb commands | 36 | * SETUP usb commands |
33 | * needed, because some SETUP's are handled in hw, but must be passed to | 37 | * needed, because some SETUP's are handled in hw, but must be passed to |
@@ -112,6 +116,7 @@ | |||
112 | #define UDC_DEVCTL_BRLEN_MASK 0x00ff0000 | 116 | #define UDC_DEVCTL_BRLEN_MASK 0x00ff0000 |
113 | #define UDC_DEVCTL_BRLEN_OFS 16 | 117 | #define UDC_DEVCTL_BRLEN_OFS 16 |
114 | 118 | ||
119 | #define UDC_DEVCTL_SRX_FLUSH 14 | ||
115 | #define UDC_DEVCTL_CSR_DONE 13 | 120 | #define UDC_DEVCTL_CSR_DONE 13 |
116 | #define UDC_DEVCTL_DEVNAK 12 | 121 | #define UDC_DEVCTL_DEVNAK 12 |
117 | #define UDC_DEVCTL_SD 10 | 122 | #define UDC_DEVCTL_SD 10 |
@@ -564,7 +569,15 @@ struct udc { | |||
564 | u16 cur_intf; | 569 | u16 cur_intf; |
565 | u16 cur_alt; | 570 | u16 cur_alt; |
566 | 571 | ||
572 | /* for platform device and extcon support */ | ||
567 | struct device *dev; | 573 | struct device *dev; |
574 | struct phy *udc_phy; | ||
575 | struct extcon_dev *edev; | ||
576 | struct extcon_specific_cable_nb extcon_nb; | ||
577 | struct notifier_block nb; | ||
578 | struct delayed_work drd_work; | ||
579 | struct workqueue_struct *drd_wq; | ||
580 | u32 conn_type; | ||
568 | }; | 581 | }; |
569 | 582 | ||
570 | #define to_amd5536_udc(g) (container_of((g), struct udc, gadget)) | 583 | #define to_amd5536_udc(g) (container_of((g), struct udc, gadget)) |
@@ -580,6 +593,7 @@ int udc_enable_dev_setup_interrupts(struct udc *dev); | |||
580 | int udc_mask_unused_interrupts(struct udc *dev); | 593 | int udc_mask_unused_interrupts(struct udc *dev); |
581 | irqreturn_t udc_irq(int irq, void *pdev); | 594 | irqreturn_t udc_irq(int irq, void *pdev); |
582 | void gadget_release(struct device *pdev); | 595 | void gadget_release(struct device *pdev); |
596 | void empty_req_queue(struct udc_ep *ep); | ||
583 | void udc_basic_init(struct udc *dev); | 597 | void udc_basic_init(struct udc *dev); |
584 | void free_dma_pools(struct udc *dev); | 598 | void free_dma_pools(struct udc *dev); |
585 | int init_dma_pools(struct udc *dev); | 599 | int init_dma_pools(struct udc *dev); |
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index d592f77da744..38a165dbf924 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c | |||
@@ -41,7 +41,6 @@ | |||
41 | #include "amd5536udc.h" | 41 | #include "amd5536udc.h" |
42 | 42 | ||
43 | static void udc_tasklet_disconnect(unsigned long); | 43 | static void udc_tasklet_disconnect(unsigned long); |
44 | static void empty_req_queue(struct udc_ep *); | ||
45 | static void udc_setup_endpoints(struct udc *dev); | 44 | static void udc_setup_endpoints(struct udc *dev); |
46 | static void udc_soft_reset(struct udc *dev); | 45 | static void udc_soft_reset(struct udc *dev); |
47 | static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep); | 46 | static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep); |
@@ -1244,7 +1243,7 @@ finished: | |||
1244 | } | 1243 | } |
1245 | 1244 | ||
1246 | /* Empty request queue of an endpoint; caller holds spinlock */ | 1245 | /* Empty request queue of an endpoint; caller holds spinlock */ |
1247 | static void empty_req_queue(struct udc_ep *ep) | 1246 | void empty_req_queue(struct udc_ep *ep) |
1248 | { | 1247 | { |
1249 | struct udc_request *req; | 1248 | struct udc_request *req; |
1250 | 1249 | ||
@@ -1256,6 +1255,7 @@ static void empty_req_queue(struct udc_ep *ep) | |||
1256 | complete_req(ep, req, -ESHUTDOWN); | 1255 | complete_req(ep, req, -ESHUTDOWN); |
1257 | } | 1256 | } |
1258 | } | 1257 | } |
1258 | EXPORT_SYMBOL_GPL(empty_req_queue); | ||
1259 | 1259 | ||
1260 | /* Dequeues a request packet, called by gadget driver */ | 1260 | /* Dequeues a request packet, called by gadget driver */ |
1261 | static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq) | 1261 | static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq) |
@@ -1623,6 +1623,9 @@ static void udc_setup_endpoints(struct udc *dev) | |||
1623 | /* Bringup after Connect event, initial bringup to be ready for ep0 events */ | 1623 | /* Bringup after Connect event, initial bringup to be ready for ep0 events */ |
1624 | static void usb_connect(struct udc *dev) | 1624 | static void usb_connect(struct udc *dev) |
1625 | { | 1625 | { |
1626 | /* Return if already connected */ | ||
1627 | if (dev->connected) | ||
1628 | return; | ||
1626 | 1629 | ||
1627 | dev_info(dev->dev, "USB Connect\n"); | 1630 | dev_info(dev->dev, "USB Connect\n"); |
1628 | 1631 | ||
@@ -1641,6 +1644,9 @@ static void usb_connect(struct udc *dev) | |||
1641 | */ | 1644 | */ |
1642 | static void usb_disconnect(struct udc *dev) | 1645 | static void usb_disconnect(struct udc *dev) |
1643 | { | 1646 | { |
1647 | /* Return if already disconnected */ | ||
1648 | if (!dev->connected) | ||
1649 | return; | ||
1644 | 1650 | ||
1645 | dev_info(dev->dev, "USB Disconnect\n"); | 1651 | dev_info(dev->dev, "USB Disconnect\n"); |
1646 | 1652 | ||
@@ -1715,11 +1721,15 @@ static void udc_soft_reset(struct udc *dev) | |||
1715 | /* device int. status reset */ | 1721 | /* device int. status reset */ |
1716 | writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts); | 1722 | writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts); |
1717 | 1723 | ||
1718 | spin_lock_irqsave(&udc_irq_spinlock, flags); | 1724 | /* Don't do this for Broadcom UDC since this is a reserved |
1719 | writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); | 1725 | * bit. |
1720 | readl(&dev->regs->cfg); | 1726 | */ |
1721 | spin_unlock_irqrestore(&udc_irq_spinlock, flags); | 1727 | if (dev->chiprev != UDC_BCM_REV) { |
1722 | 1728 | spin_lock_irqsave(&udc_irq_spinlock, flags); | |
1729 | writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); | ||
1730 | readl(&dev->regs->cfg); | ||
1731 | spin_unlock_irqrestore(&udc_irq_spinlock, flags); | ||
1732 | } | ||
1723 | } | 1733 | } |
1724 | 1734 | ||
1725 | /* RDE timer callback to set RDE bit */ | 1735 | /* RDE timer callback to set RDE bit */ |
@@ -3171,21 +3181,27 @@ int udc_probe(struct udc *dev) | |||
3171 | dev_info(dev->dev, "%s\n", mod_desc); | 3181 | dev_info(dev->dev, "%s\n", mod_desc); |
3172 | 3182 | ||
3173 | snprintf(tmp, sizeof(tmp), "%d", dev->irq); | 3183 | snprintf(tmp, sizeof(tmp), "%d", dev->irq); |
3174 | dev_info(dev->dev, | 3184 | |
3175 | "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n", | 3185 | /* Print this device info for AMD chips only*/ |
3176 | tmp, dev->phys_addr, dev->chiprev, | 3186 | if (dev->chiprev == UDC_HSA0_REV || |
3177 | (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); | 3187 | dev->chiprev == UDC_HSB1_REV) { |
3178 | strcpy(tmp, UDC_DRIVER_VERSION_STRING); | 3188 | dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n", |
3179 | if (dev->chiprev == UDC_HSA0_REV) { | 3189 | tmp, dev->phys_addr, dev->chiprev, |
3180 | dev_err(dev->dev, "chip revision is A0; too old\n"); | 3190 | (dev->chiprev == UDC_HSA0_REV) ? |
3181 | retval = -ENODEV; | 3191 | "A0" : "B1"); |
3182 | goto finished; | 3192 | strcpy(tmp, UDC_DRIVER_VERSION_STRING); |
3193 | if (dev->chiprev == UDC_HSA0_REV) { | ||
3194 | dev_err(dev->dev, "chip revision is A0; too old\n"); | ||
3195 | retval = -ENODEV; | ||
3196 | goto finished; | ||
3197 | } | ||
3198 | dev_info(dev->dev, | ||
3199 | "driver version: %s(for Geode5536 B1)\n", tmp); | ||
3183 | } | 3200 | } |
3184 | dev_info(dev->dev, | 3201 | |
3185 | "driver version: %s(for Geode5536 B1)\n", tmp); | ||
3186 | udc = dev; | 3202 | udc = dev; |
3187 | 3203 | ||
3188 | retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget, | 3204 | retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget, |
3189 | gadget_release); | 3205 | gadget_release); |
3190 | if (retval) | 3206 | if (retval) |
3191 | goto finished; | 3207 | goto finished; |
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c new file mode 100644 index 000000000000..2e11f19e07ae --- /dev/null +++ b/drivers/usb/gadget/udc/snps_udc_plat.c | |||
@@ -0,0 +1,344 @@ | |||
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"); | ||