diff --git a/doc/driver-model/pci-info.txt b/doc/driver-model/pci-info.txt index 52b4389d2c..e1701d1fbc 100644 --- a/doc/driver-model/pci-info.txt +++ b/doc/driver-model/pci-info.txt @@ -133,3 +133,31 @@ When this bus is scanned we will end up with something like this: When accesses go to the pci@1f,0 device they are forwarded to its child, the emulator. + +The sandbox PCI drivers also support dynamic driver binding, allowing device +driver to declare the driver binding information via U_BOOT_PCI_DEVICE(), +eliminating the need to provide any device tree node under the host controller +node. It is required a "sandbox,dev-info" property must be provided in the +host controller node for this functionality to work. + + pci1: pci-controller1 { + compatible = "sandbox,pci"; + ... + sandbox,dev-info = <0x08 0x00 0x1234 0x5678 + 0x0c 0x00 0x1234 0x5678>; + }; + +The "sandbox,dev-info" property specifies all dynamic PCI devices on this bus. +Each dynamic PCI device is encoded as 4 cells a group. The first and second +cells are PCI device number and function number respectively. The third and +fourth cells are PCI vendor ID and device ID respectively. + +When this bus is scanned we will end up with something like this: + + pci [ + ] pci_sandbo |-- pci-controller1 + pci_emul [ ] sandbox_sw | |-- sandbox_swap_case_emul + pci_emul [ ] sandbox_sw | `-- sandbox_swap_case_emul + +Note the difference from the statically declared device nodes is that the +device is directly attached to the host controller, instead of via a container +device like pci@1f,0. diff --git a/drivers/pci/pci-emul-uclass.c b/drivers/pci/pci-emul-uclass.c index 8570a5da20..e9d2f49793 100644 --- a/drivers/pci/pci-emul-uclass.c +++ b/drivers/pci/pci-emul-uclass.c @@ -16,21 +16,27 @@ struct sandbox_pci_priv { }; int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn, - struct udevice **emulp) + struct udevice **containerp, struct udevice **emulp) { struct udevice *dev; int ret; + *containerp = NULL; ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(find_devfn), &dev); if (ret) { debug("%s: Could not find emulator for dev %x\n", __func__, find_devfn); return ret; } + *containerp = dev; - ret = device_find_first_child(dev, emulp); - if (ret) - return ret; + if (device_get_uclass_id(dev) == UCLASS_PCI_GENERIC) { + ret = device_find_first_child(dev, emulp); + if (ret) + return ret; + } else { + *emulp = dev; + } return *emulp ? 0 : -ENODEV; } diff --git a/drivers/pci/pci_sandbox.c b/drivers/pci/pci_sandbox.c index 67cd733e61..119a98d061 100644 --- a/drivers/pci/pci_sandbox.c +++ b/drivers/pci/pci_sandbox.c @@ -10,15 +10,27 @@ #include #include +#define FDT_DEV_INFO_CELLS 4 +#define FDT_DEV_INFO_SIZE (FDT_DEV_INFO_CELLS * sizeof(u32)) + +#define SANDBOX_PCI_DEVFN(d, f) ((d << 3) | f) + +struct sandbox_pci_priv { + struct { + u16 vendor; + u16 device; + } vendev[256]; +}; + static int sandbox_pci_write_config(struct udevice *bus, pci_dev_t devfn, uint offset, ulong value, enum pci_size_t size) { struct dm_pci_emul_ops *ops; - struct udevice *emul; + struct udevice *container, *emul; int ret; - ret = sandbox_pci_get_emul(bus, devfn, &emul); + ret = sandbox_pci_get_emul(bus, devfn, &container, &emul); if (ret) return ret == -ENODEV ? 0 : ret; ops = pci_get_emul_ops(emul); @@ -33,14 +45,31 @@ static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn, enum pci_size_t size) { struct dm_pci_emul_ops *ops; - struct udevice *emul; + struct udevice *container, *emul; + struct sandbox_pci_priv *priv = dev_get_priv(bus); int ret; /* Prepare the default response */ *valuep = pci_get_ff(size); - ret = sandbox_pci_get_emul(bus, devfn, &emul); - if (ret) - return ret == -ENODEV ? 0 : ret; + ret = sandbox_pci_get_emul(bus, devfn, &container, &emul); + if (ret) { + if (!container) { + u16 vendor, device; + + devfn = SANDBOX_PCI_DEVFN(PCI_DEV(devfn), + PCI_FUNC(devfn)); + vendor = priv->vendev[devfn].vendor; + device = priv->vendev[devfn].device; + if (offset == PCI_VENDOR_ID && vendor) + *valuep = vendor; + else if (offset == PCI_DEVICE_ID && device) + *valuep = device; + + return 0; + } else { + return ret == -ENODEV ? 0 : ret; + } + } ops = pci_get_emul_ops(emul); if (!ops || !ops->read_config) return -ENOSYS; @@ -48,6 +77,41 @@ static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn, return ops->read_config(emul, offset, valuep, size); } +static int sandbox_pci_probe(struct udevice *dev) +{ + struct sandbox_pci_priv *priv = dev_get_priv(dev); + const fdt32_t *cell; + u8 pdev, pfn, devfn; + int len; + + cell = ofnode_get_property(dev_ofnode(dev), "sandbox,dev-info", &len); + if (!cell) + return 0; + + if ((len % FDT_DEV_INFO_SIZE) == 0) { + int num = len / FDT_DEV_INFO_SIZE; + int i; + + for (i = 0; i < num; i++) { + debug("dev info #%d: %02x %02x %04x %04x\n", i, + fdt32_to_cpu(cell[0]), fdt32_to_cpu(cell[1]), + fdt32_to_cpu(cell[2]), fdt32_to_cpu(cell[3])); + + pdev = fdt32_to_cpu(cell[0]); + pfn = fdt32_to_cpu(cell[1]); + if (pdev > 31 || pfn > 7) + continue; + devfn = SANDBOX_PCI_DEVFN(pdev, pfn); + priv->vendev[devfn].vendor = fdt32_to_cpu(cell[2]); + priv->vendev[devfn].device = fdt32_to_cpu(cell[3]); + + cell += FDT_DEV_INFO_CELLS; + } + } + + return 0; +} + static const struct dm_pci_ops sandbox_pci_ops = { .read_config = sandbox_pci_read_config, .write_config = sandbox_pci_write_config, @@ -63,6 +127,8 @@ U_BOOT_DRIVER(pci_sandbox) = { .id = UCLASS_PCI, .of_match = sandbox_pci_ids, .ops = &sandbox_pci_ops, + .probe = sandbox_pci_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_pci_priv), /* Attach an emulator if we can */ .child_post_bind = dm_scan_fdt_dev, diff --git a/include/pci.h b/include/pci.h index 427094cd70..c0ae5d1673 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1441,11 +1441,12 @@ struct dm_pci_emul_ops { * * @bus: PCI bus to search * @find_devfn: PCI device and function address (PCI_DEVFN()) + * @containerp: Returns container device if found * @emulp: Returns emulated device if found * @return 0 if found, -ENODEV if not found */ int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn, - struct udevice **emulp); + struct udevice **containerp, struct udevice **emulp); #endif /* CONFIG_DM_PCI */