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 const struct software_node dwc3_haps_swnode = {
  37        .properties = initial_properties,
  38};
  39
  40static int dwc3_haps_probe(struct pci_dev *pci,
  41                           const struct pci_device_id *id)
  42{
  43        struct dwc3_haps        *dwc;
  44        struct device           *dev = &pci->dev;
  45        struct resource         res[2];
  46        int                     ret;
  47
  48        ret = pcim_enable_device(pci);
  49        if (ret) {
  50                dev_err(dev, "failed to enable pci device\n");
  51                return -ENODEV;
  52        }
  53
  54        pci_set_master(pci);
  55
  56        dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
  57        if (!dwc)
  58                return -ENOMEM;
  59
  60        dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
  61        if (!dwc->dwc3)
  62                return -ENOMEM;
  63
  64        memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
  65
  66        res[0].start    = pci_resource_start(pci, 0);
  67        res[0].end      = pci_resource_end(pci, 0);
  68        res[0].name     = "dwc_usb3";
  69        res[0].flags    = IORESOURCE_MEM;
  70
  71        res[1].start    = pci->irq;
  72        res[1].name     = "dwc_usb3";
  73        res[1].flags    = IORESOURCE_IRQ;
  74
  75        ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
  76        if (ret) {
  77                dev_err(dev, "couldn't add resources to dwc3 device\n");
  78                goto err;
  79        }
  80
  81        dwc->pci = pci;
  82        dwc->dwc3->dev.parent = dev;
  83
  84        ret = device_add_software_node(&dwc->dwc3->dev, &dwc3_haps_swnode);
  85        if (ret)
  86                goto err;
  87
  88        ret = platform_device_add(dwc->dwc3);
  89        if (ret) {
  90                dev_err(dev, "failed to register dwc3 device\n");
  91                goto err;
  92        }
  93
  94        pci_set_drvdata(pci, dwc);
  95
  96        return 0;
  97err:
  98        device_remove_software_node(&dwc->dwc3->dev);
  99        platform_device_put(dwc->dwc3);
 100        return ret;
 101}
 102
 103static void dwc3_haps_remove(struct pci_dev *pci)
 104{
 105        struct dwc3_haps *dwc = pci_get_drvdata(pci);
 106
 107        device_remove_software_node(&dwc->dwc3->dev);
 108        platform_device_unregister(dwc->dwc3);
 109}
 110
 111static const struct pci_device_id dwc3_haps_id_table[] = {
 112        {
 113                PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
 114                           PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
 115                /*
 116                 * i.MX6QP and i.MX7D platform use a PCIe controller with the
 117                 * same VID and PID as this USB controller. The system may
 118                 * incorrectly match this driver to that PCIe controller. To
 119                 * workaround this, specifically use class type USB to prevent
 120                 * incorrect driver matching.
 121                 */
 122                .class = (PCI_CLASS_SERIAL_USB << 8),
 123                .class_mask = 0xffff00,
 124        },
 125        {
 126                PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
 127                           PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI),
 128        },
 129        {
 130                PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
 131                           PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31),
 132        },
 133        {  }    /* Terminating Entry */
 134};
 135MODULE_DEVICE_TABLE(pci, dwc3_haps_id_table);
 136
 137static struct pci_driver dwc3_haps_driver = {
 138        .name           = "dwc3-haps",
 139        .id_table       = dwc3_haps_id_table,
 140        .probe          = dwc3_haps_probe,
 141        .remove         = dwc3_haps_remove,
 142};
 143
 144MODULE_AUTHOR("Thinh Nguyen <thinhn@synopsys.com>");
 145MODULE_LICENSE("GPL v2");
 146MODULE_DESCRIPTION("Synopsys HAPS PCI Glue Layer");
 147
 148module_pci_driver(dwc3_haps_driver);
 149