summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci-emul-uclass.c24
-rw-r--r--drivers/pci/pci-uclass.c83
-rw-r--r--drivers/pci/pci_sandbox.c78
3 files changed, 163 insertions, 22 deletions
diff --git a/drivers/pci/pci-emul-uclass.c b/drivers/pci/pci-emul-uclass.c
index 79e2c1420e..3822758354 100644
--- a/drivers/pci/pci-emul-uclass.c
+++ b/drivers/pci/pci-emul-uclass.c
@@ -11,33 +11,39 @@
#include <pci.h>
#include <dm/lists.h>
-struct sandbox_pci_priv {
+struct sandbox_pci_emul_priv {
int dev_count;
};
int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
- struct udevice **emulp)
+ struct udevice **containerp, struct udevice **emulp)
{
struct udevice *dev;
int ret;
- ret = pci_bus_find_devfn(bus, find_devfn, &dev);
+ *containerp = NULL;
+ ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(find_devfn), &dev);
if (ret) {
debug("%s: Could not find emulator for dev %x\n", __func__,
find_devfn);
return ret;
}
+ *containerp = dev;
- ret = device_find_first_child(dev, emulp);
- if (ret)
- return ret;
+ if (device_get_uclass_id(dev) == UCLASS_PCI_GENERIC) {
+ ret = device_find_first_child(dev, emulp);
+ if (ret)
+ return ret;
+ } else {
+ *emulp = dev;
+ }
return *emulp ? 0 : -ENODEV;
}
static int sandbox_pci_emul_post_probe(struct udevice *dev)
{
- struct sandbox_pci_priv *priv = dev->uclass->priv;
+ struct sandbox_pci_emul_priv *priv = dev->uclass->priv;
priv->dev_count++;
sandbox_set_enable_pci_map(true);
@@ -47,7 +53,7 @@ static int sandbox_pci_emul_post_probe(struct udevice *dev)
static int sandbox_pci_emul_pre_remove(struct udevice *dev)
{
- struct sandbox_pci_priv *priv = dev->uclass->priv;
+ struct sandbox_pci_emul_priv *priv = dev->uclass->priv;
priv->dev_count--;
sandbox_set_enable_pci_map(priv->dev_count > 0);
@@ -60,5 +66,5 @@ UCLASS_DRIVER(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),
+ .priv_auto_alloc_size = sizeof(struct sandbox_pci_emul_priv),
};
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 46e9c71bdf..e9671d9b76 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -690,7 +690,7 @@ static int pci_find_and_bind_driver(struct udevice *parent,
if (ret)
goto error;
debug("%s: Match found: %s\n", __func__, drv->name);
- dev->driver_data = find_id->driver_data;
+ dev->driver_data = id->driver_data;
*devp = dev;
return 0;
}
@@ -745,6 +745,8 @@ int pci_bind_bus_devices(struct udevice *bus)
struct udevice *dev;
ulong class;
+ if (!PCI_FUNC(bdf))
+ found_multi = false;
if (PCI_FUNC(bdf) && !found_multi)
continue;
/* Check only the first access, we don't expect problems */
@@ -987,19 +989,18 @@ static int pci_uclass_child_post_bind(struct udevice *dev)
if (!dev_of_valid(dev))
return 0;
- /*
- * We could read vendor, device, class if available. But for now we
- * just check the address.
- */
pplat = dev_get_parent_platdata(dev);
+
+ /* Extract vendor id and device id if available */
+ ofnode_read_pci_vendev(dev_ofnode(dev), &pplat->vendor, &pplat->device);
+
+ /* Extract the devfn from fdt_pci_addr */
ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_CONFIG, "reg",
&addr);
-
if (ret) {
if (ret != -ENOENT)
return -EINVAL;
} else {
- /* extract the devfn from fdt_pci_addr */
pplat->devfn = addr.phys_hi & 0xff00;
}
@@ -1319,6 +1320,74 @@ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
return dm_pci_bus_to_virt(dev, pci_bus_addr, flags, 0, MAP_NOCACHE);
}
+int dm_pci_find_capability(struct udevice *dev, int cap)
+{
+ u16 status;
+ u8 header_type;
+ int ttl = PCI_FIND_CAP_TTL;
+ u8 id;
+ u16 ent;
+ u8 pos;
+
+ dm_pci_read_config16(dev, PCI_STATUS, &status);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+
+ dm_pci_read_config8(dev, PCI_HEADER_TYPE, &header_type);
+ if ((header_type & 0x7f) == PCI_HEADER_TYPE_CARDBUS)
+ pos = PCI_CB_CAPABILITY_LIST;
+ else
+ pos = PCI_CAPABILITY_LIST;
+
+ dm_pci_read_config8(dev, pos, &pos);
+ while (ttl--) {
+ if (pos < PCI_STD_HEADER_SIZEOF)
+ break;
+ pos &= ~3;
+ dm_pci_read_config16(dev, pos, &ent);
+
+ id = ent & 0xff;
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pos = (ent >> 8);
+ }
+
+ return 0;
+}
+
+int dm_pci_find_ext_capability(struct udevice *dev, int cap)
+{
+ u32 header;
+ int ttl;
+ int pos = PCI_CFG_SPACE_SIZE;
+
+ /* minimum 8 bytes per capability */
+ ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
+
+ dm_pci_read_config32(dev, pos, &header);
+ /*
+ * If we have no capabilities, this is indicated by cap ID,
+ * cap version and next pointer all being 0.
+ */
+ if (header == 0)
+ return 0;
+
+ while (ttl--) {
+ if (PCI_EXT_CAP_ID(header) == cap)
+ return pos;
+
+ pos = PCI_EXT_CAP_NEXT(header);
+ if (pos < PCI_CFG_SPACE_SIZE)
+ break;
+
+ dm_pci_read_config32(dev, pos, &header);
+ }
+
+ return 0;
+}
+
UCLASS_DRIVER(pci) = {
.id = UCLASS_PCI,
.name = "pci",
diff --git a/drivers/pci/pci_sandbox.c b/drivers/pci/pci_sandbox.c
index 67cd733e61..119a98d061 100644
--- a/drivers/pci/pci_sandbox.c
+++ b/drivers/pci/pci_sandbox.c
@@ -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,