Tom Rini | 10e4779 | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (c) 2014 Google, Inc |
| 4 | * Written by Simon Glass <sjg@chromium.org> |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 5 | */ |
| 6 | |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 7 | #include <dm.h> |
| 8 | #include <fdtdec.h> |
Simon Glass | 0f2af88 | 2020-05-10 11:40:05 -0600 | [diff] [blame] | 9 | #include <log.h> |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 10 | #include <pci.h> |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 11 | |
Bin Meng | 156bc6f | 2018-08-03 01:14:45 -0700 | [diff] [blame] | 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 | |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 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; |
Bin Meng | 156bc6f | 2018-08-03 01:14:45 -0700 | [diff] [blame] | 29 | struct udevice *container, *emul; |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 30 | int ret; |
| 31 | |
Bin Meng | 156bc6f | 2018-08-03 01:14:45 -0700 | [diff] [blame] | 32 | ret = sandbox_pci_get_emul(bus, devfn, &container, &emul); |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 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 | |
Simon Glass | 2a311e8 | 2020-01-27 08:49:37 -0700 | [diff] [blame] | 42 | static int sandbox_pci_read_config(const struct udevice *bus, pci_dev_t devfn, |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 43 | uint offset, ulong *valuep, |
| 44 | enum pci_size_t size) |
| 45 | { |
| 46 | struct dm_pci_emul_ops *ops; |
Bin Meng | 156bc6f | 2018-08-03 01:14:45 -0700 | [diff] [blame] | 47 | struct udevice *container, *emul; |
| 48 | struct sandbox_pci_priv *priv = dev_get_priv(bus); |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 49 | int ret; |
| 50 | |
| 51 | /* Prepare the default response */ |
| 52 | *valuep = pci_get_ff(size); |
Bin Meng | 156bc6f | 2018-08-03 01:14:45 -0700 | [diff] [blame] | 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 | } |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 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 | |
Bin Meng | 156bc6f | 2018-08-03 01:14:45 -0700 | [diff] [blame] | 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 | |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 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, |
Bin Meng | 156bc6f | 2018-08-03 01:14:45 -0700 | [diff] [blame] | 129 | .probe = sandbox_pci_probe, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 130 | .priv_auto = sizeof(struct sandbox_pci_priv), |
Simon Glass | 1823034 | 2016-07-05 17:10:10 -0600 | [diff] [blame] | 131 | |
| 132 | /* Attach an emulator if we can */ |
| 133 | .child_post_bind = dm_scan_fdt_dev, |
Simon Glass | b75b15b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 134 | .per_child_plat_auto = sizeof(struct pci_child_plat), |
Simon Glass | 4d85755 | 2015-03-05 12:25:27 -0700 | [diff] [blame] | 135 | }; |