[PATCH v1 0/2] add brcm iproc pcie driver

Add support for Broadcom pcie driver and api to get dma regions.
Rayagonda Kokatanur (1): drivers: pci: add api to get dma regions
Srinath Mannam (1): drivers: pcie: add Broadcom IPROC PCIe RC driver
drivers/pci/Kconfig | 7 + drivers/pci/Makefile | 1 + drivers/pci/pci-uclass.c | 42 ++ drivers/pci/pcie_iproc.c | 1287 ++++++++++++++++++++++++++++++++++++++ include/pci.h | 3 +- 5 files changed, 1339 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/pcie_iproc.c

Add api to get dma regions.
Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com --- drivers/pci/pci-uclass.c | 42 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 3 ++- 2 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 7f46e901fb..f5a87f74a7 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1176,6 +1176,48 @@ ulong pci_conv_size_to_32(ulong old, ulong value, uint offset, return value; }
+int pci_get_dma_regions(struct udevice *dev, struct pci_region *memp, int index) +{ + int pci_addr_cells, addr_cells, size_cells; + int cells_per_record; + const u32 *prop; + int len; + int i = 0; + + prop = ofnode_get_property(dev_ofnode(dev), "dma-ranges", &len); + if (!prop) { + log_err("PCI: Device '%s': Cannot decode dma-ranges\n", + dev->name); + return -EINVAL; + } + + pci_addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev)); + addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev->parent)); + size_cells = ofnode_read_simple_size_cells(dev_ofnode(dev)); + + /* PCI addresses are always 3-cells */ + len /= sizeof(u32); + cells_per_record = pci_addr_cells + addr_cells + size_cells; + debug("%s: len=%d, cells_per_record=%d\n", __func__, len, + cells_per_record); + + while (len) { + memp->bus_start = fdtdec_get_number(prop + 1, 2); + prop += pci_addr_cells; + memp->phys_start = fdtdec_get_number(prop, addr_cells); + prop += addr_cells; + memp->size = fdtdec_get_number(prop, size_cells); + prop += size_cells; + + if (i == index) + return 0; + i++; + len -= cells_per_record; + } + + return -EINVAL; +} + int pci_get_regions(struct udevice *dev, struct pci_region **iop, struct pci_region **memp, struct pci_region **prefp) { diff --git a/include/pci.h b/include/pci.h index aff56b24f9..bcc699e257 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1297,7 +1297,8 @@ struct udevice *pci_get_controller(struct udevice *dev); */ int pci_get_regions(struct udevice *dev, struct pci_region **iop, struct pci_region **memp, struct pci_region **prefp); - +int +pci_get_dma_regions(struct udevice *dev, struct pci_region *memp, int index); /** * dm_pci_write_bar32() - Write the address of a BAR *

Hi Rayagonda,
On Tue, 12 May 2020 at 02:00, Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com wrote:
Add api to get dma regions.
Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
drivers/pci/pci-uclass.c | 42 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 3 ++- 2 files changed, 44 insertions(+), 1 deletion(-)
Please can you add a test in test/dm/pci.c
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 7f46e901fb..f5a87f74a7 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1176,6 +1176,48 @@ ulong pci_conv_size_to_32(ulong old, ulong value, uint offset, return value; }
+int pci_get_dma_regions(struct udevice *dev, struct pci_region *memp, int index) +{
int pci_addr_cells, addr_cells, size_cells;
int cells_per_record;
const u32 *prop;
int len;
int i = 0;
prop = ofnode_get_property(dev_ofnode(dev), "dma-ranges", &len);
dev_read_prop
if (!prop) {
log_err("PCI: Device '%s': Cannot decode dma-ranges\n",
dev->name);
return -EINVAL;
}
pci_addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev));
addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev->parent));
size_cells = ofnode_read_simple_size_cells(dev_ofnode(dev));
Use dev_... versions
/* PCI addresses are always 3-cells */
len /= sizeof(u32);
cells_per_record = pci_addr_cells + addr_cells + size_cells;
debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
cells_per_record);
while (len) {
memp->bus_start = fdtdec_get_number(prop + 1, 2);
prop += pci_addr_cells;
memp->phys_start = fdtdec_get_number(prop, addr_cells);
prop += addr_cells;
memp->size = fdtdec_get_number(prop, size_cells);
prop += size_cells;
Can you look at how to share code with decode_regions()?
if (i == index)
return 0;
i++;
len -= cells_per_record;
}
return -EINVAL;
+}
int pci_get_regions(struct udevice *dev, struct pci_region **iop, struct pci_region **memp, struct pci_region **prefp) { diff --git a/include/pci.h b/include/pci.h index aff56b24f9..bcc699e257 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1297,7 +1297,8 @@ struct udevice *pci_get_controller(struct udevice *dev); */ int pci_get_regions(struct udevice *dev, struct pci_region **iop, struct pci_region **memp, struct pci_region **prefp);
+int +pci_get_dma_regions(struct udevice *dev, struct pci_region *memp, int index);
put 'int' on the same line. Also please add a function comment.
/**
- dm_pci_write_bar32() - Write the address of a BAR
-- 2.17.1
Regards, Simon

On Tue, May 12, 2020 at 01:29:49PM +0530, Rayagonda Kokatanur wrote:
Add api to get dma regions.
Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
Applied to u-boot/master, thanks!

From: Srinath Mannam srinath.mannam@broadcom.com
Add support for IPROC PAXC PCIe RC driver.
Signed-off-by: Srinath Mannam srinath.mannam@broadcom.com Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com --- drivers/pci/Kconfig | 7 + drivers/pci/Makefile | 1 + drivers/pci/pcie_iproc.c | 1287 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1295 insertions(+) create mode 100644 drivers/pci/pcie_iproc.c
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 437cd9a055..3b134c27a0 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -174,6 +174,13 @@ config PCIE_INTEL_FPGA Say Y here if you want to enable PCIe controller support on Intel FPGA, example Stratix 10.
+config PCIE_IPROC + bool "Iproc PCIe support" + depends on DM_PCI + help + Broadcom iProc PCIe controller driver. + Say Y here if you want to enable Broadcom iProc PCIe controller, + config PCI_MVEBU bool "Enable Armada XP/38x PCIe driver" depends on ARCH_MVEBU diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index c051ecc9f3..8d3fb169b1 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o +obj-$(CONFIG_PCIE_IPROC) += pcie_iproc.o obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o obj-$(CONFIG_PCIE_FSL) += pcie_fsl.o pcie_fsl_fixup.o diff --git a/drivers/pci/pcie_iproc.c b/drivers/pci/pcie_iproc.c new file mode 100644 index 0000000000..d77735fcf2 --- /dev/null +++ b/drivers/pci/pcie_iproc.c @@ -0,0 +1,1287 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Broadcom + * + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <generic-phy.h> +#include <pci.h> +#include <malloc.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/log2.h> + +#define EP_PERST_SOURCE_SELECT_SHIFT 2 +#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT) +#define EP_MODE_SURVIVE_PERST_SHIFT 1 +#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT) +#define RC_PCIE_RST_OUTPUT_SHIFT 0 +#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT) + +#define CFG_IND_ADDR_MASK 0x00001ffc + +#define CFG_ADDR_BUS_NUM_SHIFT 20 +#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000 +#define CFG_ADDR_DEV_NUM_SHIFT 15 +#define CFG_ADDR_DEV_NUM_MASK 0x000f8000 +#define CFG_ADDR_FUNC_NUM_SHIFT 12 +#define CFG_ADDR_FUNC_NUM_MASK 0x00007000 +#define CFG_ADDR_REG_NUM_SHIFT 2 +#define CFG_ADDR_REG_NUM_MASK 0x00000ffc +#define CFG_ADDR_CFG_TYPE_SHIFT 0 +#define CFG_ADDR_CFG_TYPE_MASK 0x00000003 + +#define IPROC_PCI_PM_CAP 0x48 +#define IPROC_PCI_PM_CAP_MASK 0xffff +#define IPROC_PCI_EXP_CAP 0xac + +#define IPROC_PCIE_REG_INVALID 0xffff + +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +/* CRS Software Visibility capability */ +#define PCI_EXP_RTCAP_CRSVIS 0x0001 + +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ + +#define PCIE_PHYLINKUP_SHIFT 3 +#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT) +#define PCIE_DL_ACTIVE_SHIFT 2 +#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT) + +/* derive the enum index of the outbound/inbound mapping registers */ +#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2) + +/* + * Maximum number of outbound mapping window sizes that can be supported by any + * OARR/OMAP mapping pair + */ +#define MAX_NUM_OB_WINDOW_SIZES 4 + +#define OARR_VALID_SHIFT 0 +#define OARR_VALID BIT(OARR_VALID_SHIFT) +#define OARR_SIZE_CFG_SHIFT 1 + +/* + * Maximum number of inbound mapping region sizes that can be supported by an + * IARR + */ +#define MAX_NUM_IB_REGION_SIZES 9 + +#define IMAP_VALID_SHIFT 0 +#define IMAP_VALID BIT(IMAP_VALID_SHIFT) + +#define APB_ERR_EN_SHIFT 0 +#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT) + +/** + * iProc PCIe host registers + */ +enum iproc_pcie_reg { + /* clock/reset signal control */ + IPROC_PCIE_CLK_CTRL = 0, + + /* + * To allow MSI to be steered to an external MSI controller (e.g., ARM + * GICv3 ITS) + */ + IPROC_PCIE_MSI_GIC_MODE, + + /* + * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the + * window where the MSI posted writes are written, for the writes to be + * interpreted as MSI writes. + */ + IPROC_PCIE_MSI_BASE_ADDR, + IPROC_PCIE_MSI_WINDOW_SIZE, + + /* + * To hold the address of the register where the MSI writes are + * programed. When ARM GICv3 ITS is used, this should be programmed + * with the address of the GITS_TRANSLATER register. + */ + IPROC_PCIE_MSI_ADDR_LO, + IPROC_PCIE_MSI_ADDR_HI, + + /* enable MSI */ + IPROC_PCIE_MSI_EN_CFG, + + /* allow access to root complex configuration space */ + IPROC_PCIE_CFG_IND_ADDR, + IPROC_PCIE_CFG_IND_DATA, + + /* allow access to device configuration space */ + IPROC_PCIE_CFG_ADDR, + IPROC_PCIE_CFG_DATA, + + /* enable INTx */ + IPROC_PCIE_INTX_EN, + IPROC_PCIE_INTX_CSR, + + /* outbound address mapping */ + IPROC_PCIE_OARR0, + IPROC_PCIE_OMAP0, + IPROC_PCIE_OARR1, + IPROC_PCIE_OMAP1, + IPROC_PCIE_OARR2, + IPROC_PCIE_OMAP2, + IPROC_PCIE_OARR3, + IPROC_PCIE_OMAP3, + + /* inbound address mapping */ + IPROC_PCIE_IARR0, + IPROC_PCIE_IMAP0, + IPROC_PCIE_IARR1, + IPROC_PCIE_IMAP1, + IPROC_PCIE_IARR2, + IPROC_PCIE_IMAP2, + IPROC_PCIE_IARR3, + IPROC_PCIE_IMAP3, + IPROC_PCIE_IARR4, + IPROC_PCIE_IMAP4, + + /* config read status */ + IPROC_PCIE_CFG_RD_STATUS, + + /* link status */ + IPROC_PCIE_LINK_STATUS, + + /* enable APB error for unsupported requests */ + IPROC_PCIE_APB_ERR_EN, + + /* Ordering Mode configuration registers */ + IPROC_PCIE_ORDERING_CFG, + IPROC_PCIE_IMAP0_RO_CONTROL, + IPROC_PCIE_IMAP1_RO_CONTROL, + IPROC_PCIE_IMAP2_RO_CONTROL, + IPROC_PCIE_IMAP3_RO_CONTROL, + IPROC_PCIE_IMAP4_RO_CONTROL, + + /* total number of core registers */ + IPROC_PCIE_MAX_NUM_REG, +}; + +/* iProc PCIe PAXB v2 registers */ +static const u16 iproc_pcie_reg_paxb_v2[] = { + [IPROC_PCIE_CLK_CTRL] = 0x000, + [IPROC_PCIE_CFG_IND_ADDR] = 0x120, + [IPROC_PCIE_CFG_IND_DATA] = 0x124, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, + [IPROC_PCIE_INTX_EN] = 0x330, + [IPROC_PCIE_INTX_CSR] = 0x334, + [IPROC_PCIE_OARR0] = 0xd20, + [IPROC_PCIE_OMAP0] = 0xd40, + [IPROC_PCIE_OARR1] = 0xd28, + [IPROC_PCIE_OMAP1] = 0xd48, + [IPROC_PCIE_OARR2] = 0xd60, + [IPROC_PCIE_OMAP2] = 0xd68, + [IPROC_PCIE_OARR3] = 0xdf0, + [IPROC_PCIE_OMAP3] = 0xdf8, + [IPROC_PCIE_IARR0] = 0xd00, + [IPROC_PCIE_IMAP0] = 0xc00, + [IPROC_PCIE_IARR2] = 0xd10, + [IPROC_PCIE_IMAP2] = 0xcc0, + [IPROC_PCIE_IARR3] = 0xe00, + [IPROC_PCIE_IMAP3] = 0xe08, + [IPROC_PCIE_IARR4] = 0xe68, + [IPROC_PCIE_IMAP4] = 0xe70, + [IPROC_PCIE_CFG_RD_STATUS] = 0xee0, + [IPROC_PCIE_LINK_STATUS] = 0xf0c, + [IPROC_PCIE_APB_ERR_EN] = 0xf40, + [IPROC_PCIE_ORDERING_CFG] = 0x2000, + [IPROC_PCIE_IMAP0_RO_CONTROL] = 0x201c, + [IPROC_PCIE_IMAP1_RO_CONTROL] = 0x2020, + [IPROC_PCIE_IMAP2_RO_CONTROL] = 0x2024, + [IPROC_PCIE_IMAP3_RO_CONTROL] = 0x2028, + [IPROC_PCIE_IMAP4_RO_CONTROL] = 0x202c, +}; + +/* iProc PCIe PAXC v2 registers */ +static const u16 iproc_pcie_reg_paxc_v2[] = { + [IPROC_PCIE_MSI_GIC_MODE] = 0x050, + [IPROC_PCIE_MSI_BASE_ADDR] = 0x074, + [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078, + [IPROC_PCIE_MSI_ADDR_LO] = 0x07c, + [IPROC_PCIE_MSI_ADDR_HI] = 0x080, + [IPROC_PCIE_MSI_EN_CFG] = 0x09c, + [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, + [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, +}; + +/** + * List of device IDs of controllers that have corrupted + * capability list that require SW fixup + */ +static const u16 iproc_pcie_corrupt_cap_did[] = { + 0x16cd, + 0x16f0, + 0xd802, + 0xd804 +}; + +enum iproc_pcie_type { + IPROC_PCIE_PAXB_V2, + IPROC_PCIE_PAXC, + IPROC_PCIE_PAXC_V2, +}; + +/** + * struct iproc_pcie_ob - iProc PCIe outbound mapping + * + * @axi_offset: offset from the AXI address to the internal address used by + * the iProc PCIe core + * @nr_windows: total number of supported outbound mapping windows + */ +struct iproc_pcie_ob { + resource_size_t axi_offset; + unsigned int nr_windows; +}; + +/** + * struct iproc_pcie_ib - iProc PCIe inbound mapping + * + * @nr_regions: total number of supported inbound mapping regions + */ +struct iproc_pcie_ib { + unsigned int nr_regions; +}; + +/** + * struct iproc_pcie_ob_map - outbound mapping controller specific parameters + * + * @window_sizes: list of supported outbound mapping window sizes in MB + * @nr_sizes: number of supported outbound mapping window sizes + */ +struct iproc_pcie_ob_map { + resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES]; + unsigned int nr_sizes; +}; + +static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = { + { + /* OARR0/OMAP0 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, + { + /* OARR1/OMAP1 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, + { + /* OARR2/OMAP2 */ + .window_sizes = { 128, 256, 512, 1024 }, + .nr_sizes = 4, + }, + { + /* OARR3/OMAP3 */ + .window_sizes = { 128, 256, 512, 1024 }, + .nr_sizes = 4, + }, +}; + +/** + * iProc PCIe inbound mapping type + */ +enum iproc_pcie_ib_map_type { + /* for DDR memory */ + IPROC_PCIE_IB_MAP_MEM = 0, + + /* for device I/O memory */ + IPROC_PCIE_IB_MAP_IO, + + /* invalid or unused */ + IPROC_PCIE_IB_MAP_INVALID +}; + +/** + * struct iproc_pcie_ib_map - inbound mapping controller specific parameters + * + * @type: inbound mapping region type + * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or SZ_1G + * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or + * GB, depedning on the size unit + * @nr_sizes: number of supported inbound mapping region sizes + * @nr_windows: number of supported inbound mapping windows for the region + * @imap_addr_offset: register offset between the upper and lower 32-bit + * IMAP address registers + * @imap_window_offset: register offset between each IMAP window + */ +struct iproc_pcie_ib_map { + enum iproc_pcie_ib_map_type type; + unsigned int size_unit; + resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES]; + unsigned int nr_sizes; + unsigned int nr_windows; + u16 imap_addr_offset; + u16 imap_window_offset; +}; + +static const struct iproc_pcie_ib_map paxb_v2_ib_map[] = { + { + /* IARR0/IMAP0 */ + .type = IPROC_PCIE_IB_MAP_IO, + .size_unit = SZ_1K, + .region_sizes = { 32 }, + .nr_sizes = 1, + .nr_windows = 8, + .imap_addr_offset = 0x40, + .imap_window_offset = 0x4, + }, + { + /* IARR1/IMAP1 (currently unused) */ + .type = IPROC_PCIE_IB_MAP_INVALID, + }, + { + /* IARR2/IMAP2 */ + .type = IPROC_PCIE_IB_MAP_MEM, + .size_unit = SZ_1M, + .region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192, + 16384 }, + .nr_sizes = 9, + .nr_windows = 1, + .imap_addr_offset = 0x4, + .imap_window_offset = 0x8, + }, + { + /* IARR3/IMAP3 */ + .type = IPROC_PCIE_IB_MAP_MEM, + .size_unit = SZ_1G, + .region_sizes = { 1, 2, 4, 8, 16, 32 }, + .nr_sizes = 6, + .nr_windows = 8, + .imap_addr_offset = 0x4, + .imap_window_offset = 0x8, + }, + { + /* IARR4/IMAP4 */ + .type = IPROC_PCIE_IB_MAP_MEM, + .size_unit = SZ_1G, + .region_sizes = { 32, 64, 128, 256, 512 }, + .nr_sizes = 5, + .nr_windows = 8, + .imap_addr_offset = 0x4, + .imap_window_offset = 0x8, + }, +}; + +/** + * struct iproc_pcie - iproc pcie device instance + * + * @dev: pointer to pcie udevice + * @base: device I/O base address + * @type: pci device type, PAXC or PAXB + * @reg_offsets: pointer to pcie host register + * @fix_paxc_cap: paxc capability + * @need_ob_cfg: outbound mapping status + * @ob: pcie outbound mapping + * @ob_map: pointer to outbound mapping parameters + * @need_ib_cfg: inbound mapping status + * @ib: pcie inbound mapping + * @ib_map: pointer to inbound mapping parameters + * @ep_is_internal: ep status + * @phy: phy device + * @link_is_active: link up status + * @has_apb_err_disable: apb error status + */ +struct iproc_pcie { + struct udevice *dev; + void __iomem *base; + enum iproc_pcie_type type; + u16 *reg_offsets; + bool fix_paxc_cap; + bool need_ob_cfg; + struct iproc_pcie_ob ob; + const struct iproc_pcie_ob_map *ob_map; + bool need_ib_cfg; + struct iproc_pcie_ib ib; + const struct iproc_pcie_ib_map *ib_map; + bool ep_is_internal; + struct phy phy; + bool link_is_active; + bool has_apb_err_disable; +}; + +static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset) +{ + return !!(reg_offset == IPROC_PCIE_REG_INVALID); +} + +static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie, + enum iproc_pcie_reg reg) +{ + return pcie->reg_offsets[reg]; +} + +static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie, + enum iproc_pcie_reg reg) +{ + u16 offset = iproc_pcie_reg_offset(pcie, reg); + + if (iproc_pcie_reg_is_invalid(offset)) + return 0; + + return readl(pcie->base + offset); +} + +static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie, + enum iproc_pcie_reg reg, u32 val) +{ + u16 offset = iproc_pcie_reg_offset(pcie, reg); + + if (iproc_pcie_reg_is_invalid(offset)) + return; + + writel(val, pcie->base + offset); +} + +static int iproc_pcie_map_ep_cfg_reg(const struct udevice *udev, pci_dev_t bdf, + uint where, void **paddress) +{ + struct iproc_pcie *pcie = dev_get_priv(udev); + unsigned int busno = PCI_BUS(bdf); + unsigned int slot = PCI_DEV(bdf); + unsigned int fn = PCI_FUNC(bdf); + + u16 offset; + u32 val; + + /* root complex access */ + if (busno == 0) { + if (slot > 0 || fn > 0) + return -ENODEV; + + iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR, + where & CFG_IND_ADDR_MASK); + offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA); + if (iproc_pcie_reg_is_invalid(offset)) + return -ENODEV; + + *paddress = (pcie->base + offset); + return 0; + } + + if (!pcie->link_is_active) + return -ENODEV; + + /* EP device access */ + val = (busno << CFG_ADDR_BUS_NUM_SHIFT) | + (slot << CFG_ADDR_DEV_NUM_SHIFT) | + (fn << CFG_ADDR_FUNC_NUM_SHIFT) | + (where & CFG_ADDR_REG_NUM_MASK) | + (1 & CFG_ADDR_CFG_TYPE_MASK); + + iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val); + offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA); + + if (iproc_pcie_reg_is_invalid(offset)) + return -ENODEV; + + *paddress = (pcie->base + offset); + + return 0; +} + +static void iproc_pcie_fix_cap(struct iproc_pcie *pcie, int where, ulong *val) +{ + u32 i, dev_id; + + switch (where & ~0x3) { + case PCI_VENDOR_ID: + dev_id = *val >> 16; + + /* + * Activate fixup for those controllers that have corrupted + * capability list registers + */ + for (i = 0; i < ARRAY_SIZE(iproc_pcie_corrupt_cap_did); i++) + if (dev_id == iproc_pcie_corrupt_cap_did[i]) + pcie->fix_paxc_cap = true; + break; + + case IPROC_PCI_PM_CAP: + if (pcie->fix_paxc_cap) { + /* advertise PM, force next capability to PCIe */ + *val &= ~IPROC_PCI_PM_CAP_MASK; + *val |= IPROC_PCI_EXP_CAP << 8 | PCI_CAP_ID_PM; + } + break; + + case IPROC_PCI_EXP_CAP: + if (pcie->fix_paxc_cap) { + /* advertise root port, version 2, terminate here */ + *val = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2) << 16 | + PCI_CAP_ID_EXP; + } + break; + + case IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL: + /* Don't advertise CRS SV support */ + *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); + break; + + default: + break; + } +} + +static int iproc_pci_raw_config_read32(struct iproc_pcie *pcie, + unsigned int devfn, int where, + int size, u32 *val) +{ + void __iomem *addr; + int ret; + + ret = iproc_pcie_map_ep_cfg_reg(pcie->dev, devfn, where & ~0x3, &addr); + if (ret) { + *val = ~0; + return -EINVAL; + } + + *val = readl(addr); + + if (size <= 2) + *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); + + return 0; +} + +static int iproc_pci_raw_config_write32(struct iproc_pcie *pcie, + unsigned int devfn, int where, + int size, u32 val) +{ + void __iomem *addr; + int ret; + u32 mask, tmp; + + ret = iproc_pcie_map_ep_cfg_reg(pcie->dev, devfn, where & ~0x3, &addr); + if (ret) + return -EINVAL; + + if (size == 4) { + writel(val, addr); + return 0; + } + + mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); + tmp = readl(addr) & mask; + tmp |= val << ((where & 0x3) * 8); + writel(tmp, addr); + return 0; +} + +/** + * iproc_pcie_apb_err_disable() - configure apb error + * + * APB error forwarding can be disabled during access of configuration + * registers of the endpoint device, to prevent unsupported requests + * (typically seen during enumeration with multi-function devices) from + * triggering a system exception. + * + * @bus: pcie udevice + * @bdf: pdf value + * @disabled: flag to enable/disabled apb error + */ +static inline void iproc_pcie_apb_err_disable(const struct udevice *bus, + pci_dev_t bdf, bool disable) +{ + struct iproc_pcie *pcie = dev_get_priv(bus); + u32 val; + + if (PCI_BUS(bdf) && pcie->has_apb_err_disable) { + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN); + if (disable) + val &= ~APB_ERR_EN; + else + val |= APB_ERR_EN; + iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val); + } +} + +static int iproc_pcie_config_read32(const struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct iproc_pcie *pcie = dev_get_priv(bus); + int ret; + ulong data; + + iproc_pcie_apb_err_disable(bus, bdf, true); + ret = pci_generic_mmap_read_config(bus, iproc_pcie_map_ep_cfg_reg, + bdf, offset, &data, PCI_SIZE_32); + iproc_pcie_apb_err_disable(bus, bdf, false); + if (size <= PCI_SIZE_16) + *valuep = (data >> (8 * (offset & 3))) & + ((1 << (BIT(size) * 8)) - 1); + else + *valuep = data; + + if (!ret && PCI_BUS(bdf) == 0) + iproc_pcie_fix_cap(pcie, offset, valuep); + + return ret; +} + +static int iproc_pcie_config_write32(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + void *addr; + ulong mask, tmp; + int ret; + + ret = iproc_pcie_map_ep_cfg_reg(bus, bdf, offset, &addr); + if (ret) + return ret; + + if (size == PCI_SIZE_32) { + writel(value, addr); + return ret; + } + + iproc_pcie_apb_err_disable(bus, bdf, true); + mask = ~(((1 << (BIT(size) * 8)) - 1) << ((offset & 0x3) * 8)); + tmp = readl(addr) & mask; + tmp |= (value << ((offset & 0x3) * 8)); + writel(tmp, addr); + iproc_pcie_apb_err_disable(bus, bdf, false); + + return ret; +} + +const static struct dm_pci_ops iproc_pcie_ops = { + .read_config = iproc_pcie_config_read32, + .write_config = iproc_pcie_config_write32, +}; + +static int iproc_pcie_rev_init(struct iproc_pcie *pcie) +{ + unsigned int reg_idx; + const u16 *regs; + u16 num_elements; + + switch (pcie->type) { + case IPROC_PCIE_PAXC_V2: + pcie->ep_is_internal = true; + regs = iproc_pcie_reg_paxc_v2; + num_elements = ARRAY_SIZE(iproc_pcie_reg_paxc_v2); + break; + case IPROC_PCIE_PAXB_V2: + regs = iproc_pcie_reg_paxb_v2; + num_elements = ARRAY_SIZE(iproc_pcie_reg_paxb_v2); + pcie->has_apb_err_disable = true; + if (pcie->need_ob_cfg) { + pcie->ob.axi_offset = 0; + pcie->ob_map = paxb_v2_ob_map; + pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map); + } + pcie->need_ib_cfg = true; + pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map); + pcie->ib_map = paxb_v2_ib_map; + break; + default: + dev_dbg(pcie->dev, "incompatible iProc PCIe interface\n"); + return -EINVAL; + } + + pcie->reg_offsets = calloc(IPROC_PCIE_MAX_NUM_REG, + sizeof(*pcie->reg_offsets)); + if (!pcie->reg_offsets) + return -ENOMEM; + + /* go through the register table and populate all valid registers */ + pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ? + IPROC_PCIE_REG_INVALID : regs[0]; + for (reg_idx = 1; reg_idx < num_elements; reg_idx++) + pcie->reg_offsets[reg_idx] = regs[reg_idx] ? + regs[reg_idx] : IPROC_PCIE_REG_INVALID; + + return 0; +} + +static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie, + int window_idx) +{ + u32 val; + + val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx)); + + return !!(val & OARR_VALID); +} + +static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx, + int size_idx, u64 axi_addr, u64 pci_addr) +{ + u16 oarr_offset, omap_offset; + + /* + * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based + * on window index. + */ + oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0, + window_idx)); + omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0, + window_idx)); + if (iproc_pcie_reg_is_invalid(oarr_offset) || + iproc_pcie_reg_is_invalid(omap_offset)) + return -EINVAL; + + /* + * Program the OARR registers. The upper 32-bit OARR register is + * always right after the lower 32-bit OARR register. + */ + writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) | + OARR_VALID, pcie->base + oarr_offset); + writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4); + + /* now program the OMAP registers */ + writel(lower_32_bits(pci_addr), pcie->base + omap_offset); + writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4); + + debug("ob window [%d]: offset 0x%x axi %pap pci %pap\n", + window_idx, oarr_offset, &axi_addr, &pci_addr); + debug("oarr lo 0x%x oarr hi 0x%x\n", + readl(pcie->base + oarr_offset), + readl(pcie->base + oarr_offset + 4)); + debug("omap lo 0x%x omap hi 0x%x\n", + readl(pcie->base + omap_offset), + readl(pcie->base + omap_offset + 4)); + + return 0; +} + +/** + * iproc_pcie_setup_ob() - setup outbound address mapping + * + * Some iProc SoCs require the SW to configure the outbound address mapping + * Outbound address translation: + * + * iproc_pcie_address = axi_address - axi_offset + * OARR = iproc_pcie_address + * OMAP = pci_addr + * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address + * + * @pcie: pcie device + * @axi_addr: axi address to be translated + * @pci_addr: pci address + * @size: window size + * + * @return: 0 on success and -ve on failure + */ +static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, + u64 pci_addr, resource_size_t size) +{ + struct iproc_pcie_ob *ob = &pcie->ob; + int ret = -EINVAL, window_idx, size_idx; + + if (axi_addr < ob->axi_offset) { + pr_err("axi address %pap less than offset %pap\n", + &axi_addr, &ob->axi_offset); + return -EINVAL; + } + + /* + * Translate the AXI address to the internal address used by the iProc + * PCIe core before programming the OARR + */ + axi_addr -= ob->axi_offset; + + /* iterate through all OARR/OMAP mapping windows */ + for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) { + const struct iproc_pcie_ob_map *ob_map = + &pcie->ob_map[window_idx]; + + /* + * If current outbound window is already in use, move on to the + * next one. + */ + if (iproc_pcie_ob_is_valid(pcie, window_idx)) + continue; + + /* + * Iterate through all supported window sizes within the + * OARR/OMAP pair to find a match. Go through the window sizes + * in a descending order. + */ + for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0; + size_idx--) { + resource_size_t window_size = + ob_map->window_sizes[size_idx] * SZ_1M; + + /* + * Keep iterating until we reach the last window and + * with the minimal window size at index zero. In this + * case, we take a compromise by mapping it using the + * minimum window size that can be supported + */ + if (size < window_size) { + if (size_idx > 0 || window_idx > 0) + continue; + + /* + * For the corner case of reaching the minimal + * window size that can be supported on the + * last window + */ + axi_addr = ALIGN_DOWN(axi_addr, window_size); + pci_addr = ALIGN_DOWN(pci_addr, window_size); + size = window_size; + } + + if (!IS_ALIGNED(axi_addr, window_size) || + !IS_ALIGNED(pci_addr, window_size)) { + pr_err("axi %pap or pci %pap not aligned\n", + &axi_addr, &pci_addr); + return -EINVAL; + } + + /* + * Match found! Program both OARR and OMAP and mark + * them as a valid entry. + */ + ret = iproc_pcie_ob_write(pcie, window_idx, size_idx, + axi_addr, pci_addr); + if (ret) + goto err_ob; + + size -= window_size; + if (size == 0) + return 0; + + /* + * If we are here, we are done with the current window, + * but not yet finished all mappings. Need to move on + * to the next window. + */ + axi_addr += window_size; + pci_addr += window_size; + break; + } + } + +err_ob: + pr_err("unable to configure outbound mapping\n"); + pr_err("axi %pap, axi offset %pap, pci %pap, res size %pap\n", + &axi_addr, &ob->axi_offset, &pci_addr, &size); + + return ret; +} + +static int iproc_pcie_map_ranges(struct udevice *dev) +{ + struct iproc_pcie *pcie = dev_get_priv(dev); + struct udevice *bus = pci_get_controller(dev); + struct pci_controller *hose = dev_get_uclass_priv(bus); + int i, ret; + + for (i = 0; i < hose->region_count; i++) { + if (hose->regions[i].flags == PCI_REGION_MEM || + hose->regions[i].flags == PCI_REGION_PREFETCH) { + debug("%d: bus_addr %p, axi_addr %p, size 0x%lx\n", + i, &hose->regions[i].bus_start, + &hose->regions[i].phys_start, + hose->regions[i].size); + ret = iproc_pcie_setup_ob(pcie, + hose->regions[i].phys_start, + hose->regions[i].bus_start, + hose->regions[i].size); + if (ret) + return ret; + } + } + + return 0; +} + +static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie, + int region_idx) +{ + const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; + u32 val; + + val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx)); + + return !!(val & (BIT(ib_map->nr_sizes) - 1)); +} + +static inline bool +iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map, + enum iproc_pcie_ib_map_type type) +{ + return !!(ib_map->type == type); +} + +static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx, + int size_idx, int nr_windows, u64 axi_addr, + u64 pci_addr, resource_size_t size) +{ + const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; + u16 iarr_offset, imap_offset; + u32 val; + int window_idx; + + iarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_IARR0, + region_idx)); + imap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_IMAP0, + region_idx)); + if (iproc_pcie_reg_is_invalid(iarr_offset) || + iproc_pcie_reg_is_invalid(imap_offset)) + return -EINVAL; + + debug("ib region [%d]: offset 0x%x axi %pap pci %pap\n", + region_idx, iarr_offset, &axi_addr, &pci_addr); + + /* + * Program the IARR registers. The upper 32-bit IARR register is + * always right after the lower 32-bit IARR register. + */ + writel(lower_32_bits(pci_addr) | BIT(size_idx), + pcie->base + iarr_offset); + writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4); + + debug("iarr lo 0x%x iarr hi 0x%x\n", + readl(pcie->base + iarr_offset), + readl(pcie->base + iarr_offset + 4)); + + /* + * Now program the IMAP registers. Each IARR region may have one or + * more IMAP windows. + */ + size >>= ilog2(nr_windows); + for (window_idx = 0; window_idx < nr_windows; window_idx++) { + val = readl(pcie->base + imap_offset); + val |= lower_32_bits(axi_addr) | IMAP_VALID; + writel(val, pcie->base + imap_offset); + writel(upper_32_bits(axi_addr), + pcie->base + imap_offset + ib_map->imap_addr_offset); + + debug("imap window [%d] lo 0x%x hi 0x%x\n", + window_idx, readl(pcie->base + imap_offset), + readl(pcie->base + imap_offset + + ib_map->imap_addr_offset)); + + imap_offset += ib_map->imap_window_offset; + axi_addr += size; + } + + return 0; +} + +/** + * iproc_pcie_setup_ib() - setup inbound address mapping + * + * @pcie: pcie device + * @axi_addr: axi address to be translated + * @pci_addr: pci address + * @size: window size + * @type: inbound mapping type + * + * @return: 0 on success and -ve on failure + */ +static int iproc_pcie_setup_ib(struct iproc_pcie *pcie, u64 axi_addr, + u64 pci_addr, resource_size_t size, + enum iproc_pcie_ib_map_type type) +{ + struct iproc_pcie_ib *ib = &pcie->ib; + int ret; + unsigned int region_idx, size_idx; + + /* iterate through all IARR mapping regions */ + for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) { + const struct iproc_pcie_ib_map *ib_map = + &pcie->ib_map[region_idx]; + + /* + * If current inbound region is already in use or not a + * compatible type, move on to the next. + */ + if (iproc_pcie_ib_is_in_use(pcie, region_idx) || + !iproc_pcie_ib_check_type(ib_map, type)) + continue; + + /* iterate through all supported region sizes to find a match */ + for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) { + resource_size_t region_size = + ib_map->region_sizes[size_idx] * ib_map->size_unit; + + if (size != region_size) + continue; + + if (!IS_ALIGNED(axi_addr, region_size) || + !IS_ALIGNED(pci_addr, region_size)) { + pr_err("axi %pap or pci %pap not aligned\n", + &axi_addr, &pci_addr); + return -EINVAL; + } + + /* Match found! Program IARR and all IMAP windows. */ + ret = iproc_pcie_ib_write(pcie, region_idx, size_idx, + ib_map->nr_windows, axi_addr, + pci_addr, size); + if (ret) + goto err_ib; + else + return 0; + } + } + ret = -EINVAL; + +err_ib: + pr_err("unable to configure inbound mapping\n"); + pr_err("axi %pap, pci %pap, res size %pap\n", + &axi_addr, &pci_addr, &size); + + return ret; +} + +static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) +{ + int ret; + struct pci_region regions; + int i = 0; + + while (!pci_get_dma_regions(pcie->dev, ®ions, i)) { + dev_dbg(pcie->dev, + "dma %d: bus_addr %#lx, axi_addr %#llx, size %#lx\n", + i, regions.bus_start, regions.phys_start, regions.size); + + /* Each range entry corresponds to an inbound mapping region */ + ret = iproc_pcie_setup_ib(pcie, regions.phys_start, + regions.bus_start, + regions.size, + IPROC_PCIE_IB_MAP_MEM); + if (ret) + return ret; + i++; + } + return 0; +} + +static void iproc_pcie_reset_map_regs(struct iproc_pcie *pcie) +{ + struct iproc_pcie_ib *ib = &pcie->ib; + struct iproc_pcie_ob *ob = &pcie->ob; + int window_idx, region_idx; + + if (pcie->ep_is_internal) + return; + + /* iterate through all OARR mapping regions */ + for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) { + iproc_pcie_write_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, + window_idx), 0); + } + + /* iterate through all IARR mapping regions */ + for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) { + iproc_pcie_write_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, + region_idx), 0); + } +} + +static void iproc_pcie_reset(struct iproc_pcie *pcie) +{ + u32 val; + + /* + * PAXC and the internal emulated endpoint device downstream should not + * be reset. If firmware has been loaded on the endpoint device at an + * earlier boot stage, reset here causes issues. + */ + if (pcie->ep_is_internal) + return; + + /* + * Select perst_b signal as reset source. Put the device into reset, + * and then bring it out of reset + */ + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); + val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST & + ~RC_PCIE_RST_OUTPUT; + iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); + udelay(250); + + val |= RC_PCIE_RST_OUTPUT; + iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); + mdelay(100); +} + +static inline bool iproc_pcie_link_is_active(struct iproc_pcie *pcie) +{ + u32 val; + + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS); + return !!((val & PCIE_PHYLINKUP) && (val & PCIE_DL_ACTIVE)); +} + +static int iproc_pcie_check_link(struct iproc_pcie *pcie) +{ + u32 link_status, class; + + pcie->link_is_active = false; + /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */ +#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c +#define PCI_CLASS_BRIDGE_MASK 0xffff00 +#define PCI_CLASS_BRIDGE_SHIFT 8 + iproc_pci_raw_config_read32(pcie, 0, + PCI_BRIDGE_CTRL_REG_OFFSET, + 4, &class); + class &= ~PCI_CLASS_BRIDGE_MASK; + class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT); + iproc_pci_raw_config_write32(pcie, 0, + PCI_BRIDGE_CTRL_REG_OFFSET, + 4, class); + + /* + * PAXC connects to emulated endpoint devices directly and does not + * have a Serdes. Therefore skip the link detection logic here. + */ + if (pcie->ep_is_internal) { + pcie->link_is_active = true; + return 0; + } + + if (!iproc_pcie_link_is_active(pcie)) { + pr_err("PHY or data link is INACTIVE!\n"); + return -ENODEV; + } + +#define PCI_TARGET_LINK_SPEED_MASK 0xf +#define PCI_TARGET_LINK_WIDTH_MASK 0x3f +#define PCI_TARGET_LINK_WIDTH_OFFSET 0x4 + + /* check link status to see if link is active */ + iproc_pci_raw_config_read32(pcie, 0, + IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA, + 2, &link_status); + if (link_status & PCI_EXP_LNKSTA_NLW) + pcie->link_is_active = true; + + if (pcie->link_is_active) + pr_info("link UP @ Speed Gen-%d and width-x%d\n", + link_status & PCI_TARGET_LINK_SPEED_MASK, + (link_status >> PCI_TARGET_LINK_WIDTH_OFFSET) & + PCI_TARGET_LINK_WIDTH_MASK); + else + pr_info("link DOWN\n"); + + return 0; +} + +static int iproc_pcie_probe(struct udevice *dev) +{ + struct iproc_pcie *pcie = dev_get_priv(dev); + int ret; + + pcie->type = (enum iproc_pcie_type)dev_get_driver_data(dev); + debug("PAX type %d\n", pcie->type); + pcie->base = dev_read_addr_ptr(dev); + debug("PAX reg base %p\n", pcie->base); + + if (!pcie->base) + return -ENODEV; + + if (dev_read_bool(dev, "brcm,pcie-ob")) + pcie->need_ob_cfg = true; + + pcie->dev = dev; + ret = iproc_pcie_rev_init(pcie); + if (ret) + return ret; + + if (!pcie->ep_is_internal) { + ret = generic_phy_get_by_name(dev, "pcie-phy", &pcie->phy); + if (!ret) { + ret = generic_phy_init(&pcie->phy); + if (ret) { + pr_err("failed to init %s PHY\n", dev->name); + return ret; + } + + ret = generic_phy_power_on(&pcie->phy); + if (ret) { + pr_err("power on %s PHY failed\n", dev->name); + goto err_exit_phy; + } + } + } + + iproc_pcie_reset(pcie); + + if (pcie->need_ob_cfg) { + ret = iproc_pcie_map_ranges(dev); + if (ret) { + pr_err("outbound map failed\n"); + goto err_power_off_phy; + } + } + + if (pcie->need_ib_cfg) { + ret = iproc_pcie_map_dma_ranges(pcie); + if (ret) { + pr_err("inbound map failed\n"); + goto err_power_off_phy; + } + } + + if (iproc_pcie_check_link(pcie)) + pr_info("no PCIe EP device detected\n"); + + return 0; + +err_power_off_phy: + generic_phy_power_off(&pcie->phy); +err_exit_phy: + generic_phy_exit(&pcie->phy); + return ret; +} + +static int iproc_pcie_remove(struct udevice *dev) +{ + struct iproc_pcie *pcie = dev_get_priv(dev); + int ret; + + iproc_pcie_reset_map_regs(pcie); + + if (generic_phy_valid(&pcie->phy)) { + ret = generic_phy_power_off(&pcie->phy); + if (ret) { + pr_err("failed to power off PCIe phy\n"); + return ret; + } + + ret = generic_phy_exit(&pcie->phy); + if (ret) { + pr_err("failed to power off PCIe phy\n"); + return ret; + } + } + + return 0; +} + +static const struct udevice_id pci_iproc_ids[] = { + { .compatible = "brcm,iproc-pcie-paxb-v2", + .data = IPROC_PCIE_PAXB_V2 }, + { .compatible = "brcm,iproc-pcie-paxc-v2", + .data = IPROC_PCIE_PAXC_V2 }, + { } +}; + +U_BOOT_DRIVER(pci_iproc) = { + .name = "pci_iproc", + .id = UCLASS_PCI, + .of_match = pci_iproc_ids, + .ops = &iproc_pcie_ops, + .probe = iproc_pcie_probe, + .remove = iproc_pcie_remove, + .priv_auto_alloc_size = sizeof(struct iproc_pcie), + .flags = DM_REMOVE_OS_PREPARE, +};

Hi Rayagonda,
On Tue, 12 May 2020 at 02:00, Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com wrote:
From: Srinath Mannam srinath.mannam@broadcom.com
Add support for IPROC PAXC PCIe RC driver.
Signed-off-by: Srinath Mannam srinath.mannam@broadcom.com Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
drivers/pci/Kconfig | 7 + drivers/pci/Makefile | 1 + drivers/pci/pcie_iproc.c | 1287 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1295 insertions(+) create mode 100644 drivers/pci/pcie_iproc.c
Are you missing a change log?
Regards, Simon

Hi Simon,
On Wed, May 13, 2020 at 7:56 AM Simon Glass sjg@chromium.org wrote:
Hi Rayagonda,
On Tue, 12 May 2020 at 02:00, Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com wrote:
From: Srinath Mannam srinath.mannam@broadcom.com
Add support for IPROC PAXC PCIe RC driver.
Signed-off-by: Srinath Mannam srinath.mannam@broadcom.com Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
drivers/pci/Kconfig | 7 + drivers/pci/Makefile | 1 + drivers/pci/pcie_iproc.c | 1287 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1295 insertions(+) create mode 100644 drivers/pci/pcie_iproc.c
Are you missing a change log?
This is a v1 patch. Sorry I didn't get why change log is required.
Regards, Rayagonda
Regards, Simon

Hi Rayagonda,
On Tue, 12 May 2020 at 02:00, Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com wrote:
From: Srinath Mannam srinath.mannam@broadcom.com
Add support for IPROC PAXC PCIe RC driver.
Signed-off-by: Srinath Mannam srinath.mannam@broadcom.com Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
drivers/pci/Kconfig | 7 + drivers/pci/Makefile | 1 + drivers/pci/pcie_iproc.c | 1287 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1295 insertions(+) create mode 100644 drivers/pci/pcie_iproc.c
Reviewed-by: Simon Glass sjg@chromium.org
Changes below.
For a v1 patch you don't need to put a version. Sorry I was confused by that.
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 437cd9a055..3b134c27a0 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -174,6 +174,13 @@ config PCIE_INTEL_FPGA Say Y here if you want to enable PCIe controller support on Intel FPGA, example Stratix 10.
+config PCIE_IPROC
bool "Iproc PCIe support"
depends on DM_PCI
help
Broadcom iProc PCIe controller driver.
Say Y here if you want to enable Broadcom iProc PCIe controller,
Can you add some more detail here - what features does it support?
config PCI_MVEBU bool "Enable Armada XP/38x PCIe driver" depends on ARCH_MVEBU diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index c051ecc9f3..8d3fb169b1 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o +obj-$(CONFIG_PCIE_IPROC) += pcie_iproc.o obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o obj-$(CONFIG_PCIE_FSL) += pcie_fsl.o pcie_fsl_fixup.o diff --git a/drivers/pci/pcie_iproc.c b/drivers/pci/pcie_iproc.c new file mode 100644 index 0000000000..d77735fcf2 --- /dev/null +++ b/drivers/pci/pcie_iproc.c @@ -0,0 +1,1287 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (C) 2020 Broadcom
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <generic-phy.h> +#include <pci.h> +#include <malloc.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/log2.h>
+#define EP_PERST_SOURCE_SELECT_SHIFT 2 +#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT) +#define EP_MODE_SURVIVE_PERST_SHIFT 1 +#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT) +#define RC_PCIE_RST_OUTPUT_SHIFT 0 +#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT)
+#define CFG_IND_ADDR_MASK 0x00001ffc
+#define CFG_ADDR_BUS_NUM_SHIFT 20 +#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000 +#define CFG_ADDR_DEV_NUM_SHIFT 15 +#define CFG_ADDR_DEV_NUM_MASK 0x000f8000 +#define CFG_ADDR_FUNC_NUM_SHIFT 12 +#define CFG_ADDR_FUNC_NUM_MASK 0x00007000 +#define CFG_ADDR_REG_NUM_SHIFT 2 +#define CFG_ADDR_REG_NUM_MASK 0x00000ffc +#define CFG_ADDR_CFG_TYPE_SHIFT 0 +#define CFG_ADDR_CFG_TYPE_MASK 0x00000003
+#define IPROC_PCI_PM_CAP 0x48 +#define IPROC_PCI_PM_CAP_MASK 0xffff +#define IPROC_PCI_EXP_CAP 0xac
+#define IPROC_PCIE_REG_INVALID 0xffff
+#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +/* CRS Software Visibility capability */ +#define PCI_EXP_RTCAP_CRSVIS 0x0001
+#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */
+#define PCIE_PHYLINKUP_SHIFT 3 +#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT) +#define PCIE_DL_ACTIVE_SHIFT 2 +#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
+/* derive the enum index of the outbound/inbound mapping registers */ +#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2)
+/*
- Maximum number of outbound mapping window sizes that can be supported by any
- OARR/OMAP mapping pair
- */
+#define MAX_NUM_OB_WINDOW_SIZES 4
+#define OARR_VALID_SHIFT 0 +#define OARR_VALID BIT(OARR_VALID_SHIFT) +#define OARR_SIZE_CFG_SHIFT 1
+/*
- Maximum number of inbound mapping region sizes that can be supported by an
- IARR
- */
+#define MAX_NUM_IB_REGION_SIZES 9
+#define IMAP_VALID_SHIFT 0 +#define IMAP_VALID BIT(IMAP_VALID_SHIFT)
+#define APB_ERR_EN_SHIFT 0 +#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT)
+/**
- iProc PCIe host registers
- */
+enum iproc_pcie_reg {
/* clock/reset signal control */
IPROC_PCIE_CLK_CTRL = 0,
/*
* To allow MSI to be steered to an external MSI controller (e.g., ARM
* GICv3 ITS)
*/
IPROC_PCIE_MSI_GIC_MODE,
/*
* IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the
* window where the MSI posted writes are written, for the writes to be
* interpreted as MSI writes.
*/
IPROC_PCIE_MSI_BASE_ADDR,
IPROC_PCIE_MSI_WINDOW_SIZE,
/*
* To hold the address of the register where the MSI writes are
* programed. When ARM GICv3 ITS is used, this should be programmed
programmed
* with the address of the GITS_TRANSLATER register.
*/
IPROC_PCIE_MSI_ADDR_LO,
IPROC_PCIE_MSI_ADDR_HI,
/* enable MSI */
IPROC_PCIE_MSI_EN_CFG,
/* allow access to root complex configuration space */
IPROC_PCIE_CFG_IND_ADDR,
IPROC_PCIE_CFG_IND_DATA,
/* allow access to device configuration space */
IPROC_PCIE_CFG_ADDR,
IPROC_PCIE_CFG_DATA,
/* enable INTx */
IPROC_PCIE_INTX_EN,
IPROC_PCIE_INTX_CSR,
/* outbound address mapping */
IPROC_PCIE_OARR0,
IPROC_PCIE_OMAP0,
IPROC_PCIE_OARR1,
IPROC_PCIE_OMAP1,
IPROC_PCIE_OARR2,
IPROC_PCIE_OMAP2,
IPROC_PCIE_OARR3,
IPROC_PCIE_OMAP3,
/* inbound address mapping */
IPROC_PCIE_IARR0,
IPROC_PCIE_IMAP0,
IPROC_PCIE_IARR1,
IPROC_PCIE_IMAP1,
IPROC_PCIE_IARR2,
IPROC_PCIE_IMAP2,
IPROC_PCIE_IARR3,
IPROC_PCIE_IMAP3,
IPROC_PCIE_IARR4,
IPROC_PCIE_IMAP4,
/* config read status */
IPROC_PCIE_CFG_RD_STATUS,
/* link status */
IPROC_PCIE_LINK_STATUS,
/* enable APB error for unsupported requests */
IPROC_PCIE_APB_ERR_EN,
/* Ordering Mode configuration registers */
IPROC_PCIE_ORDERING_CFG,
IPROC_PCIE_IMAP0_RO_CONTROL,
IPROC_PCIE_IMAP1_RO_CONTROL,
IPROC_PCIE_IMAP2_RO_CONTROL,
IPROC_PCIE_IMAP3_RO_CONTROL,
IPROC_PCIE_IMAP4_RO_CONTROL,
/* total number of core registers */
IPROC_PCIE_MAX_NUM_REG,
+};
+/* iProc PCIe PAXB v2 registers */ +static const u16 iproc_pcie_reg_paxb_v2[] = {
[IPROC_PCIE_CLK_CTRL] = 0x000,
[IPROC_PCIE_CFG_IND_ADDR] = 0x120,
[IPROC_PCIE_CFG_IND_DATA] = 0x124,
[IPROC_PCIE_CFG_ADDR] = 0x1f8,
[IPROC_PCIE_CFG_DATA] = 0x1fc,
[IPROC_PCIE_INTX_EN] = 0x330,
[IPROC_PCIE_INTX_CSR] = 0x334,
[IPROC_PCIE_OARR0] = 0xd20,
[IPROC_PCIE_OMAP0] = 0xd40,
[IPROC_PCIE_OARR1] = 0xd28,
[IPROC_PCIE_OMAP1] = 0xd48,
[IPROC_PCIE_OARR2] = 0xd60,
[IPROC_PCIE_OMAP2] = 0xd68,
[IPROC_PCIE_OARR3] = 0xdf0,
[IPROC_PCIE_OMAP3] = 0xdf8,
[IPROC_PCIE_IARR0] = 0xd00,
[IPROC_PCIE_IMAP0] = 0xc00,
[IPROC_PCIE_IARR2] = 0xd10,
[IPROC_PCIE_IMAP2] = 0xcc0,
[IPROC_PCIE_IARR3] = 0xe00,
[IPROC_PCIE_IMAP3] = 0xe08,
[IPROC_PCIE_IARR4] = 0xe68,
[IPROC_PCIE_IMAP4] = 0xe70,
[IPROC_PCIE_CFG_RD_STATUS] = 0xee0,
[IPROC_PCIE_LINK_STATUS] = 0xf0c,
[IPROC_PCIE_APB_ERR_EN] = 0xf40,
[IPROC_PCIE_ORDERING_CFG] = 0x2000,
[IPROC_PCIE_IMAP0_RO_CONTROL] = 0x201c,
[IPROC_PCIE_IMAP1_RO_CONTROL] = 0x2020,
[IPROC_PCIE_IMAP2_RO_CONTROL] = 0x2024,
[IPROC_PCIE_IMAP3_RO_CONTROL] = 0x2028,
[IPROC_PCIE_IMAP4_RO_CONTROL] = 0x202c,
+};
+/* iProc PCIe PAXC v2 registers */ +static const u16 iproc_pcie_reg_paxc_v2[] = {
[IPROC_PCIE_MSI_GIC_MODE] = 0x050,
[IPROC_PCIE_MSI_BASE_ADDR] = 0x074,
[IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078,
[IPROC_PCIE_MSI_ADDR_LO] = 0x07c,
[IPROC_PCIE_MSI_ADDR_HI] = 0x080,
[IPROC_PCIE_MSI_EN_CFG] = 0x09c,
[IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
[IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
[IPROC_PCIE_CFG_ADDR] = 0x1f8,
[IPROC_PCIE_CFG_DATA] = 0x1fc,
+};
+/**
- List of device IDs of controllers that have corrupted
- capability list that require SW fixup
- */
+static const u16 iproc_pcie_corrupt_cap_did[] = {
0x16cd,
0x16f0,
0xd802,
0xd804
+};
+enum iproc_pcie_type {
IPROC_PCIE_PAXB_V2,
IPROC_PCIE_PAXC,
IPROC_PCIE_PAXC_V2,
+};
+/**
- struct iproc_pcie_ob - iProc PCIe outbound mapping
- @axi_offset: offset from the AXI address to the internal address used by
- the iProc PCIe core
- @nr_windows: total number of supported outbound mapping windows
- */
+struct iproc_pcie_ob {
resource_size_t axi_offset;
unsigned int nr_windows;
+};
+/**
- struct iproc_pcie_ib - iProc PCIe inbound mapping
- @nr_regions: total number of supported inbound mapping regions
- */
+struct iproc_pcie_ib {
unsigned int nr_regions;
+};
+/**
- struct iproc_pcie_ob_map - outbound mapping controller specific parameters
- @window_sizes: list of supported outbound mapping window sizes in MB
- @nr_sizes: number of supported outbound mapping window sizes
- */
+struct iproc_pcie_ob_map {
resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES];
unsigned int nr_sizes;
+};
+static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = {
{
/* OARR0/OMAP0 */
.window_sizes = { 128, 256 },
.nr_sizes = 2,
},
{
/* OARR1/OMAP1 */
.window_sizes = { 128, 256 },
.nr_sizes = 2,
},
{
/* OARR2/OMAP2 */
.window_sizes = { 128, 256, 512, 1024 },
.nr_sizes = 4,
},
{
/* OARR3/OMAP3 */
.window_sizes = { 128, 256, 512, 1024 },
.nr_sizes = 4,
},
+};
+/**
- iProc PCIe inbound mapping type
- */
+enum iproc_pcie_ib_map_type {
/* for DDR memory */
IPROC_PCIE_IB_MAP_MEM = 0,
/* for device I/O memory */
IPROC_PCIE_IB_MAP_IO,
/* invalid or unused */
IPROC_PCIE_IB_MAP_INVALID
+};
+/**
- struct iproc_pcie_ib_map - inbound mapping controller specific parameters
- @type: inbound mapping region type
- @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or SZ_1G
- @region_sizes: list of supported inbound mapping region sizes in KB, MB, or
- GB, depedning on the size unit
depending
- @nr_sizes: number of supported inbound mapping region sizes
- @nr_windows: number of supported inbound mapping windows for the region
- @imap_addr_offset: register offset between the upper and lower 32-bit
- IMAP address registers
- @imap_window_offset: register offset between each IMAP window
- */
+struct iproc_pcie_ib_map {
enum iproc_pcie_ib_map_type type;
unsigned int size_unit;
resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES];
unsigned int nr_sizes;
unsigned int nr_windows;
u16 imap_addr_offset;
u16 imap_window_offset;
+};
+static const struct iproc_pcie_ib_map paxb_v2_ib_map[] = {
{
/* IARR0/IMAP0 */
.type = IPROC_PCIE_IB_MAP_IO,
.size_unit = SZ_1K,
.region_sizes = { 32 },
.nr_sizes = 1,
.nr_windows = 8,
.imap_addr_offset = 0x40,
.imap_window_offset = 0x4,
},
{
/* IARR1/IMAP1 (currently unused) */
.type = IPROC_PCIE_IB_MAP_INVALID,
},
{
/* IARR2/IMAP2 */
.type = IPROC_PCIE_IB_MAP_MEM,
.size_unit = SZ_1M,
.region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192,
16384 },
.nr_sizes = 9,
.nr_windows = 1,
.imap_addr_offset = 0x4,
.imap_window_offset = 0x8,
},
{
/* IARR3/IMAP3 */
.type = IPROC_PCIE_IB_MAP_MEM,
.size_unit = SZ_1G,
.region_sizes = { 1, 2, 4, 8, 16, 32 },
.nr_sizes = 6,
.nr_windows = 8,
.imap_addr_offset = 0x4,
.imap_window_offset = 0x8,
},
{
/* IARR4/IMAP4 */
.type = IPROC_PCIE_IB_MAP_MEM,
.size_unit = SZ_1G,
.region_sizes = { 32, 64, 128, 256, 512 },
.nr_sizes = 5,
.nr_windows = 8,
.imap_addr_offset = 0x4,
.imap_window_offset = 0x8,
},
+};
+/**
- struct iproc_pcie - iproc pcie device instance
- @dev: pointer to pcie udevice
- @base: device I/O base address
- @type: pci device type, PAXC or PAXB
- @reg_offsets: pointer to pcie host register
- @fix_paxc_cap: paxc capability
- @need_ob_cfg: outbound mapping status
- @ob: pcie outbound mapping
- @ob_map: pointer to outbound mapping parameters
- @need_ib_cfg: inbound mapping status
- @ib: pcie inbound mapping
- @ib_map: pointer to inbound mapping parameters
- @ep_is_internal: ep status
- @phy: phy device
- @link_is_active: link up status
- @has_apb_err_disable: apb error status
- */
+struct iproc_pcie {
struct udevice *dev;
void __iomem *base;
enum iproc_pcie_type type;
u16 *reg_offsets;
bool fix_paxc_cap;
bool need_ob_cfg;
struct iproc_pcie_ob ob;
const struct iproc_pcie_ob_map *ob_map;
bool need_ib_cfg;
struct iproc_pcie_ib ib;
const struct iproc_pcie_ib_map *ib_map;
bool ep_is_internal;
struct phy phy;
bool link_is_active;
bool has_apb_err_disable;
+};
+static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset) +{
return !!(reg_offset == IPROC_PCIE_REG_INVALID);
+}
+static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg)
+{
return pcie->reg_offsets[reg];
+}
+static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg)
+{
u16 offset = iproc_pcie_reg_offset(pcie, reg);
if (iproc_pcie_reg_is_invalid(offset))
return 0;
return readl(pcie->base + offset);
+}
+static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg, u32 val)
+{
u16 offset = iproc_pcie_reg_offset(pcie, reg);
if (iproc_pcie_reg_is_invalid(offset))
return;
writel(val, pcie->base + offset);
+}
+static int iproc_pcie_map_ep_cfg_reg(const struct udevice *udev, pci_dev_t bdf,
uint where, void **paddress)
Please use dev insead of udev throughout.
+{
struct iproc_pcie *pcie = dev_get_priv(udev);
unsigned int busno = PCI_BUS(bdf);
unsigned int slot = PCI_DEV(bdf);
unsigned int fn = PCI_FUNC(bdf);
u16 offset;
u32 val;
/* root complex access */
if (busno == 0) {
if (slot > 0 || fn > 0)
return -ENODEV;
Please use a different error. Perhaps -ENXIO?
This is reserved for driver model to know there is no device present. But clearly there is a device as you are in its driver.
iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR,
where & CFG_IND_ADDR_MASK);
offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA);
if (iproc_pcie_reg_is_invalid(offset))
return -ENODEV;
*paddress = (pcie->base + offset);
return 0;
}
if (!pcie->link_is_active)
return -ENODEV;
/* EP device access */
val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
(slot << CFG_ADDR_DEV_NUM_SHIFT) |
(fn << CFG_ADDR_FUNC_NUM_SHIFT) |
(where & CFG_ADDR_REG_NUM_MASK) |
(1 & CFG_ADDR_CFG_TYPE_MASK);
iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val);
offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA);
if (iproc_pcie_reg_is_invalid(offset))
return -ENODEV;
*paddress = (pcie->base + offset);
return 0;
+}
+static void iproc_pcie_fix_cap(struct iproc_pcie *pcie, int where, ulong *val) +{
u32 i, dev_id;
switch (where & ~0x3) {
case PCI_VENDOR_ID:
dev_id = *val >> 16;
/*
* Activate fixup for those controllers that have corrupted
* capability list registers
*/
for (i = 0; i < ARRAY_SIZE(iproc_pcie_corrupt_cap_did); i++)
if (dev_id == iproc_pcie_corrupt_cap_did[i])
pcie->fix_paxc_cap = true;
break;
case IPROC_PCI_PM_CAP:
if (pcie->fix_paxc_cap) {
/* advertise PM, force next capability to PCIe */
*val &= ~IPROC_PCI_PM_CAP_MASK;
*val |= IPROC_PCI_EXP_CAP << 8 | PCI_CAP_ID_PM;
}
break;
case IPROC_PCI_EXP_CAP:
if (pcie->fix_paxc_cap) {
/* advertise root port, version 2, terminate here */
*val = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2) << 16 |
PCI_CAP_ID_EXP;
}
break;
case IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL:
/* Don't advertise CRS SV support */
*val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
break;
default:
break;
}
+}
+static int iproc_pci_raw_config_read32(struct iproc_pcie *pcie,
unsigned int devfn, int where,
int size, u32 *val)
+{
void __iomem *addr;
int ret;
ret = iproc_pcie_map_ep_cfg_reg(pcie->dev, devfn, where & ~0x3, &addr);
if (ret) {
*val = ~0;
return -EINVAL;
return ret
No need to change the error. Please fix globally.
}
*val = readl(addr);
if (size <= 2)
*val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
Drop outer (). Please fix globally.
return 0;
+}
+static int iproc_pci_raw_config_write32(struct iproc_pcie *pcie,
unsigned int devfn, int where,
int size, u32 val)
+{
void __iomem *addr;
int ret;
u32 mask, tmp;
ret = iproc_pcie_map_ep_cfg_reg(pcie->dev, devfn, where & ~0x3, &addr);
if (ret)
return -EINVAL;
if (size == 4) {
writel(val, addr);
return 0;
}
mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
tmp = readl(addr) & mask;
tmp |= val << ((where & 0x3) * 8);
writel(tmp, addr);
return 0;
+}
+/**
- iproc_pcie_apb_err_disable() - configure apb error
- APB error forwarding can be disabled during access of configuration
- registers of the endpoint device, to prevent unsupported requests
- (typically seen during enumeration with multi-function devices) from
- triggering a system exception.
- @bus: pcie udevice
- @bdf: pdf value
- @disabled: flag to enable/disabled apb error
disable
- */
+static inline void iproc_pcie_apb_err_disable(const struct udevice *bus,
pci_dev_t bdf, bool disable)
+{
struct iproc_pcie *pcie = dev_get_priv(bus);
u32 val;
if (PCI_BUS(bdf) && pcie->has_apb_err_disable) {
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN);
if (disable)
val &= ~APB_ERR_EN;
else
val |= APB_ERR_EN;
iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val);
}
+}
+static int iproc_pcie_config_read32(const struct udevice *bus, pci_dev_t bdf,
uint offset, ulong *valuep,
enum pci_size_t size)
+{
struct iproc_pcie *pcie = dev_get_priv(bus);
int ret;
ulong data;
iproc_pcie_apb_err_disable(bus, bdf, true);
ret = pci_generic_mmap_read_config(bus, iproc_pcie_map_ep_cfg_reg,
bdf, offset, &data, PCI_SIZE_32);
iproc_pcie_apb_err_disable(bus, bdf, false);
if (size <= PCI_SIZE_16)
*valuep = (data >> (8 * (offset & 3))) &
((1 << (BIT(size) * 8)) - 1);
else
*valuep = data;
if (!ret && PCI_BUS(bdf) == 0)
iproc_pcie_fix_cap(pcie, offset, valuep);
return ret;
+}
+static int iproc_pcie_config_write32(struct udevice *bus, pci_dev_t bdf,
uint offset, ulong value,
enum pci_size_t size)
+{
void *addr;
ulong mask, tmp;
int ret;
ret = iproc_pcie_map_ep_cfg_reg(bus, bdf, offset, &addr);
if (ret)
return ret;
if (size == PCI_SIZE_32) {
writel(value, addr);
return ret;
But ret will be 0, so just return 0
}
iproc_pcie_apb_err_disable(bus, bdf, true);
mask = ~(((1 << (BIT(size) * 8)) - 1) << ((offset & 0x3) * 8));
Should that expression go in a function>
tmp = readl(addr) & mask;
tmp |= (value << ((offset & 0x3) * 8));
writel(tmp, addr);
Use setbits_le32() instead
iproc_pcie_apb_err_disable(bus, bdf, false);
return ret;
+}
+const static struct dm_pci_ops iproc_pcie_ops = {
.read_config = iproc_pcie_config_read32,
.write_config = iproc_pcie_config_write32,
+};
+static int iproc_pcie_rev_init(struct iproc_pcie *pcie) +{
unsigned int reg_idx;
const u16 *regs;
u16 num_elements;
switch (pcie->type) {
case IPROC_PCIE_PAXC_V2:
pcie->ep_is_internal = true;
regs = iproc_pcie_reg_paxc_v2;
num_elements = ARRAY_SIZE(iproc_pcie_reg_paxc_v2);
break;
case IPROC_PCIE_PAXB_V2:
regs = iproc_pcie_reg_paxb_v2;
num_elements = ARRAY_SIZE(iproc_pcie_reg_paxb_v2);
pcie->has_apb_err_disable = true;
if (pcie->need_ob_cfg) {
pcie->ob.axi_offset = 0;
pcie->ob_map = paxb_v2_ob_map;
pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map);
}
pcie->need_ib_cfg = true;
pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map);
pcie->ib_map = paxb_v2_ib_map;
break;
default:
dev_dbg(pcie->dev, "incompatible iProc PCIe interface\n");
return -EINVAL;
}
pcie->reg_offsets = calloc(IPROC_PCIE_MAX_NUM_REG,
sizeof(*pcie->reg_offsets));
if (!pcie->reg_offsets)
return -ENOMEM;
/* go through the register table and populate all valid registers */
pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ?
IPROC_PCIE_REG_INVALID : regs[0];
for (reg_idx = 1; reg_idx < num_elements; reg_idx++)
pcie->reg_offsets[reg_idx] = regs[reg_idx] ?
regs[reg_idx] : IPROC_PCIE_REG_INVALID;
return 0;
+}
+static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie,
int window_idx)
+{
u32 val;
val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx));
return !!(val & OARR_VALID);
+}
+static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx,
int size_idx, u64 axi_addr, u64 pci_addr)
+{
u16 oarr_offset, omap_offset;
/*
* Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based
* on window index.
*/
oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0,
window_idx));
omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0,
window_idx));
if (iproc_pcie_reg_is_invalid(oarr_offset) ||
iproc_pcie_reg_is_invalid(omap_offset))
return -EINVAL;
/*
* Program the OARR registers. The upper 32-bit OARR register is
* always right after the lower 32-bit OARR register.
*/
writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) |
OARR_VALID, pcie->base + oarr_offset);
writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4);
/* now program the OMAP registers */
writel(lower_32_bits(pci_addr), pcie->base + omap_offset);
writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4);
debug("ob window [%d]: offset 0x%x axi %pap pci %pap\n",
window_idx, oarr_offset, &axi_addr, &pci_addr);
debug("oarr lo 0x%x oarr hi 0x%x\n",
readl(pcie->base + oarr_offset),
readl(pcie->base + oarr_offset + 4));
debug("omap lo 0x%x omap hi 0x%x\n",
readl(pcie->base + omap_offset),
readl(pcie->base + omap_offset + 4));
return 0;
+}
+/**
- iproc_pcie_setup_ob() - setup outbound address mapping
- Some iProc SoCs require the SW to configure the outbound address mapping
- Outbound address translation:
- iproc_pcie_address = axi_address - axi_offset
- OARR = iproc_pcie_address
- OMAP = pci_addr
- axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
- @pcie: pcie device
- @axi_addr: axi address to be translated
- @pci_addr: pci address
- @size: window size
- @return: 0 on success and -ve on failure
- */
+static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
u64 pci_addr, resource_size_t size)
+{
struct iproc_pcie_ob *ob = &pcie->ob;
int ret = -EINVAL, window_idx, size_idx;
if (axi_addr < ob->axi_offset) {
pr_err("axi address %pap less than offset %pap\n",
&axi_addr, &ob->axi_offset);
return -EINVAL;
}
/*
* Translate the AXI address to the internal address used by the iProc
* PCIe core before programming the OARR
*/
axi_addr -= ob->axi_offset;
/* iterate through all OARR/OMAP mapping windows */
for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) {
const struct iproc_pcie_ob_map *ob_map =
&pcie->ob_map[window_idx];
/*
* If current outbound window is already in use, move on to the
* next one.
*/
if (iproc_pcie_ob_is_valid(pcie, window_idx))
continue;
/*
* Iterate through all supported window sizes within the
* OARR/OMAP pair to find a match. Go through the window sizes
* in a descending order.
*/
for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0;
size_idx--) {
resource_size_t window_size =
ob_map->window_sizes[size_idx] * SZ_1M;
/*
* Keep iterating until we reach the last window and
* with the minimal window size at index zero. In this
* case, we take a compromise by mapping it using the
* minimum window size that can be supported
*/
if (size < window_size) {
if (size_idx > 0 || window_idx > 0)
continue;
/*
* For the corner case of reaching the minimal
* window size that can be supported on the
* last window
*/
axi_addr = ALIGN_DOWN(axi_addr, window_size);
pci_addr = ALIGN_DOWN(pci_addr, window_size);
size = window_size;
}
if (!IS_ALIGNED(axi_addr, window_size) ||
!IS_ALIGNED(pci_addr, window_size)) {
pr_err("axi %pap or pci %pap not aligned\n",
&axi_addr, &pci_addr);
return -EINVAL;
}
/*
* Match found! Program both OARR and OMAP and mark
* them as a valid entry.
*/
ret = iproc_pcie_ob_write(pcie, window_idx, size_idx,
axi_addr, pci_addr);
if (ret)
goto err_ob;
size -= window_size;
if (size == 0)
return 0;
/*
* If we are here, we are done with the current window,
* but not yet finished all mappings. Need to move on
* to the next window.
*/
axi_addr += window_size;
pci_addr += window_size;
break;
}
}
+err_ob:
pr_err("unable to configure outbound mapping\n");
pr_err("axi %pap, axi offset %pap, pci %pap, res size %pap\n",
&axi_addr, &ob->axi_offset, &pci_addr, &size);
return ret;
+}
+static int iproc_pcie_map_ranges(struct udevice *dev) +{
struct iproc_pcie *pcie = dev_get_priv(dev);
struct udevice *bus = pci_get_controller(dev);
struct pci_controller *hose = dev_get_uclass_priv(bus);
int i, ret;
for (i = 0; i < hose->region_count; i++) {
if (hose->regions[i].flags == PCI_REGION_MEM ||
hose->regions[i].flags == PCI_REGION_PREFETCH) {
debug("%d: bus_addr %p, axi_addr %p, size 0x%lx\n",
i, &hose->regions[i].bus_start,
&hose->regions[i].phys_start,
hose->regions[i].size);
ret = iproc_pcie_setup_ob(pcie,
hose->regions[i].phys_start,
hose->regions[i].bus_start,
hose->regions[i].size);
if (ret)
return ret;
}
}
return 0;
+}
+static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie,
int region_idx)
+{
const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
u32 val;
val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx));
return !!(val & (BIT(ib_map->nr_sizes) - 1));
+}
+static inline bool
please keep on same line as function name
+iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map,
enum iproc_pcie_ib_map_type type)
+{
return !!(ib_map->type == type);
+}
+static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
int size_idx, int nr_windows, u64 axi_addr,
u64 pci_addr, resource_size_t size)
+{
const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
u16 iarr_offset, imap_offset;
u32 val;
int window_idx;
iarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_IARR0,
region_idx));
imap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_IMAP0,
region_idx));
if (iproc_pcie_reg_is_invalid(iarr_offset) ||
iproc_pcie_reg_is_invalid(imap_offset))
return -EINVAL;
debug("ib region [%d]: offset 0x%x axi %pap pci %pap\n",
region_idx, iarr_offset, &axi_addr, &pci_addr);
/*
* Program the IARR registers. The upper 32-bit IARR register is
* always right after the lower 32-bit IARR register.
*/
writel(lower_32_bits(pci_addr) | BIT(size_idx),
pcie->base + iarr_offset);
writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4);
debug("iarr lo 0x%x iarr hi 0x%x\n",
readl(pcie->base + iarr_offset),
readl(pcie->base + iarr_offset + 4));
/*
* Now program the IMAP registers. Each IARR region may have one or
* more IMAP windows.
*/
size >>= ilog2(nr_windows);
for (window_idx = 0; window_idx < nr_windows; window_idx++) {
val = readl(pcie->base + imap_offset);
val |= lower_32_bits(axi_addr) | IMAP_VALID;
writel(val, pcie->base + imap_offset);
writel(upper_32_bits(axi_addr),
pcie->base + imap_offset + ib_map->imap_addr_offset);
debug("imap window [%d] lo 0x%x hi 0x%x\n",
window_idx, readl(pcie->base + imap_offset),
readl(pcie->base + imap_offset +
ib_map->imap_addr_offset));
imap_offset += ib_map->imap_window_offset;
axi_addr += size;
}
return 0;
+}
+/**
- iproc_pcie_setup_ib() - setup inbound address mapping
- @pcie: pcie device
- @axi_addr: axi address to be translated
- @pci_addr: pci address
- @size: window size
- @type: inbound mapping type
- @return: 0 on success and -ve on failure
- */
+static int iproc_pcie_setup_ib(struct iproc_pcie *pcie, u64 axi_addr,
u64 pci_addr, resource_size_t size,
enum iproc_pcie_ib_map_type type)
+{
struct iproc_pcie_ib *ib = &pcie->ib;
int ret;
unsigned int region_idx, size_idx;
/* iterate through all IARR mapping regions */
for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) {
const struct iproc_pcie_ib_map *ib_map =
&pcie->ib_map[region_idx];
/*
* If current inbound region is already in use or not a
* compatible type, move on to the next.
*/
if (iproc_pcie_ib_is_in_use(pcie, region_idx) ||
!iproc_pcie_ib_check_type(ib_map, type))
continue;
/* iterate through all supported region sizes to find a match */
for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) {
resource_size_t region_size =
ib_map->region_sizes[size_idx] * ib_map->size_unit;
if (size != region_size)
continue;
if (!IS_ALIGNED(axi_addr, region_size) ||
!IS_ALIGNED(pci_addr, region_size)) {
pr_err("axi %pap or pci %pap not aligned\n",
&axi_addr, &pci_addr);
return -EINVAL;
}
/* Match found! Program IARR and all IMAP windows. */
ret = iproc_pcie_ib_write(pcie, region_idx, size_idx,
ib_map->nr_windows, axi_addr,
pci_addr, size);
if (ret)
goto err_ib;
else
return 0;
}
}
ret = -EINVAL;
+err_ib:
pr_err("unable to configure inbound mapping\n");
pr_err("axi %pap, pci %pap, res size %pap\n",
&axi_addr, &pci_addr, &size);
return ret;
+}
+static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) +{
int ret;
struct pci_region regions;
int i = 0;
while (!pci_get_dma_regions(pcie->dev, ®ions, i)) {
dev_dbg(pcie->dev,
"dma %d: bus_addr %#lx, axi_addr %#llx, size %#lx\n",
i, regions.bus_start, regions.phys_start, regions.size);
/* Each range entry corresponds to an inbound mapping region */
ret = iproc_pcie_setup_ib(pcie, regions.phys_start,
regions.bus_start,
regions.size,
IPROC_PCIE_IB_MAP_MEM);
if (ret)
return ret;
i++;
}
blank line before return (please check throughout)
return 0;
+}
+static void iproc_pcie_reset_map_regs(struct iproc_pcie *pcie) +{
struct iproc_pcie_ib *ib = &pcie->ib;
struct iproc_pcie_ob *ob = &pcie->ob;
int window_idx, region_idx;
if (pcie->ep_is_internal)
return;
/* iterate through all OARR mapping regions */
for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) {
iproc_pcie_write_reg(pcie, MAP_REG(IPROC_PCIE_OARR0,
window_idx), 0);
}
/* iterate through all IARR mapping regions */
for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) {
iproc_pcie_write_reg(pcie, MAP_REG(IPROC_PCIE_IARR0,
region_idx), 0);
}
+}
+static void iproc_pcie_reset(struct iproc_pcie *pcie) +{
u32 val;
/*
* PAXC and the internal emulated endpoint device downstream should not
* be reset. If firmware has been loaded on the endpoint device at an
* earlier boot stage, reset here causes issues.
*/
if (pcie->ep_is_internal)
return;
/*
* Select perst_b signal as reset source. Put the device into reset,
* and then bring it out of reset
*/
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
~RC_PCIE_RST_OUTPUT;
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
udelay(250);
val |= RC_PCIE_RST_OUTPUT;
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
mdelay(100);
That is such a long time! Do you really need this? If so, please add a comment.
+}
+static inline bool iproc_pcie_link_is_active(struct iproc_pcie *pcie) +{
u32 val;
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
return !!((val & PCIE_PHYLINKUP) && (val & PCIE_DL_ACTIVE));
That is too confusing. Can you simplify or break it up?
Or perhaps you just can drop the !! ?
+}
+static int iproc_pcie_check_link(struct iproc_pcie *pcie) +{
u32 link_status, class;
pcie->link_is_active = false;
/* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */
+#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c +#define PCI_CLASS_BRIDGE_MASK 0xffff00 +#define PCI_CLASS_BRIDGE_SHIFT 8
These should go ta the top of the file. Below also
iproc_pci_raw_config_read32(pcie, 0,
PCI_BRIDGE_CTRL_REG_OFFSET,
4, &class);
class &= ~PCI_CLASS_BRIDGE_MASK;
class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT);
iproc_pci_raw_config_write32(pcie, 0,
PCI_BRIDGE_CTRL_REG_OFFSET,
4, class);
/*
* PAXC connects to emulated endpoint devices directly and does not
* have a Serdes. Therefore skip the link detection logic here.
*/
if (pcie->ep_is_internal) {
pcie->link_is_active = true;
return 0;
}
if (!iproc_pcie_link_is_active(pcie)) {
pr_err("PHY or data link is INACTIVE!\n");
return -ENODEV;
Please avoid -ENODEV globally.
}
+#define PCI_TARGET_LINK_SPEED_MASK 0xf +#define PCI_TARGET_LINK_WIDTH_MASK 0x3f +#define PCI_TARGET_LINK_WIDTH_OFFSET 0x4
/* check link status to see if link is active */
iproc_pci_raw_config_read32(pcie, 0,
IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA,
2, &link_status);
if (link_status & PCI_EXP_LNKSTA_NLW)
pcie->link_is_active = true;
if (pcie->link_is_active)
pr_info("link UP @ Speed Gen-%d and width-x%d\n",
link_status & PCI_TARGET_LINK_SPEED_MASK,
(link_status >> PCI_TARGET_LINK_WIDTH_OFFSET) &
PCI_TARGET_LINK_WIDTH_MASK);
else
pr_info("link DOWN\n");
return 0;
+}
+static int iproc_pcie_probe(struct udevice *dev) +{
struct iproc_pcie *pcie = dev_get_priv(dev);
int ret;
pcie->type = (enum iproc_pcie_type)dev_get_driver_data(dev);
debug("PAX type %d\n", pcie->type);
pcie->base = dev_read_addr_ptr(dev);
debug("PAX reg base %p\n", pcie->base);
if (!pcie->base)
return -ENODEV;
if (dev_read_bool(dev, "brcm,pcie-ob"))
pcie->need_ob_cfg = true;
pcie->dev = dev;
ret = iproc_pcie_rev_init(pcie);
if (ret)
return ret;
if (!pcie->ep_is_internal) {
ret = generic_phy_get_by_name(dev, "pcie-phy", &pcie->phy);
if (!ret) {
ret = generic_phy_init(&pcie->phy);
if (ret) {
pr_err("failed to init %s PHY\n", dev->name);
return ret;
}
ret = generic_phy_power_on(&pcie->phy);
if (ret) {
pr_err("power on %s PHY failed\n", dev->name);
goto err_exit_phy;
}
}
}
iproc_pcie_reset(pcie);
if (pcie->need_ob_cfg) {
ret = iproc_pcie_map_ranges(dev);
if (ret) {
pr_err("outbound map failed\n");
goto err_power_off_phy;
}
}
if (pcie->need_ib_cfg) {
ret = iproc_pcie_map_dma_ranges(pcie);
if (ret) {
pr_err("inbound map failed\n");
goto err_power_off_phy;
}
}
if (iproc_pcie_check_link(pcie))
pr_info("no PCIe EP device detected\n");
return 0;
+err_power_off_phy:
generic_phy_power_off(&pcie->phy);
+err_exit_phy:
generic_phy_exit(&pcie->phy);
return ret;
+}
+static int iproc_pcie_remove(struct udevice *dev) +{
struct iproc_pcie *pcie = dev_get_priv(dev);
int ret;
iproc_pcie_reset_map_regs(pcie);
if (generic_phy_valid(&pcie->phy)) {
ret = generic_phy_power_off(&pcie->phy);
if (ret) {
pr_err("failed to power off PCIe phy\n");
return ret;
}
ret = generic_phy_exit(&pcie->phy);
if (ret) {
pr_err("failed to power off PCIe phy\n");
return ret;
}
}
return 0;
+}
+static const struct udevice_id pci_iproc_ids[] = {
{ .compatible = "brcm,iproc-pcie-paxb-v2",
.data = IPROC_PCIE_PAXB_V2 },
{ .compatible = "brcm,iproc-pcie-paxc-v2",
.data = IPROC_PCIE_PAXC_V2 },
{ }
+};
+U_BOOT_DRIVER(pci_iproc) = {
.name = "pci_iproc",
.id = UCLASS_PCI,
.of_match = pci_iproc_ids,
.ops = &iproc_pcie_ops,
.probe = iproc_pcie_probe,
.remove = iproc_pcie_remove,
.priv_auto_alloc_size = sizeof(struct iproc_pcie),
.flags = DM_REMOVE_OS_PREPARE,
+};
2.17.1
Regards, Simon

On Tue, May 12, 2020 at 01:29:50PM +0530, Rayagonda Kokatanur wrote:
From: Srinath Mannam srinath.mannam@broadcom.com
Add support for IPROC PAXC PCIe RC driver.
Signed-off-by: Srinath Mannam srinath.mannam@broadcom.com Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/master, thanks!
participants (3)
-
Rayagonda Kokatanur
-
Simon Glass
-
Tom Rini