Linux Audio

Check our new training course

Buildroot integration, development and maintenance

Need a Buildroot system for your embedded project?
Loading...
Note: File does not exist in v4.10.11.
  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	udc->virt_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
116	if (IS_ERR(udc->virt_addr))
117		return PTR_ERR(udc->virt_addr);
118
119	/* udc csr registers base */
120	udc->csr = udc->virt_addr + UDC_CSR_ADDR;
121
122	/* dev registers base */
123	udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
124
125	/* ep registers base */
126	udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
127
128	/* fifo's base */
129	udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
130	udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
131
132	udc->phys_addr = (unsigned long)res->start;
133
134	udc->irq = irq_of_parse_and_map(dev->of_node, 0);
135	if (udc->irq <= 0) {
136		dev_err(dev, "Can't parse and map interrupt\n");
137		return -EINVAL;
138	}
139
140	udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
141	if (IS_ERR(udc->udc_phy)) {
142		dev_err(dev, "Failed to obtain phy from device tree\n");
143		return PTR_ERR(udc->udc_phy);
144	}
145
146	ret = phy_init(udc->udc_phy);
147	if (ret) {
148		dev_err(dev, "UDC phy init failed");
149		return ret;
150	}
151
152	ret = phy_power_on(udc->udc_phy);
153	if (ret) {
154		dev_err(dev, "UDC phy power on failed");
155		phy_exit(udc->udc_phy);
156		return ret;
157	}
158
159	/* Register for extcon if supported */
160	if (of_property_present(dev->of_node, "extcon")) {
161		udc->edev = extcon_get_edev_by_phandle(dev, 0);
162		if (IS_ERR(udc->edev)) {
163			if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
164				return -EPROBE_DEFER;
165			dev_err(dev, "Invalid or missing extcon\n");
166			ret = PTR_ERR(udc->edev);
167			goto exit_phy;
168		}
169
170		udc->nb.notifier_call = usbd_connect_notify;
171		ret = extcon_register_notifier(udc->edev, EXTCON_USB,
172					       &udc->nb);
173		if (ret < 0) {
174			dev_err(dev, "Can't register extcon device\n");
175			goto exit_phy;
176		}
177
178		ret = extcon_get_state(udc->edev, EXTCON_USB);
179		if (ret < 0) {
180			dev_err(dev, "Can't get cable state\n");
181			goto exit_extcon;
182		} else if (ret) {
183			udc->conn_type = ret;
184		}
185		INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
186	}
187
188	/* init dma pools */
189	if (use_dma) {
190		ret = init_dma_pools(udc);
191		if (ret != 0)
192			goto exit_extcon;
193	}
194
195	ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
196			       "snps-udc", udc);
197	if (ret < 0) {
198		dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
199		goto exit_dma;
200	}
201
202	platform_set_drvdata(pdev, udc);
203	udc->chiprev = UDC_BCM_REV;
204
205	if (udc_probe(udc)) {
206		ret = -ENODEV;
207		goto exit_dma;
208	}
209	dev_info(dev, "Synopsys UDC platform driver probe successful\n");
210
211	return 0;
212
213exit_dma:
214	if (use_dma)
215		free_dma_pools(udc);
216exit_extcon:
217	if (udc->edev)
218		extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
219exit_phy:
220	if (udc->udc_phy) {
221		phy_power_off(udc->udc_phy);
222		phy_exit(udc->udc_phy);
223	}
224	return ret;
225}
226
227static void udc_plat_remove(struct platform_device *pdev)
228{
229	struct udc *dev;
230
231	dev = platform_get_drvdata(pdev);
232
233	usb_del_gadget_udc(&dev->gadget);
234	/* gadget driver must not be registered */
235	if (WARN_ON(dev->driver))
236		return;
237
238	/* dma pool cleanup */
239	free_dma_pools(dev);
240
241	udc_remove(dev);
242
243	platform_set_drvdata(pdev, NULL);
244
245	phy_power_off(dev->udc_phy);
246	phy_exit(dev->udc_phy);
247	extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
248
249	dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
250}
251
252#ifdef CONFIG_PM_SLEEP
253static int udc_plat_suspend(struct device *dev)
254{
255	struct udc *udc;
256
257	udc = dev_get_drvdata(dev);
258	stop_udc(udc);
259
260	if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
261		dev_dbg(udc->dev, "device -> idle\n");
262		stop_udc(udc);
263	}
264	phy_power_off(udc->udc_phy);
265	phy_exit(udc->udc_phy);
266
267	return 0;
268}
269
270static int udc_plat_resume(struct device *dev)
271{
272	struct udc *udc;
273	int ret;
274
275	udc = dev_get_drvdata(dev);
276
277	ret = phy_init(udc->udc_phy);
278	if (ret) {
279		dev_err(udc->dev, "UDC phy init failure");
280		return ret;
281	}
282
283	ret = phy_power_on(udc->udc_phy);
284	if (ret) {
285		dev_err(udc->dev, "UDC phy power on failure");
286		phy_exit(udc->udc_phy);
287		return ret;
288	}
289
290	if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
291		dev_dbg(udc->dev, "idle -> device\n");
292		start_udc(udc);
293	}
294
295	return 0;
296}
297static const struct dev_pm_ops udc_plat_pm_ops = {
298	.suspend	= udc_plat_suspend,
299	.resume		= udc_plat_resume,
300};
301#endif
302
303static const struct of_device_id of_udc_match[] = {
304	{ .compatible = "brcm,ns2-udc", },
305	{ .compatible = "brcm,cygnus-udc", },
306	{ .compatible = "brcm,iproc-udc", },
307	{ }
308};
309MODULE_DEVICE_TABLE(of, of_udc_match);
310
311static struct platform_driver udc_plat_driver = {
312	.probe		= udc_plat_probe,
313	.remove_new	= udc_plat_remove,
314	.driver		= {
315		.name	= "snps-udc-plat",
316		.of_match_table = of_udc_match,
317#ifdef CONFIG_PM_SLEEP
318		.pm	= &udc_plat_pm_ops,
319#endif
320	},
321};
322module_platform_driver(udc_plat_driver);
323
324MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
325MODULE_AUTHOR("Broadcom");
326MODULE_LICENSE("GPL v2");