162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Cadence PCI Glue driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019 Cadence.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Pawel Laszczak <pawell@cadence.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "core.h"
1962306a36Sopenharmony_ci#include "gadget-export.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PCI_BAR_HOST		0
2262306a36Sopenharmony_ci#define PCI_BAR_OTG		0
2362306a36Sopenharmony_ci#define PCI_BAR_DEV		2
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define PCI_DEV_FN_HOST_DEVICE	0
2662306a36Sopenharmony_ci#define PCI_DEV_FN_OTG		1
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define PCI_DRIVER_NAME		"cdns-pci-usbssp"
2962306a36Sopenharmony_ci#define PLAT_DRIVER_NAME	"cdns-usbssp"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define CDNS_VENDOR_ID		0x17cd
3262306a36Sopenharmony_ci#define CDNS_DEVICE_ID		0x0200
3362306a36Sopenharmony_ci#define CDNS_DRD_ID		0x0100
3462306a36Sopenharmony_ci#define CDNS_DRD_IF		(PCI_CLASS_SERIAL_USB << 8 | 0x80)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	/*
3962306a36Sopenharmony_ci	 * Gets the second function.
4062306a36Sopenharmony_ci	 * Platform has two function. The fist keeps resources for
4162306a36Sopenharmony_ci	 * Host/Device while the secon keeps resources for DRD/OTG.
4262306a36Sopenharmony_ci	 */
4362306a36Sopenharmony_ci	if (pdev->device == CDNS_DEVICE_ID)
4462306a36Sopenharmony_ci		return  pci_get_device(pdev->vendor, CDNS_DRD_ID, NULL);
4562306a36Sopenharmony_ci	else if (pdev->device == CDNS_DRD_ID)
4662306a36Sopenharmony_ci		return pci_get_device(pdev->vendor, CDNS_DEVICE_ID, NULL);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return NULL;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int cdnsp_pci_probe(struct pci_dev *pdev,
5262306a36Sopenharmony_ci			   const struct pci_device_id *id)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
5562306a36Sopenharmony_ci	struct pci_dev *func;
5662306a36Sopenharmony_ci	struct resource *res;
5762306a36Sopenharmony_ci	struct cdns *cdnsp;
5862306a36Sopenharmony_ci	int ret;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * For GADGET/HOST PCI (devfn) function number is 0,
6262306a36Sopenharmony_ci	 * for OTG PCI (devfn) function number is 1.
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
6562306a36Sopenharmony_ci		    pdev->devfn != PCI_DEV_FN_OTG))
6662306a36Sopenharmony_ci		return -EINVAL;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	func = cdnsp_get_second_fun(pdev);
6962306a36Sopenharmony_ci	if (!func)
7062306a36Sopenharmony_ci		return -EINVAL;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (func->class == PCI_CLASS_SERIAL_USB_XHCI ||
7362306a36Sopenharmony_ci	    pdev->class == PCI_CLASS_SERIAL_USB_XHCI) {
7462306a36Sopenharmony_ci		ret = -EINVAL;
7562306a36Sopenharmony_ci		goto put_pci;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
7962306a36Sopenharmony_ci	if (ret) {
8062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret);
8162306a36Sopenharmony_ci		goto put_pci;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	pci_set_master(pdev);
8562306a36Sopenharmony_ci	if (pci_is_enabled(func)) {
8662306a36Sopenharmony_ci		cdnsp = pci_get_drvdata(func);
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		cdnsp = kzalloc(sizeof(*cdnsp), GFP_KERNEL);
8962306a36Sopenharmony_ci		if (!cdnsp) {
9062306a36Sopenharmony_ci			ret = -ENOMEM;
9162306a36Sopenharmony_ci			goto disable_pci;
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* For GADGET device function number is 0. */
9662306a36Sopenharmony_ci	if (pdev->devfn == 0) {
9762306a36Sopenharmony_ci		resource_size_t rsrc_start, rsrc_len;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		/* Function 0: host(BAR_0) + device(BAR_1).*/
10062306a36Sopenharmony_ci		dev_dbg(dev, "Initialize resources\n");
10162306a36Sopenharmony_ci		rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
10262306a36Sopenharmony_ci		rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
10362306a36Sopenharmony_ci		res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
10462306a36Sopenharmony_ci		if (!res) {
10562306a36Sopenharmony_ci			dev_dbg(dev, "controller already in use\n");
10662306a36Sopenharmony_ci			ret = -EBUSY;
10762306a36Sopenharmony_ci			goto free_cdnsp;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		cdnsp->dev_regs = devm_ioremap(dev, rsrc_start, rsrc_len);
11162306a36Sopenharmony_ci		if (!cdnsp->dev_regs) {
11262306a36Sopenharmony_ci			dev_dbg(dev, "error mapping memory\n");
11362306a36Sopenharmony_ci			ret = -EFAULT;
11462306a36Sopenharmony_ci			goto free_cdnsp;
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		cdnsp->dev_irq = pdev->irq;
11862306a36Sopenharmony_ci		dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
11962306a36Sopenharmony_ci			&rsrc_start);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		res = &cdnsp->xhci_res[0];
12262306a36Sopenharmony_ci		res->start = pci_resource_start(pdev, PCI_BAR_HOST);
12362306a36Sopenharmony_ci		res->end = pci_resource_end(pdev, PCI_BAR_HOST);
12462306a36Sopenharmony_ci		res->name = "xhci";
12562306a36Sopenharmony_ci		res->flags = IORESOURCE_MEM;
12662306a36Sopenharmony_ci		dev_dbg(dev, "USBSS-XHCI physical base addr: %pa\n",
12762306a36Sopenharmony_ci			&res->start);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		/* Interrupt for XHCI, */
13062306a36Sopenharmony_ci		res = &cdnsp->xhci_res[1];
13162306a36Sopenharmony_ci		res->start = pdev->irq;
13262306a36Sopenharmony_ci		res->name = "host";
13362306a36Sopenharmony_ci		res->flags = IORESOURCE_IRQ;
13462306a36Sopenharmony_ci	} else {
13562306a36Sopenharmony_ci		res = &cdnsp->otg_res;
13662306a36Sopenharmony_ci		res->start = pci_resource_start(pdev, PCI_BAR_OTG);
13762306a36Sopenharmony_ci		res->end =   pci_resource_end(pdev, PCI_BAR_OTG);
13862306a36Sopenharmony_ci		res->name = "otg";
13962306a36Sopenharmony_ci		res->flags = IORESOURCE_MEM;
14062306a36Sopenharmony_ci		dev_dbg(dev, "CDNSP-DRD physical base addr: %pa\n",
14162306a36Sopenharmony_ci			&res->start);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		/* Interrupt for OTG/DRD. */
14462306a36Sopenharmony_ci		cdnsp->otg_irq = pdev->irq;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (pci_is_enabled(func)) {
14862306a36Sopenharmony_ci		cdnsp->dev = dev;
14962306a36Sopenharmony_ci		cdnsp->gadget_init = cdnsp_gadget_init;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		ret = cdns_init(cdnsp);
15262306a36Sopenharmony_ci		if (ret)
15362306a36Sopenharmony_ci			goto free_cdnsp;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	pci_set_drvdata(pdev, cdnsp);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	device_wakeup_enable(&pdev->dev);
15962306a36Sopenharmony_ci	if (pci_dev_run_wake(pdev))
16062306a36Sopenharmony_ci		pm_runtime_put_noidle(&pdev->dev);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cifree_cdnsp:
16562306a36Sopenharmony_ci	if (!pci_is_enabled(func))
16662306a36Sopenharmony_ci		kfree(cdnsp);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cidisable_pci:
16962306a36Sopenharmony_ci	pci_disable_device(pdev);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciput_pci:
17262306a36Sopenharmony_ci	pci_dev_put(func);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return ret;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void cdnsp_pci_remove(struct pci_dev *pdev)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct cdns *cdnsp;
18062306a36Sopenharmony_ci	struct pci_dev *func;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	func = cdnsp_get_second_fun(pdev);
18362306a36Sopenharmony_ci	cdnsp = (struct cdns *)pci_get_drvdata(pdev);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (pci_dev_run_wake(pdev))
18662306a36Sopenharmony_ci		pm_runtime_get_noresume(&pdev->dev);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (pci_is_enabled(func)) {
18962306a36Sopenharmony_ci		cdns_remove(cdnsp);
19062306a36Sopenharmony_ci	} else {
19162306a36Sopenharmony_ci		kfree(cdnsp);
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	pci_dev_put(func);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int __maybe_unused cdnsp_pci_suspend(struct device *dev)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct cdns *cdns = dev_get_drvdata(dev);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return cdns_suspend(cdns);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int __maybe_unused cdnsp_pci_resume(struct device *dev)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct cdns *cdns = dev_get_drvdata(dev);
20762306a36Sopenharmony_ci	unsigned long flags;
20862306a36Sopenharmony_ci	int ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	spin_lock_irqsave(&cdns->lock, flags);
21162306a36Sopenharmony_ci	ret = cdns_resume(cdns);
21262306a36Sopenharmony_ci	spin_unlock_irqrestore(&cdns->lock, flags);
21362306a36Sopenharmony_ci	cdns_set_active(cdns, 1);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return ret;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic const struct dev_pm_ops cdnsp_pci_pm_ops = {
21962306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(cdnsp_pci_suspend, cdnsp_pci_resume)
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic const struct pci_device_id cdnsp_pci_ids[] = {
22362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
22462306a36Sopenharmony_ci	  PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
22562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
22662306a36Sopenharmony_ci	  CDNS_DRD_IF, PCI_ANY_ID },
22762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CDNS, CDNS_DRD_ID, PCI_ANY_ID, PCI_ANY_ID,
22862306a36Sopenharmony_ci	  CDNS_DRD_IF, PCI_ANY_ID },
22962306a36Sopenharmony_ci	{ 0, }
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic struct pci_driver cdnsp_pci_driver = {
23362306a36Sopenharmony_ci	.name = "cdnsp-pci",
23462306a36Sopenharmony_ci	.id_table = &cdnsp_pci_ids[0],
23562306a36Sopenharmony_ci	.probe = cdnsp_pci_probe,
23662306a36Sopenharmony_ci	.remove = cdnsp_pci_remove,
23762306a36Sopenharmony_ci	.driver = {
23862306a36Sopenharmony_ci		.pm = &cdnsp_pci_pm_ops,
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci};
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cimodule_pci_driver(cdnsp_pci_driver);
24362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cdnsp_pci_ids);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciMODULE_ALIAS("pci:cdnsp");
24662306a36Sopenharmony_ciMODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
24762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
24862306a36Sopenharmony_ciMODULE_DESCRIPTION("Cadence CDNSP PCI driver");
249