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.
};
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;
}
#include <inttypes.h>
#include <pci.h>
+#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);
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;
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,
.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,