linux/drivers/usb/dwc3/dwc3-haps.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/**
   3 * dwc3-haps.c - Synopsys HAPS PCI Specific glue layer
   4 *
   5 * Copyright (C) 2018 Synopsys, Inc.
   6 *
   7 * Authors: Thinh Nguyen <thinhn@synopsys.com>,
   8 *          John Youn <johnyoun@synopsys.com>
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/slab.h>
  14#include <linux/pci.h>
  15#include <linux/platform_device.h>
  16#include <linux/property.h>
  17
  18/**
  19 * struct dwc3_haps - Driver private structure
  20 * @dwc3: child dwc3 platform_device
  21 * @pci: our link to PCI bus
  22 */
  23struct dwc3_haps {
  24        struct platform_device *dwc3;
  25        struct pci_dev *pci;
  26};
  27
  28static const struct property_entry initial_properties[] = {
  29        PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"),
  30        PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
  31        PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"),
  32        PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
  33        { },
  34};
  35
  36static int dwc3_haps_probe(struct pci_dev *pci,
  37                           const struct pci_device_id *id)
  38{
  39        struct dwc3_haps        *dwc;
  40        struct device           *dev = &pci->dev;
  41        struct resource         res[2];
  42        int                     ret;
  43
  44        ret = pcim_enable_device(pci);
  45        if (ret) {
  46                dev_err(dev, "failed to enable pci device\n");
  47                return -ENODEV;
  48        }
  49
  50        pci_set_master(pci);
  51
  52        dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
  53        if (!dwc)
  54                return -ENOMEM;
  55
  56        dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
  57        if (!dwc->dwc3)
  58                return -ENOMEM;
  59
  60        memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
  61
  62        res[0].start    = pci_resource_start(pci, 0);
  63        res[0].end      = pci_resource_end(pci, 0);
  64        res[0].name     = "dwc_usb3";
  65        res[0].flags    = IORESOURCE_MEM;
  66
  67        res[1].start    = pci->irq;
  68        res[1].name     = "dwc_usb3";
  69        res[1].flags    = IORESOURCE_IRQ;
  70
  71        ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
  72        if (ret) {
  73                dev_err(dev, "couldn't add resources to dwc3 device\n");
  74                goto err;
  75        }
  76
  77        dwc->pci = pci;
  78        dwc->dwc3->dev.parent = dev;
  79
  80        ret = platform_device_add_properties(dwc->dwc3, initial_properties);
  81        if (ret)
  82                goto err;
  83
  84        ret = platform_device_add(dwc->dwc3);
  85        if (ret) {
  86                dev_err(dev, "failed to register dwc3 device\n");
  87                goto err;
  88        }
  89
  90        pci_set_drvdata(pci, dwc);
  91
  92        return 0;
  93err:
  94        platform_device_put(dwc->dwc3);
  95        return ret;
  96}
  97
  98static void dwc3_haps_remove(struct pci_dev *pci)
  99{
 100        struct dwc3_haps *dwc = pci_get_drvdata(pci);
 101
 102        platform_device_unregister(dwc->dwc3);
 103}
 104
 105static const struct pci_device_id dwc3_haps_id_table[] = {
 106        {
 107                PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
 108                           PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
 109                /*
 110                 * i.MX6QP and i.MX7D platform use a PCIe controller with the
 111                 * same VID and PID as this USB controller. The system may
 112                 * incorrectly match this driver to that PCIe controller. To
 113                 * workaround this, specifically use class type USB to prevent
 114                 * incorrect driver matching.
 115                 */
 116                .class = (PCI_CLASS_SERIAL_USB << 8),
 117                .class_mask = 0xffff00,
 118        },
 119        {
 120                PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
 121                           PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI),
 122        },
 123        {
 124                PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
 125                           PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31),
 126        },
 127        {  }    /* Terminating Entry */
 128};
 129MODULE_DEVICE_TABLE(pci, dwc3_haps_id_table);
 130
 131static struct pci_driver dwc3_haps_driver = {
 132        .name           = "dwc3-haps",
 133        .id_table       = dwc3_haps_id_table,
 134        .probe          = dwc3_haps_probe,
 135        .remove         = dwc3_haps_remove,
 136};
 137
 138MODULE_AUTHOR("Thinh Nguyen <thinhn@synopsys.com>");
 139MODULE_LICENSE("GPL v2");
 140MODULE_DESCRIPTION("Synopsys HAPS PCI Glue Layer");
 141
 142module_pci_driver(dwc3_haps_driver);
 143