linux/drivers/usb/cdns3/cdns3-pci-wrap.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Cadence USBSS PCI Glue driver
   4 *
   5 * Copyright (C) 2018-2019 Cadence.
   6 *
   7 * Author: Pawel Laszczak <pawell@cadence.com>
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/pci.h>
  13#include <linux/platform_device.h>
  14#include <linux/dma-mapping.h>
  15#include <linux/slab.h>
  16
  17struct cdns3_wrap {
  18        struct platform_device *plat_dev;
  19        struct resource dev_res[6];
  20        int devfn;
  21};
  22
  23#define RES_IRQ_HOST_ID         0
  24#define RES_IRQ_PERIPHERAL_ID   1
  25#define RES_IRQ_OTG_ID          2
  26#define RES_HOST_ID             3
  27#define RES_DEV_ID              4
  28#define RES_DRD_ID              5
  29
  30#define PCI_BAR_HOST            0
  31#define PCI_BAR_DEV             2
  32#define PCI_BAR_OTG             0
  33
  34#define PCI_DEV_FN_HOST_DEVICE  0
  35#define PCI_DEV_FN_OTG          1
  36
  37#define PCI_DRIVER_NAME         "cdns3-pci-usbss"
  38#define PLAT_DRIVER_NAME        "cdns-usb3"
  39
  40#define CDNS_VENDOR_ID          0x17cd
  41#define CDNS_DEVICE_ID          0x0100
  42
  43static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
  44{
  45        struct pci_dev *func;
  46
  47        /*
  48         * Gets the second function.
  49         * It's little tricky, but this platform has two function.
  50         * The fist keeps resources for Host/Device while the second
  51         * keeps resources for DRD/OTG.
  52         */
  53        func = pci_get_device(pdev->vendor, pdev->device, NULL);
  54        if (unlikely(!func))
  55                return NULL;
  56
  57        if (func->devfn == pdev->devfn) {
  58                func = pci_get_device(pdev->vendor, pdev->device, func);
  59                if (unlikely(!func))
  60                        return NULL;
  61        }
  62
  63        return func;
  64}
  65
  66static int cdns3_pci_probe(struct pci_dev *pdev,
  67                           const struct pci_device_id *id)
  68{
  69        struct platform_device_info plat_info;
  70        struct cdns3_wrap *wrap;
  71        struct resource *res;
  72        struct pci_dev *func;
  73        int err;
  74
  75        /*
  76         * for GADGET/HOST PCI (devfn) function number is 0,
  77         * for OTG PCI (devfn) function number is 1
  78         */
  79        if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
  80                    pdev->devfn != PCI_DEV_FN_OTG))
  81                return -EINVAL;
  82
  83        func = cdns3_get_second_fun(pdev);
  84        if (unlikely(!func))
  85                return -EINVAL;
  86
  87        err = pcim_enable_device(pdev);
  88        if (err) {
  89                dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
  90                return err;
  91        }
  92
  93        pci_set_master(pdev);
  94
  95        if (pci_is_enabled(func)) {
  96                wrap = pci_get_drvdata(func);
  97        } else {
  98                wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
  99                if (!wrap) {
 100                        pci_disable_device(pdev);
 101                        return -ENOMEM;
 102                }
 103        }
 104
 105        res = wrap->dev_res;
 106
 107        if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
 108                /* function 0: host(BAR_0) + device(BAR_1).*/
 109                dev_dbg(&pdev->dev, "Initialize Device resources\n");
 110                res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
 111                res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
 112                res[RES_DEV_ID].name = "dev";
 113                res[RES_DEV_ID].flags = IORESOURCE_MEM;
 114                dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
 115                        &res[RES_DEV_ID].start);
 116
 117                res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
 118                res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
 119                res[RES_HOST_ID].name = "xhci";
 120                res[RES_HOST_ID].flags = IORESOURCE_MEM;
 121                dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
 122                        &res[RES_HOST_ID].start);
 123
 124                /* Interrupt for XHCI */
 125                wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
 126                wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
 127                wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
 128
 129                /* Interrupt device. It's the same as for HOST. */
 130                wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
 131                wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
 132                wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
 133        } else {
 134                res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
 135                res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
 136                res[RES_DRD_ID].name = "otg";
 137                res[RES_DRD_ID].flags = IORESOURCE_MEM;
 138                dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
 139                        &res[RES_DRD_ID].start);
 140
 141                /* Interrupt for OTG/DRD. */
 142                wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
 143                wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
 144                wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
 145        }
 146
 147        if (pci_is_enabled(func)) {
 148                /* set up platform device info */
 149                memset(&plat_info, 0, sizeof(plat_info));
 150                plat_info.parent = &pdev->dev;
 151                plat_info.fwnode = pdev->dev.fwnode;
 152                plat_info.name = PLAT_DRIVER_NAME;
 153                plat_info.id = pdev->devfn;
 154                wrap->devfn  = pdev->devfn;
 155                plat_info.res = wrap->dev_res;
 156                plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
 157                plat_info.dma_mask = pdev->dma_mask;
 158                /* register platform device */
 159                wrap->plat_dev = platform_device_register_full(&plat_info);
 160                if (IS_ERR(wrap->plat_dev)) {
 161                        pci_disable_device(pdev);
 162                        err = PTR_ERR(wrap->plat_dev);
 163                        kfree(wrap);
 164                        return err;
 165                }
 166        }
 167
 168        pci_set_drvdata(pdev, wrap);
 169        return err;
 170}
 171
 172static void cdns3_pci_remove(struct pci_dev *pdev)
 173{
 174        struct cdns3_wrap *wrap;
 175        struct pci_dev *func;
 176
 177        func = cdns3_get_second_fun(pdev);
 178
 179        wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
 180        if (wrap->devfn == pdev->devfn)
 181                platform_device_unregister(wrap->plat_dev);
 182
 183        if (!pci_is_enabled(func))
 184                kfree(wrap);
 185}
 186
 187static const struct pci_device_id cdns3_pci_ids[] = {
 188        { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
 189        { 0, }
 190};
 191
 192static struct pci_driver cdns3_pci_driver = {
 193        .name = PCI_DRIVER_NAME,
 194        .id_table = cdns3_pci_ids,
 195        .probe = cdns3_pci_probe,
 196        .remove = cdns3_pci_remove,
 197};
 198
 199module_pci_driver(cdns3_pci_driver);
 200MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
 201
 202MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
 203MODULE_LICENSE("GPL v2");
 204MODULE_DESCRIPTION("Cadence USBSS PCI wrapper");
 205