mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-26 18:25:27 +07:00
229e368239
Now that the SPDX tag is in all USB files, that identifies the license in a specific and legally-defined manner. So the extra GPL text wording can be removed as it is no longer needed at all. This is done on a quest to remove the 700+ different ways that files in the kernel describe the GPL license text. And there's unneeded stuff like the address (sometimes incorrect) for the FSF which is never needed. No copyright headers or other non-license-description text was removed. Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Sylvain Lemieux <slemieux.tyco@gmail.com> Cc: Daniel Mack <daniel@zonque.org> Cc: Haojian Zhuang <haojian.zhuang@gmail.com> Cc: Michal Simek <michal.simek@xilinx.com> Cc: "Sören Brinkmann" <soren.brinkmann@xilinx.com> Cc: Raviteja Garimella <raviteja.garimella@broadcom.com> Cc: Romain Perier <romain.perier@collabora.com> Cc: Johan Hovold <johan@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Srinath Mannam <srinath.mannam@broadcom.com> Cc: Roger Quadros <rogerq@ti.com> Cc: Krzysztof Opasiak <k.opasiak@samsung.com> Cc: Stefan Agner <stefan@agner.ch> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: "Felix Hädicke" <felixhaedicke@web.de> Cc: Peter Chen <peter.chen@nxp.com> Cc: Allen Pais <allen.lkml@gmail.com> Cc: Yuyang Du <yuyang.du@intel.com> Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com> Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com> Acked-by: Robert Jarzmik <robert.jarzmik@free.fr> Acked-by: Li Yang <leoyang.li@nxp.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
337 lines
7.1 KiB
C
337 lines
7.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* snps_udc_plat.c - Synopsys UDC Platform Driver
|
|
*
|
|
* Copyright (C) 2016 Broadcom
|
|
*/
|
|
|
|
#include <linux/extcon.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/phy/phy.h>
|
|
#include <linux/module.h>
|
|
#include <linux/dmapool.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/moduleparam.h>
|
|
#include "amd5536udc.h"
|
|
|
|
/* description */
|
|
#define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver"
|
|
|
|
static void start_udc(struct udc *udc)
|
|
{
|
|
if (udc->driver) {
|
|
dev_info(udc->dev, "Connecting...\n");
|
|
udc_enable_dev_setup_interrupts(udc);
|
|
udc_basic_init(udc);
|
|
udc->connected = 1;
|
|
}
|
|
}
|
|
|
|
static void stop_udc(struct udc *udc)
|
|
{
|
|
int tmp;
|
|
u32 reg;
|
|
|
|
spin_lock(&udc->lock);
|
|
|
|
/* Flush the receieve fifo */
|
|
reg = readl(&udc->regs->ctl);
|
|
reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
|
|
writel(reg, &udc->regs->ctl);
|
|
|
|
reg = readl(&udc->regs->ctl);
|
|
reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
|
|
writel(reg, &udc->regs->ctl);
|
|
dev_dbg(udc->dev, "ep rx queue flushed\n");
|
|
|
|
/* Mask interrupts. Required more so when the
|
|
* UDC is connected to a DRD phy.
|
|
*/
|
|
udc_mask_unused_interrupts(udc);
|
|
|
|
/* Disconnect gadget driver */
|
|
if (udc->driver) {
|
|
spin_unlock(&udc->lock);
|
|
udc->driver->disconnect(&udc->gadget);
|
|
spin_lock(&udc->lock);
|
|
|
|
/* empty queues */
|
|
for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
|
|
empty_req_queue(&udc->ep[tmp]);
|
|
}
|
|
udc->connected = 0;
|
|
|
|
spin_unlock(&udc->lock);
|
|
dev_info(udc->dev, "Device disconnected\n");
|
|
}
|
|
|
|
static void udc_drd_work(struct work_struct *work)
|
|
{
|
|
struct udc *udc;
|
|
|
|
udc = container_of(to_delayed_work(work),
|
|
struct udc, drd_work);
|
|
|
|
if (udc->conn_type) {
|
|
dev_dbg(udc->dev, "idle -> device\n");
|
|
start_udc(udc);
|
|
} else {
|
|
dev_dbg(udc->dev, "device -> idle\n");
|
|
stop_udc(udc);
|
|
}
|
|
}
|
|
|
|
static int usbd_connect_notify(struct notifier_block *self,
|
|
unsigned long event, void *ptr)
|
|
{
|
|
struct udc *udc = container_of(self, struct udc, nb);
|
|
|
|
dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
|
|
|
|
udc->conn_type = event;
|
|
|
|
schedule_delayed_work(&udc->drd_work, 0);
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static int udc_plat_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct resource *res;
|
|
struct udc *udc;
|
|
int ret;
|
|
|
|
udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
|
|
if (!udc)
|
|
return -ENOMEM;
|
|
|
|
spin_lock_init(&udc->lock);
|
|
udc->dev = dev;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
udc->virt_addr = devm_ioremap_resource(dev, res);
|
|
if (IS_ERR(udc->regs))
|
|
return PTR_ERR(udc->regs);
|
|
|
|
/* udc csr registers base */
|
|
udc->csr = udc->virt_addr + UDC_CSR_ADDR;
|
|
|
|
/* dev registers base */
|
|
udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
|
|
|
|
/* ep registers base */
|
|
udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
|
|
|
|
/* fifo's base */
|
|
udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
|
|
udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
|
|
|
|
udc->phys_addr = (unsigned long)res->start;
|
|
|
|
udc->irq = irq_of_parse_and_map(dev->of_node, 0);
|
|
if (udc->irq <= 0) {
|
|
dev_err(dev, "Can't parse and map interrupt\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
|
|
if (IS_ERR(udc->udc_phy)) {
|
|
dev_err(dev, "Failed to obtain phy from device tree\n");
|
|
return PTR_ERR(udc->udc_phy);
|
|
}
|
|
|
|
ret = phy_init(udc->udc_phy);
|
|
if (ret) {
|
|
dev_err(dev, "UDC phy init failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = phy_power_on(udc->udc_phy);
|
|
if (ret) {
|
|
dev_err(dev, "UDC phy power on failed");
|
|
phy_exit(udc->udc_phy);
|
|
return ret;
|
|
}
|
|
|
|
/* Register for extcon if supported */
|
|
if (of_get_property(dev->of_node, "extcon", NULL)) {
|
|
udc->edev = extcon_get_edev_by_phandle(dev, 0);
|
|
if (IS_ERR(udc->edev)) {
|
|
if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
|
|
return -EPROBE_DEFER;
|
|
dev_err(dev, "Invalid or missing extcon\n");
|
|
ret = PTR_ERR(udc->edev);
|
|
goto exit_phy;
|
|
}
|
|
|
|
udc->nb.notifier_call = usbd_connect_notify;
|
|
ret = extcon_register_notifier(udc->edev, EXTCON_USB,
|
|
&udc->nb);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Can't register extcon device\n");
|
|
goto exit_phy;
|
|
}
|
|
|
|
ret = extcon_get_state(udc->edev, EXTCON_USB);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Can't get cable state\n");
|
|
goto exit_extcon;
|
|
} else if (ret) {
|
|
udc->conn_type = ret;
|
|
}
|
|
INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
|
|
}
|
|
|
|
/* init dma pools */
|
|
if (use_dma) {
|
|
ret = init_dma_pools(udc);
|
|
if (ret != 0)
|
|
goto exit_extcon;
|
|
}
|
|
|
|
ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
|
|
"snps-udc", udc);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
|
|
goto exit_dma;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, udc);
|
|
udc->chiprev = UDC_BCM_REV;
|
|
|
|
if (udc_probe(udc)) {
|
|
ret = -ENODEV;
|
|
goto exit_dma;
|
|
}
|
|
dev_info(dev, "Synopsys UDC platform driver probe successful\n");
|
|
|
|
return 0;
|
|
|
|
exit_dma:
|
|
if (use_dma)
|
|
free_dma_pools(udc);
|
|
exit_extcon:
|
|
if (udc->edev)
|
|
extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
|
|
exit_phy:
|
|
if (udc->udc_phy) {
|
|
phy_power_off(udc->udc_phy);
|
|
phy_exit(udc->udc_phy);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int udc_plat_remove(struct platform_device *pdev)
|
|
{
|
|
struct udc *dev;
|
|
|
|
dev = platform_get_drvdata(pdev);
|
|
|
|
usb_del_gadget_udc(&dev->gadget);
|
|
/* gadget driver must not be registered */
|
|
if (WARN_ON(dev->driver))
|
|
return 0;
|
|
|
|
/* dma pool cleanup */
|
|
free_dma_pools(dev);
|
|
|
|
udc_remove(dev);
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
if (dev->drd_wq) {
|
|
flush_workqueue(dev->drd_wq);
|
|
destroy_workqueue(dev->drd_wq);
|
|
}
|
|
|
|
phy_power_off(dev->udc_phy);
|
|
phy_exit(dev->udc_phy);
|
|
extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
|
|
|
|
dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int udc_plat_suspend(struct device *dev)
|
|
{
|
|
struct udc *udc;
|
|
|
|
udc = dev_get_drvdata(dev);
|
|
stop_udc(udc);
|
|
|
|
if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
|
|
dev_dbg(udc->dev, "device -> idle\n");
|
|
stop_udc(udc);
|
|
}
|
|
phy_power_off(udc->udc_phy);
|
|
phy_exit(udc->udc_phy);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int udc_plat_resume(struct device *dev)
|
|
{
|
|
struct udc *udc;
|
|
int ret;
|
|
|
|
udc = dev_get_drvdata(dev);
|
|
|
|
ret = phy_init(udc->udc_phy);
|
|
if (ret) {
|
|
dev_err(udc->dev, "UDC phy init failure");
|
|
return ret;
|
|
}
|
|
|
|
ret = phy_power_on(udc->udc_phy);
|
|
if (ret) {
|
|
dev_err(udc->dev, "UDC phy power on failure");
|
|
phy_exit(udc->udc_phy);
|
|
return ret;
|
|
}
|
|
|
|
if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
|
|
dev_dbg(udc->dev, "idle -> device\n");
|
|
start_udc(udc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static const struct dev_pm_ops udc_plat_pm_ops = {
|
|
.suspend = udc_plat_suspend,
|
|
.resume = udc_plat_resume,
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONFIG_OF)
|
|
static const struct of_device_id of_udc_match[] = {
|
|
{ .compatible = "brcm,ns2-udc", },
|
|
{ .compatible = "brcm,cygnus-udc", },
|
|
{ .compatible = "brcm,iproc-udc", },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, of_udc_match);
|
|
#endif
|
|
|
|
static struct platform_driver udc_plat_driver = {
|
|
.probe = udc_plat_probe,
|
|
.remove = udc_plat_remove,
|
|
.driver = {
|
|
.name = "snps-udc-plat",
|
|
.of_match_table = of_match_ptr(of_udc_match),
|
|
#ifdef CONFIG_PM_SLEEP
|
|
.pm = &udc_plat_pm_ops,
|
|
#endif
|
|
},
|
|
};
|
|
module_platform_driver(udc_plat_driver);
|
|
|
|
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
|
|
MODULE_AUTHOR("Broadcom");
|
|
MODULE_LICENSE("GPL v2");
|