123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Cadence PCI Glue driver.
- *
- * Copyright (C) 2019 Cadence.
- *
- * Author: Pawel Laszczak <[email protected]>
- *
- */
- #include <linux/platform_device.h>
- #include <linux/dma-mapping.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/pci.h>
- #include "core.h"
- #include "gadget-export.h"
- #define PCI_BAR_HOST 0
- #define PCI_BAR_OTG 0
- #define PCI_BAR_DEV 2
- #define PCI_DEV_FN_HOST_DEVICE 0
- #define PCI_DEV_FN_OTG 1
- #define PCI_DRIVER_NAME "cdns-pci-usbssp"
- #define PLAT_DRIVER_NAME "cdns-usbssp"
- #define CDNS_VENDOR_ID 0x17cd
- #define CDNS_DEVICE_ID 0x0200
- #define CDNS_DRD_ID 0x0100
- #define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80)
- static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
- {
- /*
- * Gets the second function.
- * Platform has two function. The fist keeps resources for
- * Host/Device while the secon keeps resources for DRD/OTG.
- */
- if (pdev->device == CDNS_DEVICE_ID)
- return pci_get_device(pdev->vendor, CDNS_DRD_ID, NULL);
- else if (pdev->device == CDNS_DRD_ID)
- return pci_get_device(pdev->vendor, CDNS_DEVICE_ID, NULL);
- return NULL;
- }
- static int cdnsp_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
- {
- struct device *dev = &pdev->dev;
- struct pci_dev *func;
- struct resource *res;
- struct cdns *cdnsp;
- int ret;
- /*
- * For GADGET/HOST PCI (devfn) function number is 0,
- * for OTG PCI (devfn) function number is 1.
- */
- if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
- pdev->devfn != PCI_DEV_FN_OTG))
- return -EINVAL;
- func = cdnsp_get_second_fun(pdev);
- if (!func)
- return -EINVAL;
- if (func->class == PCI_CLASS_SERIAL_USB_XHCI ||
- pdev->class == PCI_CLASS_SERIAL_USB_XHCI) {
- ret = -EINVAL;
- goto put_pci;
- }
- ret = pcim_enable_device(pdev);
- if (ret) {
- dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret);
- goto put_pci;
- }
- pci_set_master(pdev);
- if (pci_is_enabled(func)) {
- cdnsp = pci_get_drvdata(func);
- } else {
- cdnsp = kzalloc(sizeof(*cdnsp), GFP_KERNEL);
- if (!cdnsp) {
- ret = -ENOMEM;
- goto disable_pci;
- }
- }
- /* For GADGET device function number is 0. */
- if (pdev->devfn == 0) {
- resource_size_t rsrc_start, rsrc_len;
- /* Function 0: host(BAR_0) + device(BAR_1).*/
- dev_dbg(dev, "Initialize resources\n");
- rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
- rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
- res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
- if (!res) {
- dev_dbg(dev, "controller already in use\n");
- ret = -EBUSY;
- goto free_cdnsp;
- }
- cdnsp->dev_regs = devm_ioremap(dev, rsrc_start, rsrc_len);
- if (!cdnsp->dev_regs) {
- dev_dbg(dev, "error mapping memory\n");
- ret = -EFAULT;
- goto free_cdnsp;
- }
- cdnsp->dev_irq = pdev->irq;
- dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
- &rsrc_start);
- res = &cdnsp->xhci_res[0];
- res->start = pci_resource_start(pdev, PCI_BAR_HOST);
- res->end = pci_resource_end(pdev, PCI_BAR_HOST);
- res->name = "xhci";
- res->flags = IORESOURCE_MEM;
- dev_dbg(dev, "USBSS-XHCI physical base addr: %pa\n",
- &res->start);
- /* Interrupt for XHCI, */
- res = &cdnsp->xhci_res[1];
- res->start = pdev->irq;
- res->name = "host";
- res->flags = IORESOURCE_IRQ;
- } else {
- res = &cdnsp->otg_res;
- res->start = pci_resource_start(pdev, PCI_BAR_OTG);
- res->end = pci_resource_end(pdev, PCI_BAR_OTG);
- res->name = "otg";
- res->flags = IORESOURCE_MEM;
- dev_dbg(dev, "CDNSP-DRD physical base addr: %pa\n",
- &res->start);
- /* Interrupt for OTG/DRD. */
- cdnsp->otg_irq = pdev->irq;
- }
- if (pci_is_enabled(func)) {
- cdnsp->dev = dev;
- cdnsp->gadget_init = cdnsp_gadget_init;
- ret = cdns_init(cdnsp);
- if (ret)
- goto free_cdnsp;
- }
- pci_set_drvdata(pdev, cdnsp);
- device_wakeup_enable(&pdev->dev);
- if (pci_dev_run_wake(pdev))
- pm_runtime_put_noidle(&pdev->dev);
- return 0;
- free_cdnsp:
- if (!pci_is_enabled(func))
- kfree(cdnsp);
- disable_pci:
- pci_disable_device(pdev);
- put_pci:
- pci_dev_put(func);
- return ret;
- }
- static void cdnsp_pci_remove(struct pci_dev *pdev)
- {
- struct cdns *cdnsp;
- struct pci_dev *func;
- func = cdnsp_get_second_fun(pdev);
- cdnsp = (struct cdns *)pci_get_drvdata(pdev);
- if (pci_dev_run_wake(pdev))
- pm_runtime_get_noresume(&pdev->dev);
- if (!pci_is_enabled(func)) {
- kfree(cdnsp);
- goto pci_put;
- }
- cdns_remove(cdnsp);
- pci_put:
- pci_dev_put(func);
- }
- static int __maybe_unused cdnsp_pci_suspend(struct device *dev)
- {
- struct cdns *cdns = dev_get_drvdata(dev);
- return cdns_suspend(cdns);
- }
- static int __maybe_unused cdnsp_pci_resume(struct device *dev)
- {
- struct cdns *cdns = dev_get_drvdata(dev);
- unsigned long flags;
- int ret;
- spin_lock_irqsave(&cdns->lock, flags);
- ret = cdns_resume(cdns);
- spin_unlock_irqrestore(&cdns->lock, flags);
- cdns_set_active(cdns, 1);
- return ret;
- }
- static const struct dev_pm_ops cdnsp_pci_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(cdnsp_pci_suspend, cdnsp_pci_resume)
- };
- static const struct pci_device_id cdnsp_pci_ids[] = {
- { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
- { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
- CDNS_DRD_IF, PCI_ANY_ID },
- { PCI_VENDOR_ID_CDNS, CDNS_DRD_ID, PCI_ANY_ID, PCI_ANY_ID,
- CDNS_DRD_IF, PCI_ANY_ID },
- { 0, }
- };
- static struct pci_driver cdnsp_pci_driver = {
- .name = "cdnsp-pci",
- .id_table = &cdnsp_pci_ids[0],
- .probe = cdnsp_pci_probe,
- .remove = cdnsp_pci_remove,
- .driver = {
- .pm = &cdnsp_pci_pm_ops,
- }
- };
- module_pci_driver(cdnsp_pci_driver);
- MODULE_DEVICE_TABLE(pci, cdnsp_pci_ids);
- MODULE_ALIAS("pci:cdnsp");
- MODULE_AUTHOR("Pawel Laszczak <[email protected]>");
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("Cadence CDNSP PCI driver");
|