18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Broadcom Corporation
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/pci.h>
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/of_pci.h>
158c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
168c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
178c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "../pci.h"
208c2ecf20Sopenharmony_ci#include "pcie-iproc.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const struct of_device_id iproc_pcie_of_match_table[] = {
238c2ecf20Sopenharmony_ci	{
248c2ecf20Sopenharmony_ci		.compatible = "brcm,iproc-pcie",
258c2ecf20Sopenharmony_ci		.data = (int *)IPROC_PCIE_PAXB,
268c2ecf20Sopenharmony_ci	}, {
278c2ecf20Sopenharmony_ci		.compatible = "brcm,iproc-pcie-paxb-v2",
288c2ecf20Sopenharmony_ci		.data = (int *)IPROC_PCIE_PAXB_V2,
298c2ecf20Sopenharmony_ci	}, {
308c2ecf20Sopenharmony_ci		.compatible = "brcm,iproc-pcie-paxc",
318c2ecf20Sopenharmony_ci		.data = (int *)IPROC_PCIE_PAXC,
328c2ecf20Sopenharmony_ci	}, {
338c2ecf20Sopenharmony_ci		.compatible = "brcm,iproc-pcie-paxc-v2",
348c2ecf20Sopenharmony_ci		.data = (int *)IPROC_PCIE_PAXC_V2,
358c2ecf20Sopenharmony_ci	},
368c2ecf20Sopenharmony_ci	{ /* sentinel */ }
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int iproc_pcie_pltfm_probe(struct platform_device *pdev)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
438c2ecf20Sopenharmony_ci	struct iproc_pcie *pcie;
448c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
458c2ecf20Sopenharmony_ci	struct resource reg;
468c2ecf20Sopenharmony_ci	struct pci_host_bridge *bridge;
478c2ecf20Sopenharmony_ci	int ret;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
508c2ecf20Sopenharmony_ci	if (!bridge)
518c2ecf20Sopenharmony_ci		return -ENOMEM;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	pcie = pci_host_bridge_priv(bridge);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	pcie->dev = dev;
568c2ecf20Sopenharmony_ci	pcie->type = (enum iproc_pcie_type) of_device_get_match_data(dev);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	ret = of_address_to_resource(np, 0, &reg);
598c2ecf20Sopenharmony_ci	if (ret < 0) {
608c2ecf20Sopenharmony_ci		dev_err(dev, "unable to obtain controller resources\n");
618c2ecf20Sopenharmony_ci		return ret;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	pcie->base = devm_pci_remap_cfgspace(dev, reg.start,
658c2ecf20Sopenharmony_ci					     resource_size(&reg));
668c2ecf20Sopenharmony_ci	if (!pcie->base) {
678c2ecf20Sopenharmony_ci		dev_err(dev, "unable to map controller registers\n");
688c2ecf20Sopenharmony_ci		return -ENOMEM;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	pcie->base_addr = reg.start;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "brcm,pcie-ob")) {
738c2ecf20Sopenharmony_ci		u32 val;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
768c2ecf20Sopenharmony_ci					   &val);
778c2ecf20Sopenharmony_ci		if (ret) {
788c2ecf20Sopenharmony_ci			dev_err(dev,
798c2ecf20Sopenharmony_ci				"missing brcm,pcie-ob-axi-offset property\n");
808c2ecf20Sopenharmony_ci			return ret;
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci		pcie->ob.axi_offset = val;
838c2ecf20Sopenharmony_ci		pcie->need_ob_cfg = true;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/*
878c2ecf20Sopenharmony_ci	 * DT nodes are not used by all platforms that use the iProc PCIe
888c2ecf20Sopenharmony_ci	 * core driver. For platforms that require explicit inbound mapping
898c2ecf20Sopenharmony_ci	 * configuration, "dma-ranges" would have been present in DT
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci	pcie->need_ib_cfg = of_property_read_bool(np, "dma-ranges");
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* PHY use is optional */
948c2ecf20Sopenharmony_ci	pcie->phy = devm_phy_optional_get(dev, "pcie-phy");
958c2ecf20Sopenharmony_ci	if (IS_ERR(pcie->phy))
968c2ecf20Sopenharmony_ci		return PTR_ERR(pcie->phy);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* PAXC doesn't support legacy IRQs, skip mapping */
998c2ecf20Sopenharmony_ci	switch (pcie->type) {
1008c2ecf20Sopenharmony_ci	case IPROC_PCIE_PAXC:
1018c2ecf20Sopenharmony_ci	case IPROC_PCIE_PAXC_V2:
1028c2ecf20Sopenharmony_ci		pcie->map_irq = NULL;
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci	default:
1058c2ecf20Sopenharmony_ci		break;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ret = iproc_pcie_setup(pcie, &bridge->windows);
1098c2ecf20Sopenharmony_ci	if (ret) {
1108c2ecf20Sopenharmony_ci		dev_err(dev, "PCIe controller setup failed\n");
1118c2ecf20Sopenharmony_ci		return ret;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pcie);
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int iproc_pcie_pltfm_remove(struct platform_device *pdev)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct iproc_pcie *pcie = platform_get_drvdata(pdev);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return iproc_pcie_remove(pcie);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void iproc_pcie_pltfm_shutdown(struct platform_device *pdev)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct iproc_pcie *pcie = platform_get_drvdata(pdev);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	iproc_pcie_shutdown(pcie);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic struct platform_driver iproc_pcie_pltfm_driver = {
1338c2ecf20Sopenharmony_ci	.driver = {
1348c2ecf20Sopenharmony_ci		.name = "iproc-pcie",
1358c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(iproc_pcie_of_match_table),
1368c2ecf20Sopenharmony_ci	},
1378c2ecf20Sopenharmony_ci	.probe = iproc_pcie_pltfm_probe,
1388c2ecf20Sopenharmony_ci	.remove = iproc_pcie_pltfm_remove,
1398c2ecf20Sopenharmony_ci	.shutdown = iproc_pcie_pltfm_shutdown,
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_cimodule_platform_driver(iproc_pcie_pltfm_driver);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
1448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver");
1458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
146