| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 2014 Google, Inc |
| * Written by Simon Glass <sjg@chromium.org> |
| */ |
| |
| #include <common.h> |
| #include <dm.h> |
| #include <fdtdec.h> |
| #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 *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), |
| }; |