1 // SPDX-License-Identifier: GPL-2.0
3 * snps_udc_plat.c - Synopsys UDC Platform Driver
5 * Copyright (C) 2016 Broadcom
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"
21 #define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver"
23 static void start_udc(struct udc
*udc
)
26 dev_info(udc
->dev
, "Connecting...\n");
27 udc_enable_dev_setup_interrupts(udc
);
33 static void stop_udc(struct udc
*udc
)
38 spin_lock(&udc
->lock
);
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
);
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");
50 /* Mask interrupts. Required more so when the
51 * UDC is connected to a DRD phy.
53 udc_mask_unused_interrupts(udc
);
55 /* Disconnect gadget driver */
57 spin_unlock(&udc
->lock
);
58 udc
->driver
->disconnect(&udc
->gadget
);
59 spin_lock(&udc
->lock
);
62 for (tmp
= 0; tmp
< UDC_EP_NUM
; tmp
++)
63 empty_req_queue(&udc
->ep
[tmp
]);
67 spin_unlock(&udc
->lock
);
68 dev_info(udc
->dev
, "Device disconnected\n");
71 static void udc_drd_work(struct work_struct
*work
)
75 udc
= container_of(to_delayed_work(work
),
76 struct udc
, drd_work
);
79 dev_dbg(udc
->dev
, "idle -> device\n");
82 dev_dbg(udc
->dev
, "device -> idle\n");
87 static int usbd_connect_notify(struct notifier_block
*self
,
88 unsigned long event
, void *ptr
)
90 struct udc
*udc
= container_of(self
, struct udc
, nb
);
92 dev_dbg(udc
->dev
, "%s: event: %lu\n", __func__
, event
);
94 udc
->conn_type
= event
;
96 schedule_delayed_work(&udc
->drd_work
, 0);
101 static int udc_plat_probe(struct platform_device
*pdev
)
103 struct device
*dev
= &pdev
->dev
;
104 struct resource
*res
;
108 udc
= devm_kzalloc(dev
, sizeof(*udc
), GFP_KERNEL
);
112 spin_lock_init(&udc
->lock
);
115 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
116 udc
->virt_addr
= devm_ioremap_resource(dev
, res
);
117 if (IS_ERR(udc
->regs
))
118 return PTR_ERR(udc
->regs
);
120 /* udc csr registers base */
121 udc
->csr
= udc
->virt_addr
+ UDC_CSR_ADDR
;
123 /* dev registers base */
124 udc
->regs
= udc
->virt_addr
+ UDC_DEVCFG_ADDR
;
126 /* ep registers base */
127 udc
->ep_regs
= udc
->virt_addr
+ UDC_EPREGS_ADDR
;
130 udc
->rxfifo
= (u32 __iomem
*)(udc
->virt_addr
+ UDC_RXFIFO_ADDR
);
131 udc
->txfifo
= (u32 __iomem
*)(udc
->virt_addr
+ UDC_TXFIFO_ADDR
);
133 udc
->phys_addr
= (unsigned long)res
->start
;
135 udc
->irq
= irq_of_parse_and_map(dev
->of_node
, 0);
137 dev_err(dev
, "Can't parse and map interrupt\n");
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
);
147 ret
= phy_init(udc
->udc_phy
);
149 dev_err(dev
, "UDC phy init failed");
153 ret
= phy_power_on(udc
->udc_phy
);
155 dev_err(dev
, "UDC phy power on failed");
156 phy_exit(udc
->udc_phy
);
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
);
171 udc
->nb
.notifier_call
= usbd_connect_notify
;
172 ret
= extcon_register_notifier(udc
->edev
, EXTCON_USB
,
175 dev_err(dev
, "Can't register extcon device\n");
179 ret
= extcon_get_state(udc
->edev
, EXTCON_USB
);
181 dev_err(dev
, "Can't get cable state\n");
184 udc
->conn_type
= ret
;
186 INIT_DELAYED_WORK(&udc
->drd_work
, udc_drd_work
);
191 ret
= init_dma_pools(udc
);
196 ret
= devm_request_irq(dev
, udc
->irq
, udc_irq
, IRQF_SHARED
,
199 dev_err(dev
, "Request irq %d failed for UDC\n", udc
->irq
);
203 platform_set_drvdata(pdev
, udc
);
204 udc
->chiprev
= UDC_BCM_REV
;
206 if (udc_probe(udc
)) {
210 dev_info(dev
, "Synopsys UDC platform driver probe successful\n");
219 extcon_unregister_notifier(udc
->edev
, EXTCON_USB
, &udc
->nb
);
222 phy_power_off(udc
->udc_phy
);
223 phy_exit(udc
->udc_phy
);
228 static int udc_plat_remove(struct platform_device
*pdev
)
232 dev
= platform_get_drvdata(pdev
);
234 usb_del_gadget_udc(&dev
->gadget
);
235 /* gadget driver must not be registered */
236 if (WARN_ON(dev
->driver
))
239 /* dma pool cleanup */
244 platform_set_drvdata(pdev
, NULL
);
247 flush_workqueue(dev
->drd_wq
);
248 destroy_workqueue(dev
->drd_wq
);
251 phy_power_off(dev
->udc_phy
);
252 phy_exit(dev
->udc_phy
);
253 extcon_unregister_notifier(dev
->edev
, EXTCON_USB
, &dev
->nb
);
255 dev_info(&pdev
->dev
, "Synopsys UDC platform driver removed\n");
260 #ifdef CONFIG_PM_SLEEP
261 static int udc_plat_suspend(struct device
*dev
)
265 udc
= dev_get_drvdata(dev
);
268 if (extcon_get_state(udc
->edev
, EXTCON_USB
) > 0) {
269 dev_dbg(udc
->dev
, "device -> idle\n");
272 phy_power_off(udc
->udc_phy
);
273 phy_exit(udc
->udc_phy
);
278 static int udc_plat_resume(struct device
*dev
)
283 udc
= dev_get_drvdata(dev
);
285 ret
= phy_init(udc
->udc_phy
);
287 dev_err(udc
->dev
, "UDC phy init failure");
291 ret
= phy_power_on(udc
->udc_phy
);
293 dev_err(udc
->dev
, "UDC phy power on failure");
294 phy_exit(udc
->udc_phy
);
298 if (extcon_get_state(udc
->edev
, EXTCON_USB
) > 0) {
299 dev_dbg(udc
->dev
, "idle -> device\n");
305 static const struct dev_pm_ops udc_plat_pm_ops
= {
306 .suspend
= udc_plat_suspend
,
307 .resume
= udc_plat_resume
,
311 #if defined(CONFIG_OF)
312 static const struct of_device_id of_udc_match
[] = {
313 { .compatible
= "brcm,ns2-udc", },
314 { .compatible
= "brcm,cygnus-udc", },
315 { .compatible
= "brcm,iproc-udc", },
318 MODULE_DEVICE_TABLE(of
, of_udc_match
);
321 static struct platform_driver udc_plat_driver
= {
322 .probe
= udc_plat_probe
,
323 .remove
= udc_plat_remove
,
325 .name
= "snps-udc-plat",
326 .of_match_table
= of_match_ptr(of_udc_match
),
327 #ifdef CONFIG_PM_SLEEP
328 .pm
= &udc_plat_pm_ops
,
332 module_platform_driver(udc_plat_driver
);
334 MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION
);
335 MODULE_AUTHOR("Broadcom");
336 MODULE_LICENSE("GPL v2");