mirror of
https://github.com/torvalds/linux.git
synced 2024-12-16 16:12:52 +00:00
943ebae781
Traditionally, all iProc PCIe root complexes use PAXB-based wrapper, with an integrated on-chip Serdes to support external endpoint devices. On newer iProc platforms, a PAXC-based wrapper is introduced, for connection with internally emulated PCIe endpoint devices in the ASIC. Add support for PAXC-based iProc PCIe root complex in the iProc PCIe core driver. This change factors out common logic between PAXB and PAXC, and uses tables to store register offsets that are different between PAXB and PAXC. This allows the driver to be scaled to support subsequent PAXC revisions in the future. Signed-off-by: Ray Jui <rjui@broadcom.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Scott Branden <sbranden@broadcom.com>
148 lines
3.7 KiB
C
148 lines
3.7 KiB
C
/*
|
|
* Copyright (C) 2015 Broadcom Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation version 2.
|
|
*
|
|
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
|
* kind, whether express or implied; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_pci.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/phy/phy.h>
|
|
|
|
#include "pcie-iproc.h"
|
|
|
|
static const struct of_device_id iproc_pcie_of_match_table[] = {
|
|
{
|
|
.compatible = "brcm,iproc-pcie",
|
|
.data = (int *)IPROC_PCIE_PAXB,
|
|
}, {
|
|
.compatible = "brcm,iproc-pcie-paxc",
|
|
.data = (int *)IPROC_PCIE_PAXC,
|
|
},
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
|
|
|
|
static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
|
|
{
|
|
const struct of_device_id *of_id;
|
|
struct iproc_pcie *pcie;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct resource reg;
|
|
resource_size_t iobase = 0;
|
|
LIST_HEAD(res);
|
|
int ret;
|
|
|
|
of_id = of_match_device(iproc_pcie_of_match_table, &pdev->dev);
|
|
if (!of_id)
|
|
return -EINVAL;
|
|
|
|
pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL);
|
|
if (!pcie)
|
|
return -ENOMEM;
|
|
|
|
pcie->dev = &pdev->dev;
|
|
pcie->type = (enum iproc_pcie_type)of_id->data;
|
|
platform_set_drvdata(pdev, pcie);
|
|
|
|
ret = of_address_to_resource(np, 0, ®);
|
|
if (ret < 0) {
|
|
dev_err(pcie->dev, "unable to obtain controller resources\n");
|
|
return ret;
|
|
}
|
|
|
|
pcie->base = devm_ioremap(pcie->dev, reg.start, resource_size(®));
|
|
if (!pcie->base) {
|
|
dev_err(pcie->dev, "unable to map controller registers\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (of_property_read_bool(np, "brcm,pcie-ob")) {
|
|
u32 val;
|
|
|
|
ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
|
|
&val);
|
|
if (ret) {
|
|
dev_err(pcie->dev,
|
|
"missing brcm,pcie-ob-axi-offset property\n");
|
|
return ret;
|
|
}
|
|
pcie->ob.axi_offset = val;
|
|
|
|
ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
|
|
&val);
|
|
if (ret) {
|
|
dev_err(pcie->dev,
|
|
"missing brcm,pcie-ob-window-size property\n");
|
|
return ret;
|
|
}
|
|
pcie->ob.window_size = (resource_size_t)val * SZ_1M;
|
|
|
|
if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
|
|
pcie->ob.set_oarr_size = true;
|
|
|
|
pcie->need_ob_cfg = true;
|
|
}
|
|
|
|
/* PHY use is optional */
|
|
pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
|
|
if (IS_ERR(pcie->phy)) {
|
|
if (PTR_ERR(pcie->phy) == -EPROBE_DEFER)
|
|
return -EPROBE_DEFER;
|
|
pcie->phy = NULL;
|
|
}
|
|
|
|
ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase);
|
|
if (ret) {
|
|
dev_err(pcie->dev,
|
|
"unable to get PCI host bridge resources\n");
|
|
return ret;
|
|
}
|
|
|
|
pcie->map_irq = of_irq_parse_and_map_pci;
|
|
|
|
ret = iproc_pcie_setup(pcie, &res);
|
|
if (ret)
|
|
dev_err(pcie->dev, "PCIe controller setup failed\n");
|
|
|
|
pci_free_resource_list(&res);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
|
|
{
|
|
struct iproc_pcie *pcie = platform_get_drvdata(pdev);
|
|
|
|
return iproc_pcie_remove(pcie);
|
|
}
|
|
|
|
static struct platform_driver iproc_pcie_pltfm_driver = {
|
|
.driver = {
|
|
.name = "iproc-pcie",
|
|
.of_match_table = of_match_ptr(iproc_pcie_of_match_table),
|
|
},
|
|
.probe = iproc_pcie_pltfm_probe,
|
|
.remove = iproc_pcie_pltfm_remove,
|
|
};
|
|
module_platform_driver(iproc_pcie_pltfm_driver);
|
|
|
|
MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
|
|
MODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver");
|
|
MODULE_LICENSE("GPL v2");
|