summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig37
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pci-uclass.c288
-rw-r--r--drivers/pci/pci_octeontx.c364
4 files changed, 648 insertions, 42 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 5e0a39396b..06d39df1d3 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -43,6 +43,35 @@ config PCI_PNP
help
Enable PCI memory and I/O space resource allocation and assignment.
+config PCI_REGION_MULTI_ENTRY
+ bool "Enable Multiple entries of region type MEMORY in ranges for PCI"
+ depends on PCI || DM_PCI
+ default n
+ help
+ Enable PCI memory regions to be of multiple entry. Multiple entry
+ here refers to allow more than one count of address ranges for MEMORY
+ region type. This helps to add support for SoC's like OcteonTX/TX2
+ where every peripheral is on the PCI bus.
+
+config PCI_SRIOV
+ bool "Enable Single Root I/O Virtualization support for PCI"
+ depends on PCI || DM_PCI
+ default n
+ help
+ Say Y here if you want to enable PCI Single Root I/O Virtualization
+ capability support. This helps to enumerate Virtual Function devices
+ if available on a PCI Physical Function device and probe for
+ applicable drivers.
+
+config PCI_ARID
+ bool "Enable Alternate Routing-ID support for PCI"
+ depends on PCI || DM_PCI
+ default n
+ help
+ Say Y here if you want to enable Alternate Routing-ID capability
+ support on PCI devices. This helps to skip some devices in BDF
+ scan that are not present.
+
config PCIE_ECAM_GENERIC
bool "Generic ECAM-based PCI host controller support"
default n
@@ -120,6 +149,14 @@ config PCI_TEGRA
with a total of 5 lanes. Some boards require this for Ethernet
support to work (e.g. beaver, jetson-tk1).
+config PCI_OCTEONTX
+ bool "OcteonTX PCI support"
+ depends on (ARCH_OCTEONTX || ARCH_OCTEONTX2)
+ help
+ Enable support for the OcteonTX/TX2 SoC family ECAM/PEM controllers.
+ These controllers provide PCI configuration access to all on-board
+ peripherals so it should only be disabled for testing purposes
+
config PCI_XILINX
bool "Xilinx AXI Bridge for PCI Express"
depends on DM_PCI
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 9db90fb53c..8b4d49a590 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -49,3 +49,4 @@ obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o
obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o
obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o
+obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 40cc9f1090..d8a6647a1d 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -539,7 +539,8 @@ int pci_auto_config_devices(struct udevice *bus)
int ret;
debug("%s: device %s\n", __func__, dev->name);
- if (dev_read_bool(dev, "pci,no-autoconfig"))
+ if (dev_of_valid(dev) &&
+ dev_read_bool(dev, "pci,no-autoconfig"))
continue;
ret = dm_pciauto_config_device(dev);
if (ret < 0)
@@ -620,10 +621,19 @@ int dm_pci_hose_probe_bus(struct udevice *bus)
{
int sub_bus;
int ret;
+ int ea_pos;
+ u8 reg;
debug("%s\n", __func__);
- sub_bus = pci_get_bus_max() + 1;
+ ea_pos = dm_pci_find_capability(bus, PCI_CAP_ID_EA);
+ if (ea_pos) {
+ dm_pci_read_config8(bus, ea_pos + sizeof(u32) + sizeof(u8),
+ &reg);
+ sub_bus = reg;
+ } else {
+ sub_bus = pci_get_bus_max() + 1;
+ }
debug("%s: bus = %d/%s\n", __func__, sub_bus, bus->name);
dm_pciauto_prescan_setup_bridge(bus, sub_bus);
@@ -633,12 +643,15 @@ int dm_pci_hose_probe_bus(struct udevice *bus)
ret);
return ret;
}
- if (sub_bus != bus->seq) {
- printf("%s: Internal error, bus '%s' got seq %d, expected %d\n",
- __func__, bus->name, bus->seq, sub_bus);
- return -EPIPE;
+
+ if (!ea_pos) {
+ if (sub_bus != bus->seq) {
+ debug("%s: Internal error, bus '%s' got seq %d, expected %d\n",
+ __func__, bus->name, bus->seq, sub_bus);
+ return -EPIPE;
+ }
+ sub_bus = pci_get_bus_max();
}
- sub_bus = pci_get_bus_max();
dm_pciauto_postscan_setup_bridge(bus, sub_bus);
return sub_bus;
@@ -696,7 +709,8 @@ static int pci_find_and_bind_driver(struct udevice *parent,
find_id->vendor, find_id->device);
/* Determine optional OF node */
- pci_dev_find_ofnode(parent, bdf, &node);
+ if (ofnode_valid(dev_ofnode(parent)))
+ pci_dev_find_ofnode(parent, bdf, &node);
if (ofnode_valid(node) && !ofnode_is_available(node)) {
debug("%s: Ignoring disabled device\n", __func__);
@@ -785,6 +799,7 @@ int pci_bind_bus_devices(struct udevice *bus)
ulong header_type;
pci_dev_t bdf, end;
bool found_multi;
+ int ari_off;
int ret;
found_multi = false;
@@ -858,6 +873,31 @@ int pci_bind_bus_devices(struct udevice *bus)
pplat->vendor = vendor;
pplat->device = device;
pplat->class = class;
+
+ if (IS_ENABLED(CONFIG_PCI_ARID)) {
+ ari_off = dm_pci_find_ext_capability(dev,
+ PCI_EXT_CAP_ID_ARI);
+ if (ari_off) {
+ u16 ari_cap;
+
+ /*
+ * Read Next Function number in ARI Cap
+ * Register
+ */
+ dm_pci_read_config16(dev, ari_off + 4,
+ &ari_cap);
+ /*
+ * Update next scan on this function number,
+ * subtract 1 in BDF to satisfy loop increment.
+ */
+ if (ari_cap & 0xff00) {
+ bdf = PCI_BDF(PCI_BUS(bdf),
+ PCI_DEV(ari_cap),
+ PCI_FUNC(ari_cap));
+ bdf = bdf - 0x100;
+ }
+ }
+ }
}
return 0;
@@ -871,8 +911,10 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
ofnode node)
{
int pci_addr_cells, addr_cells, size_cells;
+ struct bd_info *bd = gd->bd;
int cells_per_record;
const u32 *prop;
+ int max_regions;
int len;
int i;
@@ -892,7 +934,13 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
hose->region_count = 0;
debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
cells_per_record);
- for (i = 0; i < MAX_PCI_REGIONS; i++, len -= cells_per_record) {
+
+ /* Dynamically allocate the regions array */
+ max_regions = len / cells_per_record + CONFIG_NR_DRAM_BANKS;
+ hose->regions = (struct pci_region *)
+ calloc(1, max_regions * sizeof(struct pci_region));
+
+ for (i = 0; i < max_regions; i++, len -= cells_per_record) {
u64 pci_addr, addr, size;
int space_code;
u32 flags;
@@ -927,10 +975,13 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
}
pos = -1;
- for (i = 0; i < hose->region_count; i++) {
- if (hose->regions[i].flags == type)
- pos = i;
+ if (!IS_ENABLED(CONFIG_PCI_REGION_MULTI_ENTRY)) {
+ for (i = 0; i < hose->region_count; i++) {
+ if (hose->regions[i].flags == type)
+ pos = i;
+ }
}
+
if (pos == -1)
pos = hose->region_count++;
debug(" - type=%d, pos=%d\n", type, pos);
@@ -938,18 +989,10 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
}
/* Add a region for our local memory */
-#ifdef CONFIG_NR_DRAM_BANKS
- struct bd_info *bd = gd->bd;
-
if (!bd)
return;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) {
- if (hose->region_count == MAX_PCI_REGIONS) {
- pr_err("maximum number of regions parsed, aborting\n");
- break;
- }
-
if (bd->bi_dram[i].size) {
pci_set_region(hose->regions + hose->region_count++,
bd->bi_dram[i].start,
@@ -958,19 +1001,6 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
}
}
-#else
- phys_addr_t base = 0, size;
-
- size = gd->ram_size;
-#ifdef CONFIG_SYS_SDRAM_BASE
- base = CONFIG_SYS_SDRAM_BASE;
-#endif
- if (gd->pci_ram_top && gd->pci_ram_top < base + size)
- size = gd->pci_ram_top - base;
- if (size)
- pci_set_region(hose->regions + hose->region_count++, base,
- base, size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
-#endif
return;
}
@@ -996,8 +1026,11 @@ static int pci_uclass_pre_probe(struct udevice *bus)
hose->bus = bus;
hose->first_busno = bus->seq;
hose->last_busno = bus->seq;
- hose->skip_auto_config_until_reloc =
- dev_read_bool(bus, "u-boot,skip-auto-config-until-reloc");
+ if (dev_of_valid(bus)) {
+ hose->skip_auto_config_until_reloc =
+ dev_read_bool(bus,
+ "u-boot,skip-auto-config-until-reloc");
+ }
return 0;
}
@@ -1406,14 +1439,55 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
return bus_addr;
}
+static phys_addr_t dm_pci_map_ea_virt(struct udevice *dev, int ea_off,
+ struct pci_child_platdata *pdata)
+{
+ phys_addr_t addr = 0;
+
+ /*
+ * In the case of a Virtual Function device using BAR
+ * base and size, add offset for VFn BAR(1, 2, 3...n)
+ */
+ if (pdata->is_virtfn) {
+ size_t sz;
+ u32 ea_entry;
+
+ /* MaxOffset, 1st DW */
+ dm_pci_read_config32(dev, ea_off + 8, &ea_entry);
+ sz = ea_entry & PCI_EA_FIELD_MASK;
+ /* Fill up lower 2 bits */
+ sz |= (~PCI_EA_FIELD_MASK);
+
+ if (ea_entry & PCI_EA_IS_64) {
+ /* MaxOffset 2nd DW */
+ dm_pci_read_config32(dev, ea_off + 16, &ea_entry);
+ sz |= ((u64)ea_entry) << 32;
+ }
+
+ addr = (pdata->virtid - 1) * (sz + 1);
+ }
+
+ return addr;
+}
+
static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
- int ea_off)
+ int ea_off, struct pci_child_platdata *pdata)
{
int ea_cnt, i, entry_size;
int bar_id = (bar - PCI_BASE_ADDRESS_0) >> 2;
u32 ea_entry;
phys_addr_t addr;
+ if (IS_ENABLED(CONFIG_PCI_SRIOV)) {
+ /*
+ * In the case of a Virtual Function device, device is
+ * Physical function, so pdata will point to required VF
+ * specific data.
+ */
+ if (pdata->is_virtfn)
+ bar_id += PCI_EA_BEI_VF_BAR0;
+ }
+
/* EA capability structure header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
ea_cnt = (ea_entry >> 16) & PCI_EA_NUM_ENT_MASK;
@@ -1436,8 +1510,11 @@ static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
addr |= ((u64)ea_entry) << 32;
}
+ if (IS_ENABLED(CONFIG_PCI_SRIOV))
+ addr += dm_pci_map_ea_virt(dev, ea_off, pdata);
+
/* size ignored for now */
- return map_physmem(addr, flags, 0);
+ return map_physmem(addr, 0, flags);
}
return 0;
@@ -1445,29 +1522,42 @@ static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
{
+ struct pci_child_platdata *pdata = dev_get_parent_platdata(dev);
+ struct udevice *udev = dev;
pci_addr_t pci_bus_addr;
u32 bar_response;
int ea_off;
+ if (IS_ENABLED(CONFIG_PCI_SRIOV)) {
+ /*
+ * In case of Virtual Function devices, use PF udevice
+ * as EA capability is defined in Physical Function
+ */
+ if (pdata->is_virtfn)
+ udev = pdata->pfdev;
+ }
+
/*
* if the function supports Enhanced Allocation use that instead of
* BARs
+ * Incase of virtual functions, pdata will help read VF BEI
+ * and EA entry size.
*/
- ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA);
+ ea_off = dm_pci_find_capability(udev, PCI_CAP_ID_EA);
if (ea_off)
- return dm_pci_map_ea_bar(dev, bar, flags, ea_off);
+ return dm_pci_map_ea_bar(udev, bar, flags, ea_off, pdata);
/* read BAR address */
- dm_pci_read_config32(dev, bar, &bar_response);
+ dm_pci_read_config32(udev, bar, &bar_response);
pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
/*
* Pass "0" as the length argument to pci_bus_to_virt. The arg
- * isn't actualy used on any platform because u-boot assumes a static
+ * isn't actually used on any platform because U-Boot assumes a static
* linear mapping. In the future, this could read the BAR size
* and pass that as the size if needed.
*/
- return dm_pci_bus_to_virt(dev, pci_bus_addr, flags, 0, MAP_NOCACHE);
+ return dm_pci_bus_to_virt(udev, pci_bus_addr, flags, 0, MAP_NOCACHE);
}
static int _dm_pci_find_next_capability(struct udevice *dev, u8 pos, int cap)
@@ -1583,6 +1673,120 @@ int dm_pci_flr(struct udevice *dev)
return 0;
}
+#if defined(CONFIG_PCI_SRIOV)
+int pci_sriov_init(struct udevice *pdev, int vf_en)
+{
+ u16 vendor, device;
+ struct udevice *bus;
+ struct udevice *dev;
+ pci_dev_t bdf;
+ u16 ctrl;
+ u16 num_vfs;
+ u16 total_vf;
+ u16 vf_offset;
+ u16 vf_stride;
+ int vf, ret;
+ int pos;
+
+ pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos) {
+ debug("Error: SRIOV capability not found\n");
+ return -ENOENT;
+ }
+
+ dm_pci_read_config16(pdev, pos + PCI_SRIOV_CTRL, &ctrl);
+
+ dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf);
+ if (vf_en > total_vf)
+ vf_en = total_vf;
+ dm_pci_write_config16(pdev, pos + PCI_SRIOV_NUM_VF, vf_en);
+
+ ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
+ dm_pci_write_config16(pdev, pos + PCI_SRIOV_CTRL, ctrl);
+
+ dm_pci_read_config16(pdev, pos + PCI_SRIOV_NUM_VF, &num_vfs);
+ if (num_vfs > vf_en)
+ num_vfs = vf_en;
+
+ dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_OFFSET, &vf_offset);
+ dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_STRIDE, &vf_stride);
+
+ dm_pci_read_config16(pdev, PCI_VENDOR_ID, &vendor);
+ dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_DID, &device);
+
+ bdf = dm_pci_get_bdf(pdev);
+
+ pci_get_bus(PCI_BUS(bdf), &bus);
+
+ if (!bus)
+ return -ENODEV;
+
+ bdf += PCI_BDF(0, 0, vf_offset);
+
+ for (vf = 0; vf < num_vfs; vf++) {
+ struct pci_child_platdata *pplat;
+ ulong class;
+
+ pci_bus_read_config(bus, bdf, PCI_CLASS_DEVICE,
+ &class, PCI_SIZE_16);
+
+ debug("%s: bus %d/%s: found VF %x:%x\n", __func__,
+ bus->seq, bus->name, PCI_DEV(bdf), PCI_FUNC(bdf));
+
+ /* Find this device in the device tree */
+ ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), &dev);
+
+ if (ret == -ENODEV) {
+ struct pci_device_id find_id;
+
+ memset(&find_id, '\0', sizeof(find_id));
+ find_id.vendor = vendor;
+ find_id.device = device;
+ find_id.class = class;
+
+ ret = pci_find_and_bind_driver(bus, &find_id,
+ bdf, &dev);
+
+ if (ret)
+ return ret;
+ }
+
+ /* Update the platform data */
+ pplat = dev_get_parent_platdata(dev);
+ pplat->devfn = PCI_MASK_BUS(bdf);
+ pplat->vendor = vendor;
+ pplat->device = device;
+ pplat->class = class;
+ pplat->is_virtfn = true;
+ pplat->pfdev = pdev;
+ pplat->virtid = vf * vf_stride + vf_offset;
+
+ debug("%s: bus %d/%s: found VF %x:%x %x:%x class %lx id %x\n",
+ __func__, dev->seq, dev->name, PCI_DEV(bdf),
+ PCI_FUNC(bdf), vendor, device, class, pplat->virtid);
+ bdf += PCI_BDF(0, 0, vf_stride);
+ }
+
+ return 0;
+}
+
+int pci_sriov_get_totalvfs(struct udevice *pdev)
+{
+ u16 total_vf;
+ int pos;
+
+ pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos) {
+ debug("Error: SRIOV capability not found\n");
+ return -ENOENT;
+ }
+
+ dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf);
+
+ return total_vf;
+}
+#endif /* SRIOV */
+
UCLASS_DRIVER(pci) = {
.id = UCLASS_PCI,
.name = "pci",
diff --git a/drivers/pci/pci_octeontx.c b/drivers/pci/pci_octeontx.c
new file mode 100644
index 0000000000..30537543a0
--- /dev/null
+++ b/drivers/pci/pci_octeontx.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * https://spdx.org/licenses
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <pci.h>
+
+#include <asm/io.h>
+
+#include <linux/ioport.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * This driver supports multiple types of operations / host bridges / busses:
+ *
+ * OTX_ECAM: Octeon TX & TX2 ECAM (Enhanced Configuration Access Mechanism)
+ * Used to access the internal on-chip devices which are connected
+ * to internal buses
+ * OTX_PEM: Octeon TX PEM (PCI Express MAC)
+ * Used to access the external (off-chip) PCI devices
+ * OTX2_PEM: Octeon TX2 PEM (PCI Express MAC)
+ * Used to access the external (off-chip) PCI devices
+ */
+enum {
+ OTX_ECAM,
+ OTX_PEM,
+ OTX2_PEM,
+};
+
+/**
+ * struct octeontx_pci - Driver private data
+ * @type: Device type matched via compatible (e.g. OTX_ECAM etc)
+ * @cfg: Config resource
+ * @bus: Bus resource
+ */
+struct octeontx_pci {
+ unsigned int type;
+
+ struct resource cfg;
+ struct resource bus;
+};
+
+static uintptr_t octeontx_cfg_addr(struct octeontx_pci *pcie,
+ int bus_offs, int shift_offs,
+ pci_dev_t bdf, uint offset)
+{
+ u32 bus, dev, func;
+ uintptr_t address;
+
+ bus = PCI_BUS(bdf) + bus_offs;
+ dev = PCI_DEV(bdf);
+ func = PCI_FUNC(bdf);
+
+ address = (bus << (20 + shift_offs)) |
+ (dev << (15 + shift_offs)) |
+ (func << (12 + shift_offs)) | offset;
+ address += pcie->cfg.start;
+
+ return address;
+}
+
+static ulong readl_size(uintptr_t addr, enum pci_size_t size)
+{
+ ulong val;
+
+ switch (size) {
+ case PCI_SIZE_8:
+ val = readb(addr);
+ break;
+ case PCI_SIZE_16:
+ val = readw(addr);
+ break;
+ case PCI_SIZE_32:
+ val = readl(addr);
+ break;
+ default:
+ printf("Invalid size\n");
+ return -EINVAL;
+ };
+
+ return val;
+}
+
+static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep)
+{
+ switch (size) {
+ case PCI_SIZE_8:
+ writeb(valuep, addr);
+ break;
+ case PCI_SIZE_16:
+ writew(valuep, addr);
+ break;
+ case PCI_SIZE_32:
+ writel(valuep, addr);
+ break;
+ default:
+ printf("Invalid size\n");
+ };
+}
+
+static bool octeontx_bdf_invalid(pci_dev_t bdf)
+{
+ if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0)
+ return true;
+
+ return false;
+}
+
+static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ uintptr_t address;
+
+ address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
+ 0, bdf, offset);
+ *valuep = readl_size(address, size);
+
+ debug("%02x.%02x.%02x: u%d %x -> %lx\n",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
+
+ return 0;
+}
+
+static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ uintptr_t address;
+
+ address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
+ 0, bdf, offset);
+ writel_size(address, size, value);
+
+ debug("%02x.%02x.%02x: u%d %x <- %lx\n",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value);
+
+ return 0;
+}
+
+static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ uintptr_t address;
+ u8 hdrtype;
+ u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
+ u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
+
+ address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4,
+ bdf, 0);
+
+ *valuep = pci_conv_32_to_size(~0UL, offset, size);
+
+ if (octeontx_bdf_invalid(bdf))
+ return -EPERM;
+
+ *valuep = readl_size(address + offset, size);
+
+ hdrtype = readb(address + PCI_HEADER_TYPE);
+ if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
+ offset >= PCI_PRIMARY_BUS &&
+ offset <= PCI_SUBORDINATE_BUS &&
+ *valuep != pci_conv_32_to_size(~0UL, offset, size))
+ *valuep -= pci_conv_32_to_size(bus_offs, offset, size);
+
+ return 0;
+}
+
+static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ uintptr_t address;
+ u8 hdrtype;
+ u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
+ u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
+
+ address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4, bdf, 0);
+
+ hdrtype = readb(address + PCI_HEADER_TYPE);
+ if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
+ offset >= PCI_PRIMARY_BUS &&
+ offset <= PCI_SUBORDINATE_BUS &&
+ value != pci_conv_32_to_size(~0UL, offset, size))
+ value += pci_conv_32_to_size(bus_offs, offset, size);
+
+ if (octeontx_bdf_invalid(bdf))
+ return -EPERM;
+
+ writel_size(address + offset, size, value);
+
+ debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
+ address, value);
+
+ return 0;
+}
+
+static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ uintptr_t address;
+
+ address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
+ bdf, 0);
+
+ *valuep = pci_conv_32_to_size(~0UL, offset, size);
+
+ if (octeontx_bdf_invalid(bdf))
+ return -EPERM;
+
+ *valuep = readl_size(address + offset, size);
+
+ debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
+ address, *valuep);
+
+ return 0;
+}
+
+static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ uintptr_t address;
+
+ address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
+ bdf, 0);
+
+ if (octeontx_bdf_invalid(bdf))
+ return -EPERM;
+
+ writel_size(address + offset, size, value);
+
+ debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
+ address, value);
+
+ return 0;
+}
+
+int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ int ret = -EIO;
+
+ switch (pcie->type) {
+ case OTX_ECAM:
+ ret = octeontx_ecam_read_config(bus, bdf, offset, valuep,
+ size);
+ break;
+ case OTX_PEM:
+ ret = octeontx_pem_read_config(bus, bdf, offset, valuep,
+ size);
+ break;
+ case OTX2_PEM:
+ ret = octeontx2_pem_read_config(bus, bdf, offset, valuep,
+ size);
+ break;
+ }
+
+ return ret;
+}
+
+int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+ int ret = -EIO;
+
+ switch (pcie->type) {
+ case OTX_ECAM:
+ ret = octeontx_ecam_write_config(bus, bdf, offset, value,
+ size);
+ break;
+ case OTX_PEM:
+ ret = octeontx_pem_write_config(bus, bdf, offset, value,
+ size);
+ break;
+ case OTX2_PEM:
+ ret = octeontx2_pem_write_config(bus, bdf, offset, value,
+ size);
+ break;
+ }
+
+ return ret;
+}
+
+static int pci_octeontx_ofdata_to_platdata(struct udevice *dev)
+{
+ return 0;
+}
+
+static int pci_octeontx_probe(struct udevice *dev)
+{
+ struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev);
+ int err;
+
+ pcie->type = dev_get_driver_data(dev);
+
+ err = dev_read_resource(dev, 0, &pcie->cfg);
+ if (err) {
+ debug("Error reading resource: %s\n", fdt_strerror(err));
+ return err;
+ }
+
+ err = dev_read_pci_bus_range(dev, &pcie->bus);
+ if (err) {
+ debug("Error reading resource: %s\n", fdt_strerror(err));
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct dm_pci_ops pci_octeontx_ops = {
+ .read_config = pci_octeontx_read_config,
+ .write_config = pci_octeontx_write_config,
+};
+
+static const struct udevice_id pci_octeontx_ids[] = {
+ { .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM },
+ { .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM },
+ { .compatible = "pci-host-ecam-generic", .data = OTX_ECAM },
+ { .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM },
+ { .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM },
+ { }
+};
+
+U_BOOT_DRIVER(pci_octeontx) = {
+ .name = "pci_octeontx",
+ .id = UCLASS_PCI,
+ .of_match = pci_octeontx_ids,
+ .ops = &pci_octeontx_ops,
+ .ofdata_to_platdata = pci_octeontx_ofdata_to_platdata,
+ .probe = pci_octeontx_probe,
+ .priv_auto_alloc_size = sizeof(struct octeontx_pci),
+ .flags = DM_FLAG_PRE_RELOC,
+};