162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Cadence PCIe platform  driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2019, Cadence Design Systems
662306a36Sopenharmony_ci * Author: Tom Joseph <tjoseph@cadence.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/of_pci.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1362306a36Sopenharmony_ci#include "pcie-cadence.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define CDNS_PLAT_CPU_TO_BUS_ADDR	0x0FFFFFFF
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * struct cdns_plat_pcie - private data for this PCIe platform driver
1962306a36Sopenharmony_ci * @pcie: Cadence PCIe controller
2062306a36Sopenharmony_ci * @is_rc: Set to 1 indicates the PCIe controller mode is Root Complex,
2162306a36Sopenharmony_ci *         if 0 it is in Endpoint mode.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistruct cdns_plat_pcie {
2462306a36Sopenharmony_ci	struct cdns_pcie        *pcie;
2562306a36Sopenharmony_ci	bool is_rc;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct cdns_plat_pcie_of_data {
2962306a36Sopenharmony_ci	bool is_rc;
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic const struct of_device_id cdns_plat_pcie_of_match[];
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	return cpu_addr & CDNS_PLAT_CPU_TO_BUS_ADDR;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct cdns_pcie_ops cdns_plat_ops = {
4062306a36Sopenharmony_ci	.cpu_addr_fixup = cdns_plat_cpu_addr_fixup,
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int cdns_plat_pcie_probe(struct platform_device *pdev)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	const struct cdns_plat_pcie_of_data *data;
4662306a36Sopenharmony_ci	struct cdns_plat_pcie *cdns_plat_pcie;
4762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
4862306a36Sopenharmony_ci	struct pci_host_bridge *bridge;
4962306a36Sopenharmony_ci	struct cdns_pcie_ep *ep;
5062306a36Sopenharmony_ci	struct cdns_pcie_rc *rc;
5162306a36Sopenharmony_ci	int phy_count;
5262306a36Sopenharmony_ci	bool is_rc;
5362306a36Sopenharmony_ci	int ret;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	data = of_device_get_match_data(dev);
5662306a36Sopenharmony_ci	if (!data)
5762306a36Sopenharmony_ci		return -EINVAL;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	is_rc = data->is_rc;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc);
6262306a36Sopenharmony_ci	cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL);
6362306a36Sopenharmony_ci	if (!cdns_plat_pcie)
6462306a36Sopenharmony_ci		return -ENOMEM;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	platform_set_drvdata(pdev, cdns_plat_pcie);
6762306a36Sopenharmony_ci	if (is_rc) {
6862306a36Sopenharmony_ci		if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST))
6962306a36Sopenharmony_ci			return -ENODEV;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc));
7262306a36Sopenharmony_ci		if (!bridge)
7362306a36Sopenharmony_ci			return -ENOMEM;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		rc = pci_host_bridge_priv(bridge);
7662306a36Sopenharmony_ci		rc->pcie.dev = dev;
7762306a36Sopenharmony_ci		rc->pcie.ops = &cdns_plat_ops;
7862306a36Sopenharmony_ci		cdns_plat_pcie->pcie = &rc->pcie;
7962306a36Sopenharmony_ci		cdns_plat_pcie->is_rc = is_rc;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
8262306a36Sopenharmony_ci		if (ret) {
8362306a36Sopenharmony_ci			dev_err(dev, "failed to init phy\n");
8462306a36Sopenharmony_ci			return ret;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci		pm_runtime_enable(dev);
8762306a36Sopenharmony_ci		ret = pm_runtime_get_sync(dev);
8862306a36Sopenharmony_ci		if (ret < 0) {
8962306a36Sopenharmony_ci			dev_err(dev, "pm_runtime_get_sync() failed\n");
9062306a36Sopenharmony_ci			goto err_get_sync;
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		ret = cdns_pcie_host_setup(rc);
9462306a36Sopenharmony_ci		if (ret)
9562306a36Sopenharmony_ci			goto err_init;
9662306a36Sopenharmony_ci	} else {
9762306a36Sopenharmony_ci		if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_EP))
9862306a36Sopenharmony_ci			return -ENODEV;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
10162306a36Sopenharmony_ci		if (!ep)
10262306a36Sopenharmony_ci			return -ENOMEM;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		ep->pcie.dev = dev;
10562306a36Sopenharmony_ci		ep->pcie.ops = &cdns_plat_ops;
10662306a36Sopenharmony_ci		cdns_plat_pcie->pcie = &ep->pcie;
10762306a36Sopenharmony_ci		cdns_plat_pcie->is_rc = is_rc;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
11062306a36Sopenharmony_ci		if (ret) {
11162306a36Sopenharmony_ci			dev_err(dev, "failed to init phy\n");
11262306a36Sopenharmony_ci			return ret;
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		pm_runtime_enable(dev);
11662306a36Sopenharmony_ci		ret = pm_runtime_get_sync(dev);
11762306a36Sopenharmony_ci		if (ret < 0) {
11862306a36Sopenharmony_ci			dev_err(dev, "pm_runtime_get_sync() failed\n");
11962306a36Sopenharmony_ci			goto err_get_sync;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		ret = cdns_pcie_ep_setup(ep);
12362306a36Sopenharmony_ci		if (ret)
12462306a36Sopenharmony_ci			goto err_init;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci err_init:
13062306a36Sopenharmony_ci err_get_sync:
13162306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
13262306a36Sopenharmony_ci	pm_runtime_disable(dev);
13362306a36Sopenharmony_ci	cdns_pcie_disable_phy(cdns_plat_pcie->pcie);
13462306a36Sopenharmony_ci	phy_count = cdns_plat_pcie->pcie->phy_count;
13562306a36Sopenharmony_ci	while (phy_count--)
13662306a36Sopenharmony_ci		device_link_del(cdns_plat_pcie->pcie->link[phy_count]);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void cdns_plat_pcie_shutdown(struct platform_device *pdev)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
14462306a36Sopenharmony_ci	struct cdns_pcie *pcie = dev_get_drvdata(dev);
14562306a36Sopenharmony_ci	int ret;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ret = pm_runtime_put_sync(dev);
14862306a36Sopenharmony_ci	if (ret < 0)
14962306a36Sopenharmony_ci		dev_dbg(dev, "pm_runtime_put_sync failed\n");
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	pm_runtime_disable(dev);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	cdns_pcie_disable_phy(pcie);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic const struct cdns_plat_pcie_of_data cdns_plat_pcie_host_of_data = {
15762306a36Sopenharmony_ci	.is_rc = true,
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct cdns_plat_pcie_of_data cdns_plat_pcie_ep_of_data = {
16162306a36Sopenharmony_ci	.is_rc = false,
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic const struct of_device_id cdns_plat_pcie_of_match[] = {
16562306a36Sopenharmony_ci	{
16662306a36Sopenharmony_ci		.compatible = "cdns,cdns-pcie-host",
16762306a36Sopenharmony_ci		.data = &cdns_plat_pcie_host_of_data,
16862306a36Sopenharmony_ci	},
16962306a36Sopenharmony_ci	{
17062306a36Sopenharmony_ci		.compatible = "cdns,cdns-pcie-ep",
17162306a36Sopenharmony_ci		.data = &cdns_plat_pcie_ep_of_data,
17262306a36Sopenharmony_ci	},
17362306a36Sopenharmony_ci	{},
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic struct platform_driver cdns_plat_pcie_driver = {
17762306a36Sopenharmony_ci	.driver = {
17862306a36Sopenharmony_ci		.name = "cdns-pcie",
17962306a36Sopenharmony_ci		.of_match_table = cdns_plat_pcie_of_match,
18062306a36Sopenharmony_ci		.pm	= &cdns_pcie_pm_ops,
18162306a36Sopenharmony_ci	},
18262306a36Sopenharmony_ci	.probe = cdns_plat_pcie_probe,
18362306a36Sopenharmony_ci	.shutdown = cdns_plat_pcie_shutdown,
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_cibuiltin_platform_driver(cdns_plat_pcie_driver);
186