1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2014 Google, Inc 4 * Written by Simon Glass <sjg@chromium.org> 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <fdtdec.h> 10 #include <pci.h> 11 12 #define FDT_DEV_INFO_CELLS 4 13 #define FDT_DEV_INFO_SIZE (FDT_DEV_INFO_CELLS * sizeof(u32)) 14 15 #define SANDBOX_PCI_DEVFN(d, f) ((d << 3) | f) 16 17 struct sandbox_pci_priv { 18 struct { 19 u16 vendor; 20 u16 device; 21 } vendev[256]; 22 }; 23 24 static int sandbox_pci_write_config(struct udevice *bus, pci_dev_t devfn, 25 uint offset, ulong value, 26 enum pci_size_t size) 27 { 28 struct dm_pci_emul_ops *ops; 29 struct udevice *container, *emul; 30 int ret; 31 32 ret = sandbox_pci_get_emul(bus, devfn, &container, &emul); 33 if (ret) 34 return ret == -ENODEV ? 0 : ret; 35 ops = pci_get_emul_ops(emul); 36 if (!ops || !ops->write_config) 37 return -ENOSYS; 38 39 return ops->write_config(emul, offset, value, size); 40 } 41 42 static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn, 43 uint offset, ulong *valuep, 44 enum pci_size_t size) 45 { 46 struct dm_pci_emul_ops *ops; 47 struct udevice *container, *emul; 48 struct sandbox_pci_priv *priv = dev_get_priv(bus); 49 int ret; 50 51 /* Prepare the default response */ 52 *valuep = pci_get_ff(size); 53 ret = sandbox_pci_get_emul(bus, devfn, &container, &emul); 54 if (ret) { 55 if (!container) { 56 u16 vendor, device; 57 58 devfn = SANDBOX_PCI_DEVFN(PCI_DEV(devfn), 59 PCI_FUNC(devfn)); 60 vendor = priv->vendev[devfn].vendor; 61 device = priv->vendev[devfn].device; 62 if (offset == PCI_VENDOR_ID && vendor) 63 *valuep = vendor; 64 else if (offset == PCI_DEVICE_ID && device) 65 *valuep = device; 66 67 return 0; 68 } else { 69 return ret == -ENODEV ? 0 : ret; 70 } 71 } 72 ops = pci_get_emul_ops(emul); 73 if (!ops || !ops->read_config) 74 return -ENOSYS; 75 76 return ops->read_config(emul, offset, valuep, size); 77 } 78 79 static int sandbox_pci_probe(struct udevice *dev) 80 { 81 struct sandbox_pci_priv *priv = dev_get_priv(dev); 82 const fdt32_t *cell; 83 u8 pdev, pfn, devfn; 84 int len; 85 86 cell = ofnode_get_property(dev_ofnode(dev), "sandbox,dev-info", &len); 87 if (!cell) 88 return 0; 89 90 if ((len % FDT_DEV_INFO_SIZE) == 0) { 91 int num = len / FDT_DEV_INFO_SIZE; 92 int i; 93 94 for (i = 0; i < num; i++) { 95 debug("dev info #%d: %02x %02x %04x %04x\n", i, 96 fdt32_to_cpu(cell[0]), fdt32_to_cpu(cell[1]), 97 fdt32_to_cpu(cell[2]), fdt32_to_cpu(cell[3])); 98 99 pdev = fdt32_to_cpu(cell[0]); 100 pfn = fdt32_to_cpu(cell[1]); 101 if (pdev > 31 || pfn > 7) 102 continue; 103 devfn = SANDBOX_PCI_DEVFN(pdev, pfn); 104 priv->vendev[devfn].vendor = fdt32_to_cpu(cell[2]); 105 priv->vendev[devfn].device = fdt32_to_cpu(cell[3]); 106 107 cell += FDT_DEV_INFO_CELLS; 108 } 109 } 110 111 return 0; 112 } 113 114 static const struct dm_pci_ops sandbox_pci_ops = { 115 .read_config = sandbox_pci_read_config, 116 .write_config = sandbox_pci_write_config, 117 }; 118 119 static const struct udevice_id sandbox_pci_ids[] = { 120 { .compatible = "sandbox,pci" }, 121 { } 122 }; 123 124 U_BOOT_DRIVER(pci_sandbox) = { 125 .name = "pci_sandbox", 126 .id = UCLASS_PCI, 127 .of_match = sandbox_pci_ids, 128 .ops = &sandbox_pci_ops, 129 .probe = sandbox_pci_probe, 130 .priv_auto_alloc_size = sizeof(struct sandbox_pci_priv), 131 132 /* Attach an emulator if we can */ 133 .child_post_bind = dm_scan_fdt_dev, 134 .per_child_platdata_auto_alloc_size = 135 sizeof(struct pci_child_platdata), 136 }; 137