|
@@ -10,15 +10,27 @@
|
|
|
#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);
|
|
@@ -33,14 +45,31 @@ static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn,
|
|
|
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;
|
|
@@ -48,6 +77,41 @@ static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn,
|
|
|
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,
|
|
@@ -63,6 +127,8 @@ U_BOOT_DRIVER(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,
|