// SPDX-License-Identifier: GPL-2.0
/*
* Synopsys DesignWare PCIe host controller driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* https://www.samsung.com
*
* Author: Jingoo Han <jg1.han@samsung.com>
*/
#include <linux/align.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma/edma.h>
#include <linux/gpio/consumer.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/types.h>
#include "../../pci.h"
#include "pcie-designware.h"
static const char * const dw_pcie_app_clks[DW_PCIE_NUM_APP_CLKS] = {
[DW_PCIE_DBI_CLK] = "dbi",
[DW_PCIE_MSTR_CLK] = "mstr",
[DW_PCIE_SLV_CLK] = "slv",
};
static const char * const dw_pcie_core_clks[DW_PCIE_NUM_CORE_CLKS] = {
[DW_PCIE_PIPE_CLK] = "pipe",
[DW_PCIE_CORE_CLK] = "core",
[DW_PCIE_AUX_CLK] = "aux",
[DW_PCIE_REF_CLK] = "ref",
};
static const char * const dw_pcie_app_rsts[DW_PCIE_NUM_APP_RSTS] = {
[DW_PCIE_DBI_RST] = "dbi",
[DW_PCIE_MSTR_RST] = "mstr",
[DW_PCIE_SLV_RST] = "slv",
};
static const char * const dw_pcie_core_rsts[DW_PCIE_NUM_CORE_RSTS] = {
[DW_PCIE_NON_STICKY_RST] = "non-sticky",
[DW_PCIE_STICKY_RST] = "sticky",
[DW_PCIE_CORE_RST] = "core",
[DW_PCIE_PIPE_RST] = "pipe",
[DW_PCIE_PHY_RST] = "phy",
[DW_PCIE_HOT_RST] = "hot",
[DW_PCIE_PWR_RST] = "pwr",
};
static int dw_pcie_get_clocks(struct dw_pcie *pci)
{
int i, ret;
for (i = 0; i < DW_PCIE_NUM_APP_CLKS; i++)
pci->app_clks[i].id = dw_pcie_app_clks[i];
for (i = 0; i < DW_PCIE_NUM_CORE_CLKS; i++)
pci->core_clks[i].id = dw_pcie_core_clks[i];
ret = devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_APP_CLKS,
pci->app_clks);
if (ret)
return ret;
return devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_CORE_CLKS,
pci->core_clks);
}
static int dw_pcie_get_resets(struct dw_pcie *pci)
{
int i, ret;
for (i = 0; i < DW_PCIE_NUM_APP_RSTS; i++)
pci->app_rsts[i].id = dw_pcie_app_rsts[i];
for (i = 0; i < DW_PCIE_NUM_CORE_RSTS; i++)
pci->core_rsts[i].id = dw_pcie_core_rsts[i];
ret = devm_reset_control_bulk_get_optional_shared(pci->dev,
DW_PCIE_NUM_APP_RSTS,
pci->app_rsts);
if (ret)
return ret;
ret = devm_reset_control_bulk_get_optional_exclusive(pci->dev,
DW_PCIE_NUM_CORE_RSTS,
pci->core_rsts);
if (ret)
return ret;
pci->pe_rst = devm_gpiod_get_optional(pci->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(pci->pe_rst))
return PTR_ERR(pci->pe_rst);
return 0;
}
int dw_pcie_get_resources(struct dw_pcie *pci)
{
struct platform_device *pdev = to_platform_device(pci->dev);
struct device_node *np = dev_of_node(pci->dev);
struct resource *res;
int ret;
if (!pci->dbi_base) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res);
if (IS_ERR(pci->dbi_base))
return PTR_ERR(pci->dbi_base);
pci->dbi_phys_addr = res->start;
}
/* DBI2 is mainly useful for the endpoint controller */
if (!pci->dbi_base2) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2");
if (res) {
pci->dbi_base2 = devm_pci_remap_cfg_resource(pci->dev, res);
if (IS_ERR(pci->dbi_base2))
return PTR_ERR(pci->dbi_base2);
} else {
pci->dbi_base2 = pci->dbi_base + SZ_4K;
}
}
/* For non-unrolled iATU/eDMA platforms this range will be ignored */
if (!pci->atu_base) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu");
if (res) {
pci->atu_size = resource_size(res);
pci->atu_base = devm_ioremap_resource(pci->dev, res);
if (IS_ERR(pci->atu_base))
return PTR_ERR(pc