162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cadence USBSS PCI Glue driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018-2019 Cadence. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Pawel Laszczak <pawell@cadence.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct cdns3_wrap { 1862306a36Sopenharmony_ci struct platform_device *plat_dev; 1962306a36Sopenharmony_ci struct resource dev_res[6]; 2062306a36Sopenharmony_ci int devfn; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define RES_IRQ_HOST_ID 0 2462306a36Sopenharmony_ci#define RES_IRQ_PERIPHERAL_ID 1 2562306a36Sopenharmony_ci#define RES_IRQ_OTG_ID 2 2662306a36Sopenharmony_ci#define RES_HOST_ID 3 2762306a36Sopenharmony_ci#define RES_DEV_ID 4 2862306a36Sopenharmony_ci#define RES_DRD_ID 5 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define PCI_BAR_HOST 0 3162306a36Sopenharmony_ci#define PCI_BAR_DEV 2 3262306a36Sopenharmony_ci#define PCI_BAR_OTG 0 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define PCI_DEV_FN_HOST_DEVICE 0 3562306a36Sopenharmony_ci#define PCI_DEV_FN_OTG 1 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define PCI_DRIVER_NAME "cdns3-pci-usbss" 3862306a36Sopenharmony_ci#define PLAT_DRIVER_NAME "cdns-usb3" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define CDNS_VENDOR_ID 0x17cd 4162306a36Sopenharmony_ci#define CDNS_DEVICE_ID 0x0100 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct pci_dev *func; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* 4862306a36Sopenharmony_ci * Gets the second function. 4962306a36Sopenharmony_ci * It's little tricky, but this platform has two function. 5062306a36Sopenharmony_ci * The fist keeps resources for Host/Device while the second 5162306a36Sopenharmony_ci * keeps resources for DRD/OTG. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci func = pci_get_device(pdev->vendor, pdev->device, NULL); 5462306a36Sopenharmony_ci if (unlikely(!func)) 5562306a36Sopenharmony_ci return NULL; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (func->devfn == pdev->devfn) { 5862306a36Sopenharmony_ci func = pci_get_device(pdev->vendor, pdev->device, func); 5962306a36Sopenharmony_ci if (unlikely(!func)) 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (func->devfn != PCI_DEV_FN_HOST_DEVICE && 6462306a36Sopenharmony_ci func->devfn != PCI_DEV_FN_OTG) { 6562306a36Sopenharmony_ci return NULL; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return func; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int cdns3_pci_probe(struct pci_dev *pdev, 7262306a36Sopenharmony_ci const struct pci_device_id *id) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct platform_device_info plat_info; 7562306a36Sopenharmony_ci struct cdns3_wrap *wrap; 7662306a36Sopenharmony_ci struct resource *res; 7762306a36Sopenharmony_ci struct pci_dev *func; 7862306a36Sopenharmony_ci int err; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * for GADGET/HOST PCI (devfn) function number is 0, 8262306a36Sopenharmony_ci * for OTG PCI (devfn) function number is 1 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE && 8562306a36Sopenharmony_ci pdev->devfn != PCI_DEV_FN_OTG)) 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci func = cdns3_get_second_fun(pdev); 8962306a36Sopenharmony_ci if (unlikely(!func)) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci err = pcim_enable_device(pdev); 9362306a36Sopenharmony_ci if (err) { 9462306a36Sopenharmony_ci dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err); 9562306a36Sopenharmony_ci return err; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci pci_set_master(pdev); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (pci_is_enabled(func)) { 10162306a36Sopenharmony_ci wrap = pci_get_drvdata(func); 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci wrap = kzalloc(sizeof(*wrap), GFP_KERNEL); 10462306a36Sopenharmony_ci if (!wrap) { 10562306a36Sopenharmony_ci pci_disable_device(pdev); 10662306a36Sopenharmony_ci return -ENOMEM; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci res = wrap->dev_res; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) { 11362306a36Sopenharmony_ci /* function 0: host(BAR_0) + device(BAR_1).*/ 11462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Initialize Device resources\n"); 11562306a36Sopenharmony_ci res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV); 11662306a36Sopenharmony_ci res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV); 11762306a36Sopenharmony_ci res[RES_DEV_ID].name = "dev"; 11862306a36Sopenharmony_ci res[RES_DEV_ID].flags = IORESOURCE_MEM; 11962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n", 12062306a36Sopenharmony_ci &res[RES_DEV_ID].start); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST); 12362306a36Sopenharmony_ci res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST); 12462306a36Sopenharmony_ci res[RES_HOST_ID].name = "xhci"; 12562306a36Sopenharmony_ci res[RES_HOST_ID].flags = IORESOURCE_MEM; 12662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n", 12762306a36Sopenharmony_ci &res[RES_HOST_ID].start); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Interrupt for XHCI */ 13062306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq; 13162306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_HOST_ID].name = "host"; 13262306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Interrupt device. It's the same as for HOST. */ 13562306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq; 13662306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral"; 13762306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ; 13862306a36Sopenharmony_ci } else { 13962306a36Sopenharmony_ci res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG); 14062306a36Sopenharmony_ci res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG); 14162306a36Sopenharmony_ci res[RES_DRD_ID].name = "otg"; 14262306a36Sopenharmony_ci res[RES_DRD_ID].flags = IORESOURCE_MEM; 14362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n", 14462306a36Sopenharmony_ci &res[RES_DRD_ID].start); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Interrupt for OTG/DRD. */ 14762306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq; 14862306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_OTG_ID].name = "otg"; 14962306a36Sopenharmony_ci wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (pci_is_enabled(func)) { 15362306a36Sopenharmony_ci /* set up platform device info */ 15462306a36Sopenharmony_ci memset(&plat_info, 0, sizeof(plat_info)); 15562306a36Sopenharmony_ci plat_info.parent = &pdev->dev; 15662306a36Sopenharmony_ci plat_info.fwnode = pdev->dev.fwnode; 15762306a36Sopenharmony_ci plat_info.name = PLAT_DRIVER_NAME; 15862306a36Sopenharmony_ci plat_info.id = pdev->devfn; 15962306a36Sopenharmony_ci wrap->devfn = pdev->devfn; 16062306a36Sopenharmony_ci plat_info.res = wrap->dev_res; 16162306a36Sopenharmony_ci plat_info.num_res = ARRAY_SIZE(wrap->dev_res); 16262306a36Sopenharmony_ci plat_info.dma_mask = pdev->dma_mask; 16362306a36Sopenharmony_ci /* register platform device */ 16462306a36Sopenharmony_ci wrap->plat_dev = platform_device_register_full(&plat_info); 16562306a36Sopenharmony_ci if (IS_ERR(wrap->plat_dev)) { 16662306a36Sopenharmony_ci pci_disable_device(pdev); 16762306a36Sopenharmony_ci err = PTR_ERR(wrap->plat_dev); 16862306a36Sopenharmony_ci kfree(wrap); 16962306a36Sopenharmony_ci return err; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pci_set_drvdata(pdev, wrap); 17462306a36Sopenharmony_ci return err; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void cdns3_pci_remove(struct pci_dev *pdev) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct cdns3_wrap *wrap; 18062306a36Sopenharmony_ci struct pci_dev *func; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci func = cdns3_get_second_fun(pdev); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev); 18562306a36Sopenharmony_ci if (wrap->devfn == pdev->devfn) 18662306a36Sopenharmony_ci platform_device_unregister(wrap->plat_dev); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!pci_is_enabled(func)) 18962306a36Sopenharmony_ci kfree(wrap); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct pci_device_id cdns3_pci_ids[] = { 19362306a36Sopenharmony_ci { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, 19462306a36Sopenharmony_ci { 0, } 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct pci_driver cdns3_pci_driver = { 19862306a36Sopenharmony_ci .name = PCI_DRIVER_NAME, 19962306a36Sopenharmony_ci .id_table = cdns3_pci_ids, 20062306a36Sopenharmony_ci .probe = cdns3_pci_probe, 20162306a36Sopenharmony_ci .remove = cdns3_pci_remove, 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cimodule_pci_driver(cdns3_pci_driver); 20562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cdns3_pci_ids); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciMODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>"); 20862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 20962306a36Sopenharmony_ciMODULE_DESCRIPTION("Cadence USBSS PCI wrapper"); 210