cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");