dm: sandbox: pci: Add a PCI emulation uclass
authorSimon Glass <sjg@chromium.org>
Thu, 5 Mar 2015 19:25:28 +0000 (12:25 -0700)
committerSimon Glass <sjg@chromium.org>
Fri, 17 Apr 2015 01:27:43 +0000 (19:27 -0600)
Since sandbox does not have real devices (unless it borrows those from the
host) it must use emulations. Provide a uclass which permits PCI operations
to be passed through to an emulation device.

Signed-off-by: Simon Glass <sjg@chromium.org>
drivers/pci/Makefile
drivers/pci/pci-emul-uclass.c [new file with mode: 0644]
include/dm/uclass-id.h
include/pci.h

index 9e2e5f9aff0e2227f75427d90004974899bc128b..c1c2ae3c7240235774c83b7c53561ff18da5825b 100644 (file)
@@ -8,6 +8,7 @@
 ifneq ($(CONFIG_DM_PCI),)
 obj-$(CONFIG_PCI) += pci-uclass.o pci_compat.o
 obj-$(CONFIG_PCI_SANDBOX) += pci_sandbox.o
+obj-$(CONFIG_SANDBOX) += pci-emul-uclass.o
 else
 obj-$(CONFIG_PCI) += pci.o
 endif
diff --git a/drivers/pci/pci-emul-uclass.c b/drivers/pci/pci-emul-uclass.c
new file mode 100644 (file)
index 0000000..0f8e3c9
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <pci.h>
+#include <dm/lists.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sandbox_pci_priv {
+       int dev_count;
+};
+
+int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
+                        struct udevice **emulp)
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = pci_bus_find_devfn(bus, find_devfn, &dev);
+       if (ret) {
+               debug("%s: Could not find emulator for dev %x\n", __func__,
+                     find_devfn);
+               return ret;
+       }
+
+       ret = device_find_first_child(dev, emulp);
+       if (ret)
+               return ret;
+
+       return *emulp ? 0 : -ENODEV;
+}
+
+static int sandbox_pci_emul_post_probe(struct udevice *dev)
+{
+       struct sandbox_pci_priv *priv = dev->uclass->priv;
+
+       priv->dev_count++;
+       sandbox_set_enable_pci_map(true);
+
+       return 0;
+}
+
+static int sandbox_pci_emul_pre_remove(struct udevice *dev)
+{
+       struct sandbox_pci_priv *priv = dev->uclass->priv;
+
+       priv->dev_count--;
+       sandbox_set_enable_pci_map(priv->dev_count > 0);
+
+       return 0;
+}
+
+UCLASS_DRIVER(pci_emul) = {
+       .id             = UCLASS_PCI_EMUL,
+       .name           = "pci_emul",
+       .post_probe     = sandbox_pci_emul_post_probe,
+       .pre_remove     = sandbox_pci_emul_pre_remove,
+       .priv_auto_alloc_size   = sizeof(struct sandbox_pci_priv),
+};
index b984407c094ae2df7fa51567dbc2aec723b7f645..0b6e85037abfd488bcc58bfafb13cd82832a33a0 100644 (file)
@@ -20,6 +20,7 @@ enum uclass_id {
        UCLASS_TEST_BUS,
        UCLASS_SPI_EMUL,        /* sandbox SPI device emulator */
        UCLASS_I2C_EMUL,        /* sandbox I2C device emulator */
+       UCLASS_PCI_EMUL,        /* sandbox PCI device emulator */
        UCLASS_SIMPLE_BUS,
 
        /* U-Boot uclasses start here */
index 07345fd485ed5fa46f67d09075ab522e1423ffd0..07b1e9a4f54ace148ee72061348c203beb5e81a0 100644 (file)
@@ -992,6 +992,114 @@ static inline int pci_read_config_byte(pci_dev_t pcidev, int offset,
        return pci_read_config8(pcidev, offset, valuep);
 }
 
+/**
+ * struct dm_pci_emul_ops - PCI device emulator operations
+ */
+struct dm_pci_emul_ops {
+       /**
+        * get_devfn(): Check which device and function this emulators
+        *
+        * @dev:        device to check
+        * @return the device and function this emulates, or -ve on error
+        */
+       int (*get_devfn)(struct udevice *dev);
+       /**
+        * read_config() - Read a PCI configuration value
+        *
+        * @dev:        Emulated device to read from
+        * @offset:     Byte offset within the device's configuration space
+        * @valuep:     Place to put the returned value
+        * @size:       Access size
+        * @return 0 if OK, -ve on error
+        */
+       int (*read_config)(struct udevice *dev, uint offset, ulong *valuep,
+                          enum pci_size_t size);
+       /**
+        * write_config() - Write a PCI configuration value
+        *
+        * @dev:        Emulated device to write to
+        * @offset:     Byte offset within the device's configuration space
+        * @value:      Value to write
+        * @size:       Access size
+        * @return 0 if OK, -ve on error
+        */
+       int (*write_config)(struct udevice *dev, uint offset, ulong value,
+                           enum pci_size_t size);
+       /**
+        * read_io() - Read a PCI I/O value
+        *
+        * @dev:        Emulated device to read from
+        * @addr:       I/O address to read
+        * @valuep:     Place to put the returned value
+        * @size:       Access size
+        * @return 0 if OK, -ENOENT if @addr is not mapped by this device,
+        *              other -ve value on error
+        */
+       int (*read_io)(struct udevice *dev, unsigned int addr, ulong *valuep,
+                      enum pci_size_t size);
+       /**
+        * write_io() - Write a PCI I/O value
+        *
+        * @dev:        Emulated device to write from
+        * @addr:       I/O address to write
+        * @value:      Value to write
+        * @size:       Access size
+        * @return 0 if OK, -ENOENT if @addr is not mapped by this device,
+        *              other -ve value on error
+        */
+       int (*write_io)(struct udevice *dev, unsigned int addr,
+                       ulong value, enum pci_size_t size);
+       /**
+        * map_physmem() - Map a device into sandbox memory
+        *
+        * @dev:        Emulated device to map
+        * @addr:       Memory address, normally corresponding to a PCI BAR.
+        *              The device should have been configured to have a BAR
+        *              at this address.
+        * @lenp:       On entry, the size of the area to map, On exit it is
+        *              updated to the size actually mapped, which may be less
+        *              if the device has less space
+        * @ptrp:       Returns a pointer to the mapped address. The device's
+        *              space can be accessed as @lenp bytes starting here
+        * @return 0 if OK, -ENOENT if @addr is not mapped by this device,
+        *              other -ve value on error
+        */
+       int (*map_physmem)(struct udevice *dev, phys_addr_t addr,
+                          unsigned long *lenp, void **ptrp);
+       /**
+        * unmap_physmem() - undo a memory mapping
+        *
+        * This must be called after map_physmem() to undo the mapping.
+        * Some devices can use this to check what has been written into
+        * their mapped memory and perform an operations they require on it.
+        * In this way, map/unmap can be used as a sort of handshake between
+        * the emulated device and its users.
+        *
+        * @dev:        Emuated device to unmap
+        * @vaddr:      Mapped memory address, as passed to map_physmem()
+        * @len:        Size of area mapped, as returned by map_physmem()
+        * @return 0 if OK, -ve on error
+        */
+       int (*unmap_physmem)(struct udevice *dev, const void *vaddr,
+                            unsigned long len);
+};
+
+/* Get access to a PCI device emulator's operations */
+#define pci_get_emul_ops(dev)  ((struct dm_pci_emul_ops *)(dev)->driver->ops)
+
+/**
+ * sandbox_pci_get_emul() - Get the emulation device for a PCI device
+ *
+ * Searches for a suitable emulator for the given PCI bus device
+ *
+ * @bus:       PCI bus to search
+ * @find_devfn:        PCI device and function address (PCI_DEVFN())
+ * @emulp:     Returns emulated device if found
+ * @return 0 if found, -ENODEV if not found
+ */
+int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
+                        struct udevice **emulp);
+
 #endif
 
 #endif /* __ASSEMBLY__ */