// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2014 Google, Inc * Written by Simon Glass */ #include #include #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 *container, *emul; int ret; ret = sandbox_pci_get_emul(bus, devfn, &container, &emul); if (ret) return ret == -ENODEV ? 0 : ret; ops = pci_get_emul_ops(emul); if (!ops || !ops->write_config) return -ENOSYS; return ops->write_config(emul, offset, value, size); } static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn, uint offset, ulong *valuep, enum pci_size_t size) { struct dm_pci_emul_ops *ops; 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, &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, }; static const struct udevice_id sandbox_pci_ids[] = { { .compatible = "sandbox,pci" }, { } }; U_BOOT_DRIVER(pci_sandbox) = { .name = "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, .per_child_platdata_auto_alloc_size = sizeof(struct pci_child_platdata), };