Add a PCI bridge emulation device. Although real bridges need to be
configured in order to forward requests, this happens automatically for
now (see arch/sandbox/lib/pci_io.c). So just record the window registers
and check them in a test to make sure pci_auto is sane.

As each bridge consume at least 4k I/O registers, increase the root
port's I/O space so we have some left for other devices.

Signed-off-by: Sean Anderson <[email protected]>
---

Changes in v3:
- New

 arch/sandbox/dts/test.dts        |  35 +++++-
 arch/sandbox/include/asm/test.h  |   1 +
 drivers/pci/Makefile             |   2 +-
 drivers/pci/pci_sandbox_bridge.c | 192 +++++++++++++++++++++++++++++++
 test/dm/pci.c                    | 142 +++++++++++++++++++++++
 5 files changed, 370 insertions(+), 2 deletions(-)
 create mode 100644 drivers/pci/pci_sandbox_bridge.c

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index e945a47fb01..962d364f9b2 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1279,7 +1279,7 @@
                #address-cells = <3>;
                #size-cells = <2>;
                ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000000
-                               0x01000000 0 0x20000000 0x20000000 0 0x2000>;
+                         0x01000000 0 0x20000000 0x20000000 0 0x20000>;
                iommu-map = <0x0010 &iommu 0 1>;
                iommu-map-mask = <0xfffffff8>;
                sandbox,emul = <&pci_emul0>;
@@ -1301,6 +1301,15 @@
                        };
                };
 
+               pci@4,0 {
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       device_type = "pci";
+                       compatible = "pciclass,0604";
+                       reg = <0x00002000 0 0 0 0>;
+                       ranges;
+               };
+
                pci@1e,0 {
                        compatible = "sandbox,pmc";
                        reg = <0xf000 0 0 0 0>;
@@ -1340,6 +1349,30 @@
                        reg = <0x10>;
                };
 
+               emul@3,0 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "sandbox,pci-bridge";
+                       reg = <0x18>;
+
+                       emul@0,0 {
+                               compatible = "sandbox,swap-case";
+                               reg = <0x00>;
+                       };
+               };
+
+               emul@4,0 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "sandbox,pci-bridge";
+                       reg = <0x20>;
+
+                       emul@0,0 {
+                               compatible = "sandbox,swap-case";
+                               reg = <0x00>;
+                       };
+               };
+
                emul@1e,0 {
                        compatible = "sandbox,pmc-emul";
                        reg = <0xf0>;
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h
index 0e8d19ce232..86c7c833b6e 100644
--- a/arch/sandbox/include/asm/test.h
+++ b/arch/sandbox/include/asm/test.h
@@ -19,6 +19,7 @@ struct unit_test_state;
 #define SANDBOX_PCI_SWAP_CASE_EMUL_ID  0x5678
 #define SANDBOX_PCI_PMC_EMUL_ID                0x5677
 #define SANDBOX_PCI_P2SB_EMUL_ID       0x5676
+#define SANDBOX_PCI_BRIDGE_EMUL_ID     0x5662
 #define SANDBOX_PCI_CLASS_CODE         (PCI_CLASS_COMMUNICATION_SERIAL >> 8)
 #define SANDBOX_PCI_CLASS_SUB_CODE     (PCI_CLASS_COMMUNICATION_SERIAL & 0xff)
 
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 98f3c226f63..70a0dd3c1e3 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_VIDEO) += pci_rom.o
 obj-$(CONFIG_PCI) += pci-uclass.o pci_auto.o
 obj-$(CONFIG_DM_PCI_COMPAT) += pci_compat.o
 obj-$(CONFIG_PCI_SANDBOX) += pci_sandbox.o
-obj-$(CONFIG_SANDBOX) += pci-emul-uclass.o
+obj-$(CONFIG_SANDBOX) += pci-emul-uclass.o pci_sandbox_bridge.o
 obj-$(CONFIG_X86) += pci_x86.o pci_rom.o
 obj-$(CONFIG_PCI) += pci_auto_common.o pci_common.o
 
diff --git a/drivers/pci/pci_sandbox_bridge.c b/drivers/pci/pci_sandbox_bridge.c
new file mode 100644
index 00000000000..81a4e993b47
--- /dev/null
+++ b/drivers/pci/pci_sandbox_bridge.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PCI emulation device which swaps the case of text
+ *
+ * Copyright (c) 2014 Google, Inc
+ * Written by Simon Glass <[email protected]>
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <pci.h>
+#include <asm/test.h>
+#include <linux/ctype.h>
+
+/**
+ * struct bridge_priv - Sandbox PCI bridge emulation private data
+ */
+struct bridge_priv {
+       u64 pref_base, pref_limit;
+       u32 io_base, io_limit;
+       u16 command, control;
+       u16 mem_base, mem_limit;
+       u8 primary, secondary, sub;
+};
+
+#define mask(s) GENMASK_ULL((1 << ((s) + 3)) - 1, 0)
+#define extract(v, o, s) (((v) >> ((o) * 8)) & mask(s))
+#define deposit(v, o, s, f) ({ \
+       uint _o = (o); \
+       typeof(v) _v = v & ~((typeof(v))mask(s) << (_o * 8)); \
+       _v | ((typeof(v))(f) << (_o * 8)); \
+})
+
+static ulong _sandbox_bridge_read_config(const struct udevice *dev, uint off,
+                                        enum pci_size_t size)
+{
+       struct bridge_priv *priv = dev_get_priv(dev);
+
+       switch (off) {
+       case PCI_VENDOR_ID ... PCI_VENDOR_ID + 1:
+               return extract(SANDBOX_PCI_VENDOR_ID, off & 1, size);
+       case PCI_DEVICE_ID ... PCI_DEVICE_ID + 1:
+               return extract(SANDBOX_PCI_BRIDGE_EMUL_ID, off & 1, size);
+       case PCI_COMMAND ... PCI_COMMAND + 1:
+               return extract(priv->command, off & 1, size);
+       case PCI_REVISION_ID ... PCI_CLASS_CODE:
+               return extract(PCI_CLASS_BRIDGE_PCI_NORMAL << 8, off & 3, size);
+       case PCI_HEADER_TYPE:
+               return PCI_HEADER_TYPE_BRIDGE;
+       case PCI_PRIMARY_BUS:
+               return priv->primary;
+       case PCI_SECONDARY_BUS:
+               return priv->secondary;
+       case PCI_SUBORDINATE_BUS:
+               return priv->sub;
+       case PCI_IO_BASE:
+               return priv->io_base;
+       case PCI_IO_LIMIT:
+               return priv->io_limit;
+       case PCI_MEMORY_BASE ... PCI_MEMORY_BASE + 1:
+               return extract(priv->mem_base, off & 1, size);
+       case PCI_MEMORY_LIMIT ... PCI_MEMORY_LIMIT + 1:
+               return extract(priv->mem_limit, off & 1, size);
+       case PCI_PREF_MEMORY_BASE ... PCI_PREF_MEMORY_BASE + 1:
+               return extract(priv->pref_base, off & 1, size);
+       case PCI_PREF_MEMORY_LIMIT ... PCI_PREF_MEMORY_LIMIT + 1:
+               return extract(priv->pref_limit, off & 1, size);
+       case PCI_PREF_BASE_UPPER32 ... PCI_PREF_BASE_UPPER32 + 3:
+               return extract(priv->pref_base >> 16, off & 3, size);
+       case PCI_PREF_LIMIT_UPPER32 ... PCI_PREF_LIMIT_UPPER32 + 3:
+               return extract(priv->pref_limit >> 16, off & 3, size);
+       case PCI_IO_BASE_UPPER16 ... PCI_IO_BASE_UPPER16 + 1:
+               return extract(priv->io_base >> 8, off & 1, size);
+       case PCI_IO_LIMIT_UPPER16 ... PCI_IO_LIMIT_UPPER16 + 1:
+               return extract(priv->io_limit >> 8, off & 1, size);
+       case PCI_BRIDGE_CONTROL ... PCI_BRIDGE_CONTROL + 1:
+               return extract(priv->control, off & 1, size);
+       }
+
+       return 0;
+}
+
+static int sandbox_bridge_read_config(const struct udevice *dev, uint off,
+                                     ulong *valp, enum pci_size_t size)
+{
+       *valp = _sandbox_bridge_read_config(dev, off, size);
+       return 0;
+}
+
+static void sandbox_bridge_fix_regs(struct bridge_priv *priv)
+{
+       priv->io_base &= PCI_IO_RANGE_MASK;
+       priv->io_base |= PCI_IO_RANGE_TYPE_32;
+       priv->io_limit &= PCI_IO_RANGE_MASK;
+       priv->io_limit |= PCI_IO_RANGE_TYPE_32;
+       priv->mem_base &= PCI_MEMORY_RANGE_MASK;
+       priv->mem_limit &= PCI_MEMORY_RANGE_MASK;
+       priv->pref_base &= PCI_PREF_RANGE_MASK;
+       priv->pref_base |= PCI_PREF_RANGE_TYPE_64;
+       priv->pref_limit &= PCI_PREF_RANGE_MASK;
+       priv->pref_limit |= PCI_PREF_RANGE_TYPE_64;
+}
+
+static int sandbox_bridge_write_config(struct udevice *dev, uint off, ulong 
val,
+                                      enum pci_size_t size)
+{
+       struct bridge_priv *priv = dev_get_priv(dev);
+
+       switch (off) {
+       case PCI_COMMAND ... PCI_COMMAND + 1:
+               priv->command = deposit(priv->command, off & 1, size, val);
+               break;
+       case PCI_PRIMARY_BUS:
+               priv->primary = val;
+               break;
+       case PCI_SECONDARY_BUS:
+               priv->secondary = val;
+               break;
+       case PCI_SUBORDINATE_BUS:
+               priv->sub = val;
+               break;
+       case PCI_IO_BASE:
+               priv->io_base = deposit(priv->io_base, 0, 0, val);
+               break;
+       case PCI_IO_LIMIT:
+               priv->io_limit = deposit(priv->io_limit, 0, 0, val);
+               break;
+       case PCI_MEMORY_BASE ... PCI_MEMORY_BASE + 1:
+               priv->mem_base = deposit(priv->mem_base, off & 1, size, val);
+               break;
+       case PCI_MEMORY_LIMIT ... PCI_MEMORY_LIMIT + 1:
+               priv->mem_limit = deposit(priv->mem_limit, off & 1, size, val);
+               break;
+       case PCI_PREF_MEMORY_BASE ... PCI_PREF_MEMORY_BASE + 1:
+               priv->pref_base = deposit(priv->pref_base, off & 1, size, val);
+               break;
+       case PCI_PREF_MEMORY_LIMIT ... PCI_PREF_MEMORY_LIMIT + 1:
+               priv->pref_limit = deposit(priv->pref_limit, off & 1, size,
+                                          val);
+               break;
+       case PCI_PREF_BASE_UPPER32 ... PCI_PREF_BASE_UPPER32 + 3:
+               priv->pref_base = deposit(priv->pref_base, (off & 3) + 2, size,
+                                         val);
+               break;
+       case PCI_PREF_LIMIT_UPPER32 ... PCI_PREF_LIMIT_UPPER32 + 3:
+               priv->pref_limit = deposit(priv->pref_limit, (off & 3) + 2,
+                                          size, val);
+               break;
+       case PCI_IO_BASE_UPPER16 ... PCI_IO_BASE_UPPER16 + 1:
+               priv->io_base = deposit(priv->io_base, (off & 1) + 1, size,
+                                       val);
+               break;
+       case PCI_IO_LIMIT_UPPER16 ... PCI_IO_LIMIT_UPPER16 + 1:
+               priv->io_limit = deposit(priv->io_limit, (off & 1) + 1, size,
+                                        val);
+               break;
+       case PCI_BRIDGE_CONTROL ... PCI_BRIDGE_CONTROL + 1:
+               priv->control = deposit(priv->control, off & 1, size, val);
+               break;
+       }
+
+       sandbox_bridge_fix_regs(priv);
+       return 0;
+}
+
+static struct dm_pci_emul_ops sandbox_bridge_emul_ops = {
+       .read_config = sandbox_bridge_read_config,
+       .write_config = sandbox_bridge_write_config,
+};
+
+static int sandbox_bridge_probe(struct udevice *dev)
+{
+       struct bridge_priv *priv = dev_get_priv(dev);
+
+       sandbox_bridge_fix_regs(priv);
+       return 0;
+}
+
+static const struct udevice_id sandbox_bridge_ids[] = {
+       { .compatible = "sandbox,pci-bridge" },
+       { }
+};
+
+U_BOOT_DRIVER(sandbox_bridge_emul) = {
+       .name           = "sandbox_bridge_emul",
+       .id             = UCLASS_PCI_EMUL,
+       .of_match       = sandbox_bridge_ids,
+       .probe          = sandbox_bridge_probe,
+       .ops            = &sandbox_bridge_emul_ops,
+       .priv_auto      = sizeof(struct bridge_priv),
+};
diff --git a/test/dm/pci.c b/test/dm/pci.c
index 6eb19f6fea3..b6fee7b3bb3 100644
--- a/test/dm/pci.c
+++ b/test/dm/pci.c
@@ -4,6 +4,7 @@
  */
 
 #include <dm.h>
+#include <dm/device_compat.h>
 #include <asm/io.h>
 #include <asm/test.h>
 #include <dm/test.h>
@@ -486,3 +487,144 @@ static int dm_test_pci_phys_to_bus(struct unit_test_state 
*uts)
        return 0;
 }
 DM_TEST(dm_test_pci_phys_to_bus, UTF_SCAN_PDATA | UTF_SCAN_FDT);
+
+#define PCI_BAR(n) (PCI_BASE_ADDRESS_0 + (n) * 4)
+static int read_bar(struct unit_test_state *uts, struct udevice *dev, int bar,
+                   u32 *base, u32 *mask)
+{
+       int addr = PCI_BASE_ADDRESS_0 + bar * 4;
+
+       ut_assertok(dm_pci_read_config32(dev, addr, base));
+       ut_assertok(dm_pci_write_config32(dev, addr, 0xffffffff));
+       ut_assertok(dm_pci_read_config32(dev, addr, mask));
+       ut_assertok(dm_pci_write_config32(dev, addr, *base));
+       return 0;
+}
+
+/*
+ * Test that all BARs on devices under each bridge lie within the bridge's
+ * forwarding windows.
+ */
+static int _dm_test_pci_bridge_windows(struct unit_test_state *uts,
+                                      struct udevice *bus)
+{
+       struct udevice *dev;
+       u64 pref_lo, pref_hi;
+       u32 val32, io_lo, io_hi, mem_lo, mem_hi;
+       u16 val16;
+       u8 val8;
+
+       ut_assertok(dm_pci_read_config8(bus, PCI_IO_BASE, &val8));
+       ut_assertok(dm_pci_read_config16(bus, PCI_IO_BASE_UPPER16, &val16));
+       io_lo = ((u32)(val8 & PCI_IO_RANGE_MASK) << 8) | ((u32)val16 << 16);
+       ut_assertok(dm_pci_read_config8(bus, PCI_IO_LIMIT, &val8));
+       ut_assertok(dm_pci_read_config16(bus, PCI_IO_LIMIT_UPPER16, &val16));
+       io_hi = ((u32)(val8 & PCI_IO_RANGE_MASK) << 8) | ((u32)val16 << 16) |
+               GENMASK(11, 0);
+
+       ut_assertok(dm_pci_read_config16(bus, PCI_MEMORY_BASE, &val16));
+       mem_lo = (u32)val16 << 16;
+       ut_assertok(dm_pci_read_config16(bus, PCI_MEMORY_LIMIT, &val16));
+       mem_hi = ((u32)val16 << 16) | GENMASK(19, 0);
+
+       ut_assertok(dm_pci_read_config16(bus, PCI_PREF_MEMORY_BASE, &val16));
+       ut_assertok(dm_pci_read_config32(bus, PCI_PREF_BASE_UPPER32, &val32));
+       pref_lo = ((u64)(val16 & PCI_PREF_RANGE_MASK) << 16) |
+                 ((u64)val32 << 32) | GENMASK(19, 0);
+       ut_assertok(dm_pci_read_config16(bus, PCI_PREF_MEMORY_LIMIT, &val16));
+       ut_assertok(dm_pci_read_config32(bus, PCI_PREF_LIMIT_UPPER32, &val32));
+       pref_hi = ((u64)(val16 & PCI_PREF_RANGE_MASK) << 16) |
+                 ((u64)val32 << 32) | GENMASK(19, 0);
+
+       dev_dbg(bus, "io   %08x %08x\n", io_lo, io_hi);
+       dev_dbg(bus, "mem  %08x %08x\n", mem_lo, mem_hi);
+       dev_dbg(bus, "pref %016llx %016llx\n", pref_lo, pref_hi);
+       device_foreach_child(dev, bus) {
+               unsigned int bar, max_bar;
+
+               ut_assertok(dm_pci_read_config8(dev, PCI_HEADER_TYPE, &val8));
+               switch (val8) {
+               case PCI_HEADER_TYPE_NORMAL:
+                       max_bar = 6;
+                       break;
+               case PCI_HEADER_TYPE_BRIDGE:
+                       max_bar = 2;
+                       break;
+               case PCI_HEADER_TYPE_CARDBUS:
+                       max_bar = 0;
+                       break;
+               default:
+                       ut_reportf("Unknown header type %x!\n", val8);
+               }
+
+               for (bar = 0; bar < max_bar; bar++) {
+                       u32 base32, mask32;
+
+                       if (read_bar(uts, dev, bar, &base32, &mask32))
+                               return CMD_RET_FAILURE;
+
+                       if (!mask32)
+                               continue;
+
+                       if (base32 & PCI_BASE_ADDRESS_SPACE_IO) {
+                               mask32 &= PCI_BASE_ADDRESS_IO_MASK;
+                               dev_dbg(dev, "io   %08x %08x\n", base32,
+                                       base32 + ~mask32);
+
+                               ut_assert(base32 >= io_lo);
+                               ut_assert(base32 + ~mask32 <= io_hi);
+                       } else {
+                               u64 base64 = base32 & PCI_BASE_ADDRESS_MEM_MASK;
+                               u64 mask64 = mask32 & PCI_BASE_ADDRESS_MEM_MASK;
+                               u64 mem_type = base32 &
+                                              PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+                               bool pref = base32 &
+                                           PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+                               if (mem_type == PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       ut_assert(++bar < max_bar);
+                                       if (read_bar(uts, dev, bar, &base32,
+                                                    &mask32))
+                                               return CMD_RET_FAILURE;
+
+                                       base64 |= (u64)base32 << 32;
+                                       mask64 |= (u64)mask32 << 32;
+                                       mask64 = ~mask64;
+                               } else {
+                                       
ut_asserteq(PCI_BASE_ADDRESS_MEM_TYPE_32,
+                                                   mem_type);
+                                       mask64 |= GENMASK_ULL(63, 32);
+                               }
+
+                               if (pref) {
+                                       dev_dbg(dev, "pref %016llx %016llx\n",
+                                               base64, base64 + ~mask64);
+
+                                       ut_assert(base64 >= pref_lo);
+                                       ut_assert(base64 + ~mask64 <= pref_hi);
+                               } else {
+                                       dev_dbg(dev, "mem  %08llx %08llx\n",
+                                               base64, base64 + ~mask64);
+
+                                       ut_assert(base64 >= mem_lo);
+                                       ut_assert(base64 + ~mask64 <= mem_hi);
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int dm_test_pci_bridge_windows(struct unit_test_state *uts)
+{
+       struct udevice *bus;
+
+       ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x03, 0), &bus));
+       if (_dm_test_pci_bridge_windows(uts, bus))
+               return CMD_RET_FAILURE;
+
+       ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x04, 0), &bus));
+       return _dm_test_pci_bridge_windows(uts, bus);
+}
+DM_TEST(dm_test_pci_bridge_windows, UTF_SCAN_PDATA | UTF_SCAN_FDT);
-- 
2.35.1.1320.gc452695387.dirty

Reply via email to