summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig22
-rw-r--r--drivers/pci/Makefile11
-rw-r--r--drivers/pci/pci-emul-uclass.c67
-rw-r--r--drivers/pci/pci-uclass.c639
-rw-r--r--drivers/pci/pci.c281
-rw-r--r--drivers/pci/pci_auto.c16
-rw-r--r--drivers/pci/pci_common.c292
-rw-r--r--drivers/pci/pci_compat.c43
-rw-r--r--drivers/pci/pci_sandbox.c79
-rw-r--r--drivers/pci/pci_x86.c24
-rw-r--r--drivers/pci/pcie_layerscape.c52
11 files changed, 1230 insertions, 296 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index e69de29..167d405 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -0,0 +1,22 @@
+menu "PCI"
+
+config DM_PCI
+ bool "Enable driver mode for PCI"
+ depends on DM
+ help
+ Use driver model for PCI. Driver model is the new method for
+ orgnising devices in U-Boot. For PCI, driver model keeps track of
+ available PCI devices, allows scanning of PCI buses and provides
+ device configuration support.
+
+config PCI_SANDBOX
+ bool "Sandbox PCI support"
+ depends on SANDBOX && DM_PCI
+ help
+ Support PCI on sandbox, as an emulated bus. This permits testing of
+ PCI feature such as bus scanning, device configuration and device
+ access. The available (emulated) devices are defined statically in
+ the device tree but the normal PCI scan technique is used to find
+ then.
+
+endmenu
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 50b7be5..adc238f 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -5,8 +5,17 @@
# SPDX-License-Identifier: GPL-2.0+
#
+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
+obj-$(CONFIG_X86) += pci_x86.o
+else
+obj-$(CONFIG_PCI) += pci.o
+endif
+obj-$(CONFIG_PCI) += pci_common.o pci_auto.o pci_rom.o
+
obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o
-obj-$(CONFIG_PCI) += pci.o pci_auto.o pci_rom.o
obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o
obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o
obj-$(CONFIG_PCI_MSC01) += pci_msc01.o
diff --git a/drivers/pci/pci-emul-uclass.c b/drivers/pci/pci-emul-uclass.c
new file mode 100644
index 0000000..0f8e3c9
--- /dev/null
+++ b/drivers/pci/pci-emul-uclass.c
@@ -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),
+};
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
new file mode 100644
index 0000000..d48d865
--- /dev/null
+++ b/drivers/pci/pci-uclass.c
@@ -0,0 +1,639 @@
+/*
+ * 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 <errno.h>
+#include <fdtdec.h>
+#include <inttypes.h>
+#include <pci.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+#include <dm/device-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pci_controller *pci_bus_to_hose(int busnum)
+{
+ struct udevice *bus;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
+ if (ret) {
+ debug("%s: Cannot get bus %d: ret=%d\n", __func__, busnum, ret);
+ return NULL;
+ }
+ return dev_get_uclass_priv(bus);
+}
+
+/**
+ * pci_get_bus_max() - returns the bus number of the last active bus
+ *
+ * @return last bus number, or -1 if no active buses
+ */
+static int pci_get_bus_max(void)
+{
+ struct udevice *bus;
+ struct uclass *uc;
+ int ret = -1;
+
+ ret = uclass_get(UCLASS_PCI, &uc);
+ uclass_foreach_dev(bus, uc) {
+ if (bus->seq > ret)
+ ret = bus->seq;
+ }
+
+ debug("%s: ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int pci_last_busno(void)
+{
+ struct pci_controller *hose;
+ struct udevice *bus;
+ struct uclass *uc;
+ int ret;
+
+ debug("pci_last_busno\n");
+ ret = uclass_get(UCLASS_PCI, &uc);
+ if (ret || list_empty(&uc->dev_head))
+ return -1;
+
+ /* Probe the last bus */
+ bus = list_entry(uc->dev_head.prev, struct udevice, uclass_node);
+ debug("bus = %p, %s\n", bus, bus->name);
+ assert(bus);
+ ret = device_probe(bus);
+ if (ret)
+ return ret;
+
+ /* If that bus has bridges, we may have new buses now. Get the last */
+ bus = list_entry(uc->dev_head.prev, struct udevice, uclass_node);
+ hose = dev_get_uclass_priv(bus);
+ debug("bus = %s, hose = %p\n", bus->name, hose);
+
+ return hose->last_busno;
+}
+
+int pci_get_ff(enum pci_size_t size)
+{
+ switch (size) {
+ case PCI_SIZE_8:
+ return 0xff;
+ case PCI_SIZE_16:
+ return 0xffff;
+ default:
+ return 0xffffffff;
+ }
+}
+
+int pci_bus_find_devfn(struct udevice *bus, pci_dev_t find_devfn,
+ struct udevice **devp)
+{
+ struct udevice *dev;
+
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ struct pci_child_platdata *pplat;
+
+ pplat = dev_get_parent_platdata(dev);
+ if (pplat && pplat->devfn == find_devfn) {
+ *devp = dev;
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+int pci_bus_find_bdf(pci_dev_t bdf, struct udevice **devp)
+{
+ struct udevice *bus;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
+ if (ret)
+ return ret;
+ return pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), devp);
+}
+
+static int pci_device_matches_ids(struct udevice *dev,
+ struct pci_device_id *ids)
+{
+ struct pci_child_platdata *pplat;
+ int i;
+
+ pplat = dev_get_parent_platdata(dev);
+ if (!pplat)
+ return -EINVAL;
+ for (i = 0; ids[i].vendor != 0; i++) {
+ if (pplat->vendor == ids[i].vendor &&
+ pplat->device == ids[i].device)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+int pci_bus_find_devices(struct udevice *bus, struct pci_device_id *ids,
+ int *indexp, struct udevice **devp)
+{
+ struct udevice *dev;
+
+ /* Scan all devices on this bus */
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ if (pci_device_matches_ids(dev, ids) >= 0) {
+ if ((*indexp)-- <= 0) {
+ *devp = dev;
+ return 0;
+ }
+ }
+ }
+
+ return -ENODEV;
+}
+
+int pci_find_device_id(struct pci_device_id *ids, int index,
+ struct udevice **devp)
+{
+ struct udevice *bus;
+
+ /* Scan all known buses */
+ for (uclass_first_device(UCLASS_PCI, &bus);
+ bus;
+ uclass_next_device(&bus)) {
+ if (!pci_bus_find_devices(bus, ids, &index, devp))
+ return 0;
+ }
+ *devp = NULL;
+
+ return -ENODEV;
+}
+
+int pci_bus_write_config(struct udevice *bus, pci_dev_t bdf, int offset,
+ unsigned long value, enum pci_size_t size)
+{
+ struct dm_pci_ops *ops;
+
+ ops = pci_get_ops(bus);
+ if (!ops->write_config)
+ return -ENOSYS;
+ return ops->write_config(bus, bdf, offset, value, size);
+}
+
+int pci_write_config(pci_dev_t bdf, int offset, unsigned long value,
+ enum pci_size_t size)
+{
+ struct udevice *bus;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
+ if (ret)
+ return ret;
+
+ return pci_bus_write_config(bus, PCI_MASK_BUS(bdf), offset, value,
+ size);
+}
+
+int pci_write_config32(pci_dev_t bdf, int offset, u32 value)
+{
+ return pci_write_config(bdf, offset, value, PCI_SIZE_32);
+}
+
+int pci_write_config16(pci_dev_t bdf, int offset, u16 value)
+{
+ return pci_write_config(bdf, offset, value, PCI_SIZE_16);
+}
+
+int pci_write_config8(pci_dev_t bdf, int offset, u8 value)
+{
+ return pci_write_config(bdf, offset, value, PCI_SIZE_8);
+}
+
+int pci_bus_read_config(struct udevice *bus, pci_dev_t bdf, int offset,
+ unsigned long *valuep, enum pci_size_t size)
+{
+ struct dm_pci_ops *ops;
+
+ ops = pci_get_ops(bus);
+ if (!ops->read_config)
+ return -ENOSYS;
+ return ops->read_config(bus, bdf, offset, valuep, size);
+}
+
+int pci_read_config(pci_dev_t bdf, int offset, unsigned long *valuep,
+ enum pci_size_t size)
+{
+ struct udevice *bus;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
+ if (ret)
+ return ret;
+
+ return pci_bus_read_config(bus, PCI_MASK_BUS(bdf), offset, valuep,
+ size);
+}
+
+int pci_read_config32(pci_dev_t bdf, int offset, u32 *valuep)
+{
+ unsigned long value;
+ int ret;
+
+ ret = pci_read_config(bdf, offset, &value, PCI_SIZE_32);
+ if (ret)
+ return ret;
+ *valuep = value;
+
+ return 0;
+}
+
+int pci_read_config16(pci_dev_t bdf, int offset, u16 *valuep)
+{
+ unsigned long value;
+ int ret;
+
+ ret = pci_read_config(bdf, offset, &value, PCI_SIZE_16);
+ if (ret)
+ return ret;
+ *valuep = value;
+
+ return 0;
+}
+
+int pci_read_config8(pci_dev_t bdf, int offset, u8 *valuep)
+{
+ unsigned long value;
+ int ret;
+
+ ret = pci_read_config(bdf, offset, &value, PCI_SIZE_8);
+ if (ret)
+ return ret;
+ *valuep = value;
+
+ return 0;
+}
+
+int pci_auto_config_devices(struct udevice *bus)
+{
+ struct pci_controller *hose = bus->uclass_priv;
+ unsigned int sub_bus;
+ struct udevice *dev;
+ int ret;
+
+ sub_bus = bus->seq;
+ debug("%s: start\n", __func__);
+ pciauto_config_init(hose);
+ for (ret = device_find_first_child(bus, &dev);
+ !ret && dev;
+ ret = device_find_next_child(&dev)) {
+ struct pci_child_platdata *pplat;
+
+ pplat = dev_get_parent_platdata(dev);
+ unsigned int max_bus;
+ pci_dev_t bdf;
+
+ bdf = PCI_ADD_BUS(bus->seq, pplat->devfn);
+ debug("%s: device %s\n", __func__, dev->name);
+ max_bus = pciauto_config_device(hose, bdf);
+ sub_bus = max(sub_bus, max_bus);
+ }
+ debug("%s: done\n", __func__);
+
+ return sub_bus;
+}
+
+int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf)
+{
+ struct udevice *parent, *bus;
+ int sub_bus;
+ int ret;
+
+ debug("%s\n", __func__);
+ parent = hose->bus;
+
+ /* Find the bus within the parent */
+ ret = pci_bus_find_devfn(parent, bdf, &bus);
+ if (ret) {
+ debug("%s: Cannot find device %x on bus %s: %d\n", __func__,
+ bdf, parent->name, ret);
+ return ret;
+ }
+
+ sub_bus = pci_get_bus_max() + 1;
+ debug("%s: bus = %d/%s\n", __func__, sub_bus, bus->name);
+ pciauto_prescan_setup_bridge(hose, bdf, bus->seq);
+
+ ret = device_probe(bus);
+ if (ret) {
+ debug("%s: Cannot probe bus bus %s: %d\n", __func__, bus->name,
+ ret);
+ return ret;
+ }
+ if (sub_bus != bus->seq) {
+ printf("%s: Internal error, bus '%s' got seq %d, expected %d\n",
+ __func__, bus->name, bus->seq, sub_bus);
+ return -EPIPE;
+ }
+ sub_bus = pci_get_bus_max();
+ pciauto_postscan_setup_bridge(hose, bdf, sub_bus);
+
+ return sub_bus;
+}
+
+int pci_bind_bus_devices(struct udevice *bus)
+{
+ ulong vendor, device;
+ ulong header_type;
+ pci_dev_t devfn, end;
+ bool found_multi;
+ int ret;
+
+ found_multi = false;
+ end = PCI_DEVFN(PCI_MAX_PCI_DEVICES - 1, PCI_MAX_PCI_FUNCTIONS - 1);
+ for (devfn = PCI_DEVFN(0, 0); devfn < end; devfn += PCI_DEVFN(0, 1)) {
+ struct pci_child_platdata *pplat;
+ struct udevice *dev;
+ ulong class;
+
+ if (PCI_FUNC(devfn) && !found_multi)
+ continue;
+ /* Check only the first access, we don't expect problems */
+ ret = pci_bus_read_config(bus, devfn, PCI_HEADER_TYPE,
+ &header_type, PCI_SIZE_8);
+ if (ret)
+ goto error;
+ pci_bus_read_config(bus, devfn, PCI_VENDOR_ID, &vendor,
+ PCI_SIZE_16);
+ if (vendor == 0xffff || vendor == 0x0000)
+ continue;
+
+ if (!PCI_FUNC(devfn))
+ found_multi = header_type & 0x80;
+
+ debug("%s: bus %d/%s: found device %x, function %d\n", __func__,
+ bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
+ pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
+ PCI_SIZE_16);
+ pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class,
+ PCI_SIZE_16);
+
+ /* Find this device in the device tree */
+ ret = pci_bus_find_devfn(bus, devfn, &dev);
+
+ /* If nothing in the device tree, bind a generic device */
+ if (ret == -ENODEV) {
+ char name[30], *str;
+ const char *drv;
+
+ sprintf(name, "pci_%x:%x.%x", bus->seq,
+ PCI_DEV(devfn), PCI_FUNC(devfn));
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ drv = class == PCI_CLASS_BRIDGE_PCI ?
+ "pci_bridge_drv" : "pci_generic_drv";
+ ret = device_bind_driver(bus, drv, str, &dev);
+ }
+ if (ret)
+ return ret;
+
+ /* Update the platform data */
+ pplat = dev_get_parent_platdata(dev);
+ pplat->devfn = devfn;
+ pplat->vendor = vendor;
+ pplat->device = device;
+ pplat->class = class;
+ }
+
+ return 0;
+error:
+ printf("Cannot read bus configuration: %d\n", ret);
+
+ return ret;
+}
+
+static int pci_uclass_post_bind(struct udevice *bus)
+{
+ /*
+ * Scan the device tree for devices. This does not probe the PCI bus,
+ * as this is not permitted while binding. It just finds devices
+ * mentioned in the device tree.
+ *
+ * Before relocation, only bind devices marked for pre-relocation
+ * use.
+ */
+ return dm_scan_fdt_node(bus, gd->fdt_blob, bus->of_offset,
+ gd->flags & GD_FLG_RELOC ? false : true);
+}
+
+static int decode_regions(struct pci_controller *hose, const void *blob,
+ int parent_node, int node)
+{
+ int pci_addr_cells, addr_cells, size_cells;
+ int cells_per_record;
+ const u32 *prop;
+ int len;
+ int i;
+
+ prop = fdt_getprop(blob, node, "ranges", &len);
+ if (!prop)
+ return -EINVAL;
+ pci_addr_cells = fdt_address_cells(blob, node);
+ addr_cells = fdt_address_cells(blob, parent_node);
+ size_cells = fdt_size_cells(blob, node);
+
+ /* PCI addresses are always 3-cells */
+ len /= sizeof(u32);
+ cells_per_record = pci_addr_cells + addr_cells + size_cells;
+ hose->region_count = 0;
+ debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
+ cells_per_record);
+ for (i = 0; i < MAX_PCI_REGIONS; i++, len -= cells_per_record) {
+ u64 pci_addr, addr, size;
+ int space_code;
+ u32 flags;
+ int type;
+
+ if (len < cells_per_record)
+ break;
+ flags = fdt32_to_cpu(prop[0]);
+ space_code = (flags >> 24) & 3;
+ pci_addr = fdtdec_get_number(prop + 1, 2);
+ prop += pci_addr_cells;
+ addr = fdtdec_get_number(prop, addr_cells);
+ prop += addr_cells;
+ size = fdtdec_get_number(prop, size_cells);
+ prop += size_cells;
+ debug("%s: region %d, pci_addr=%" PRIx64 ", addr=%" PRIx64
+ ", size=%" PRIx64 ", space_code=%d\n", __func__,
+ hose->region_count, pci_addr, addr, size, space_code);
+ if (space_code & 2) {
+ type = flags & (1U << 30) ? PCI_REGION_PREFETCH :
+ PCI_REGION_MEM;
+ } else if (space_code & 1) {
+ type = PCI_REGION_IO;
+ } else {
+ continue;
+ }
+ debug(" - type=%d\n", type);
+ pci_set_region(hose->regions + hose->region_count++, pci_addr,
+ addr, size, type);
+ }
+
+ /* Add a region for our local memory */
+ pci_set_region(hose->regions + hose->region_count++, 0, 0,
+ gd->ram_size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+
+ return 0;
+}
+
+static int pci_uclass_pre_probe(struct udevice *bus)
+{
+ struct pci_controller *hose;
+ int ret;
+
+ debug("%s, bus=%d/%s, parent=%s\n", __func__, bus->seq, bus->name,
+ bus->parent->name);
+ hose = bus->uclass_priv;
+
+ /* For bridges, use the top-level PCI controller */
+ if (device_get_uclass_id(bus->parent) == UCLASS_ROOT) {
+ hose->ctlr = bus;
+ ret = decode_regions(hose, gd->fdt_blob, bus->parent->of_offset,
+ bus->of_offset);
+ if (ret) {
+ debug("%s: Cannot decode regions\n", __func__);
+ return ret;
+ }
+ } else {
+ struct pci_controller *parent_hose;
+
+ parent_hose = dev_get_uclass_priv(bus->parent);
+ hose->ctlr = parent_hose->bus;
+ }
+ hose->bus = bus;
+ hose->first_busno = bus->seq;
+ hose->last_busno = bus->seq;
+
+ return 0;
+}
+
+static int pci_uclass_post_probe(struct udevice *bus)
+{
+ int ret;
+
+ /* Don't scan buses before relocation */
+ if (!(gd->flags & GD_FLG_RELOC))
+ return 0;
+
+ debug("%s: probing bus %d\n", __func__, bus->seq);
+ ret = pci_bind_bus_devices(bus);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_PCI_PNP
+ ret = pci_auto_config_devices(bus);
+#endif
+
+ return ret < 0 ? ret : 0;
+}
+
+static int pci_uclass_child_post_bind(struct udevice *dev)
+{
+ struct pci_child_platdata *pplat;
+ struct fdt_pci_addr addr;
+ int ret;
+
+ if (dev->of_offset == -1)
+ return 0;
+
+ /*
+ * We could read vendor, device, class if available. But for now we
+ * just check the address.
+ */
+ pplat = dev_get_parent_platdata(dev);
+ ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset,
+ FDT_PCI_SPACE_CONFIG, "reg", &addr);
+
+ if (ret) {
+ if (ret != -ENOENT)
+ return -EINVAL;
+ } else {
+ /* extract the bdf from fdt_pci_addr */
+ pplat->devfn = addr.phys_hi & 0xffff00;
+ }
+
+ return 0;
+}
+
+int pci_bridge_read_config(struct udevice *bus, pci_dev_t devfn, uint offset,
+ ulong *valuep, enum pci_size_t size)
+{
+ struct pci_controller *hose = bus->uclass_priv;
+ pci_dev_t bdf = PCI_ADD_BUS(bus->seq, devfn);
+
+ return pci_bus_read_config(hose->ctlr, bdf, offset, valuep, size);
+}
+
+int pci_bridge_write_config(struct udevice *bus, pci_dev_t devfn, uint offset,
+ ulong value, enum pci_size_t size)
+{
+ struct pci_controller *hose = bus->uclass_priv;
+ pci_dev_t bdf = PCI_ADD_BUS(bus->seq, devfn);
+
+ return pci_bus_write_config(hose->ctlr, bdf, offset, value, size);
+}
+
+UCLASS_DRIVER(pci) = {
+ .id = UCLASS_PCI,
+ .name = "pci",
+ .post_bind = pci_uclass_post_bind,
+ .pre_probe = pci_uclass_pre_probe,
+ .post_probe = pci_uclass_post_probe,
+ .child_post_bind = pci_uclass_child_post_bind,
+ .per_device_auto_alloc_size = sizeof(struct pci_controller),
+ .per_child_platdata_auto_alloc_size =
+ sizeof(struct pci_child_platdata),
+};
+
+static const struct dm_pci_ops pci_bridge_ops = {
+ .read_config = pci_bridge_read_config,
+ .write_config = pci_bridge_write_config,
+};
+
+static const struct udevice_id pci_bridge_ids[] = {
+ { .compatible = "pci-bridge" },
+ { }
+};
+
+U_BOOT_DRIVER(pci_bridge_drv) = {
+ .name = "pci_bridge_drv",
+ .id = UCLASS_PCI,
+ .of_match = pci_bridge_ids,
+ .ops = &pci_bridge_ops,
+};
+
+UCLASS_DRIVER(pci_generic) = {
+ .id = UCLASS_PCI_GENERIC,
+ .name = "pci_generic",
+};
+
+static const struct udevice_id pci_generic_ids[] = {
+ { .compatible = "pci-generic" },
+ { }
+};
+
+U_BOOT_DRIVER(pci_generic_drv) = {
+ .name = "pci_generic_drv",
+ .id = UCLASS_PCI_GENERIC,
+ .of_match = pci_generic_ids,
+};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e1296ca..3babd94 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -101,25 +101,6 @@ PCI_READ_VIA_DWORD_OP(word, u16 *, 0x02)
PCI_WRITE_VIA_DWORD_OP(byte, u8, 0x03, 0x000000ff)
PCI_WRITE_VIA_DWORD_OP(word, u16, 0x02, 0x0000ffff)
-/* Get a virtual address associated with a BAR region */
-void *pci_map_bar(pci_dev_t pdev, int bar, int flags)
-{
- pci_addr_t pci_bus_addr;
- u32 bar_response;
-
- /* read BAR address */
- pci_read_config_dword(pdev, bar, &bar_response);
- pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
-
- /*
- * Pass "0" as the length argument to pci_bus_to_virt. The arg
- * isn't actualy used on any platform because u-boot assumes a static
- * linear mapping. In the future, this could read the BAR size
- * and pass that as the size if needed.
- */
- return pci_bus_to_virt(pdev, pci_bus_addr, flags, 0, MAP_NOCACHE);
-}
-
/*
*
*/
@@ -187,106 +168,22 @@ int pci_last_busno(void)
pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
{
struct pci_controller * hose;
- u16 vendor, device;
- u8 header_type;
pci_dev_t bdf;
- int i, bus, found_multi = 0;
+ int bus;
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
#ifdef CONFIG_SYS_SCSI_SCAN_BUS_REVERSE
- for (bus = hose->last_busno; bus >= hose->first_busno; bus--)
+ for (bus = hose->last_busno; bus >= hose->first_busno; bus--) {
#else
- for (bus = hose->first_busno; bus <= hose->last_busno; bus++)
+ for (bus = hose->first_busno; bus <= hose->last_busno; bus++) {
#endif
- for (bdf = PCI_BDF(bus, 0, 0);
- bdf < PCI_BDF(bus + 1, 0, 0);
- bdf += PCI_BDF(0, 0, 1)) {
- if (pci_skip_dev(hose, bdf))
- continue;
-
- if (!PCI_FUNC(bdf)) {
- pci_read_config_byte(bdf,
- PCI_HEADER_TYPE,
- &header_type);
-
- found_multi = header_type & 0x80;
- } else {
- if (!found_multi)
- continue;
- }
-
- pci_read_config_word(bdf,
- PCI_VENDOR_ID,
- &vendor);
- pci_read_config_word(bdf,
- PCI_DEVICE_ID,
- &device);
-
- for (i = 0; ids[i].vendor != 0; i++) {
- if (vendor == ids[i].vendor &&
- device == ids[i].device) {
- if (index <= 0)
- return bdf;
-
- index--;
- }
- }
- }
- }
-
- return -1;
-}
-
-pci_dev_t pci_find_class(uint find_class, int index)
-{
- int bus;
- int devnum;
- pci_dev_t bdf;
- uint32_t class;
-
- for (bus = 0; bus <= pci_last_busno(); bus++) {
- for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES - 1; devnum++) {
- pci_read_config_dword(PCI_BDF(bus, devnum, 0),
- PCI_CLASS_REVISION, &class);
- if (class >> 16 == 0xffff)
- continue;
-
- for (bdf = PCI_BDF(bus, devnum, 0);
- bdf <= PCI_BDF(bus, devnum,
- PCI_MAX_PCI_FUNCTIONS - 1);
- bdf += PCI_BDF(0, 0, 1)) {
- pci_read_config_dword(bdf, PCI_CLASS_REVISION,
- &class);
- class >>= 8;
-
- if (class != find_class)
- continue;
- /*
- * Decrement the index. We want to return the
- * correct device, so index is 0 for the first
- * matching device, 1 for the second, etc.
- */
- if (index) {
- index--;
- continue;
- }
- /* Return index'th controller. */
+ bdf = pci_hose_find_devices(hose, bus, ids, &index);
+ if (bdf != -1)
return bdf;
- }
}
}
- return -ENODEV;
-}
-
-pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)
-{
- struct pci_device_id ids[2] = { {}, {0, 0} };
-
- ids[0].vendor = vendor;
- ids[0].device = device;
-
- return pci_find_devices(ids, index);
+ return -1;
}
/*
@@ -355,87 +252,6 @@ pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose,
return bus_addr;
}
-int __pci_hose_bus_to_phys(struct pci_controller *hose,
- pci_addr_t bus_addr,
- unsigned long flags,
- unsigned long skip_mask,
- phys_addr_t *pa)
-{
- struct pci_region *res;
- int i;
-
- for (i = 0; i < hose->region_count; i++) {
- res = &hose->regions[i];
-
- if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
- continue;
-
- if (res->flags & skip_mask)
- continue;
-
- if (bus_addr >= res->bus_start &&
- (bus_addr - res->bus_start) < res->size) {
- *pa = (bus_addr - res->bus_start + res->phys_start);
- return 0;
- }
- }
-
- return 1;
-}
-
-phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose,
- pci_addr_t bus_addr,
- unsigned long flags)
-{
- phys_addr_t phys_addr = 0;
- int ret;
-
- if (!hose) {
- puts("pci_hose_bus_to_phys: invalid hose\n");
- return phys_addr;
- }
-
- /*
- * if PCI_REGION_MEM is set we do a two pass search with preference
- * on matches that don't have PCI_REGION_SYS_MEMORY set
- */
- if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {
- ret = __pci_hose_bus_to_phys(hose, bus_addr,
- flags, PCI_REGION_SYS_MEMORY, &phys_addr);
- if (!ret)
- return phys_addr;
- }
-
- ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr);
-
- if (ret)
- puts("pci_hose_bus_to_phys: invalid physical address\n");
-
- return phys_addr;
-}
-
-void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum,
- u32 addr_and_ctrl)
-{
- int bar;
-
- bar = PCI_BASE_ADDRESS_0 + barnum * 4;
- pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl);
-}
-
-u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum)
-{
- u32 addr;
- int bar;
-
- bar = PCI_BASE_ADDRESS_0 + barnum * 4;
- pci_hose_read_config_dword(hose, dev, bar, &addr);
- if (addr & PCI_BASE_ADDRESS_SPACE_IO)
- return addr & PCI_BASE_ADDRESS_IO_MASK;
- else
- return addr & PCI_BASE_ADDRESS_MEM_MASK;
-}
-
int pci_hose_config_device(struct pci_controller *hose,
pci_dev_t dev,
unsigned long io,
@@ -576,91 +392,6 @@ void pci_cfgfunc_do_nothing(struct pci_controller *hose,
*/
extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev);
-#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI_SCAN_SHOW)
-const char * pci_class_str(u8 class)
-{
- switch (class) {
- case PCI_CLASS_NOT_DEFINED:
- return "Build before PCI Rev2.0";
- break;
- case PCI_BASE_CLASS_STORAGE:
- return "Mass storage controller";
- break;
- case PCI_BASE_CLASS_NETWORK:
- return "Network controller";
- break;
- case PCI_BASE_CLASS_DISPLAY:
- return "Display controller";
- break;
- case PCI_BASE_CLASS_MULTIMEDIA:
- return "Multimedia device";
- break;
- case PCI_BASE_CLASS_MEMORY:
- return "Memory controller";
- break;
- case PCI_BASE_CLASS_BRIDGE:
- return "Bridge device";
- break;
- case PCI_BASE_CLASS_COMMUNICATION:
- return "Simple comm. controller";
- break;
- case PCI_BASE_CLASS_SYSTEM:
- return "Base system peripheral";
- break;
- case PCI_BASE_CLASS_INPUT:
- return "Input device";
- break;
- case PCI_BASE_CLASS_DOCKING:
- return "Docking station";
- break;
- case PCI_BASE_CLASS_PROCESSOR:
- return "Processor";
- break;
- case PCI_BASE_CLASS_SERIAL:
- return "Serial bus controller";
- break;
- case PCI_BASE_CLASS_INTELLIGENT:
- return "Intelligent controller";
- break;
- case PCI_BASE_CLASS_SATELLITE:
- return "Satellite controller";
- break;
- case PCI_BASE_CLASS_CRYPT:
- return "Cryptographic device";
- break;
- case PCI_BASE_CLASS_SIGNAL_PROCESSING:
- return "DSP";
- break;
- case PCI_CLASS_OTHERS:
- return "Does not fit any class";
- break;
- default:
- return "???";
- break;
- };
-}
-#endif /* CONFIG_CMD_PCI || CONFIG_PCI_SCAN_SHOW */
-
-__weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
-{
- /*
- * Check if pci device should be skipped in configuration
- */
- if (dev == PCI_BDF(hose->first_busno, 0, 0)) {
-#if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE) /* don't skip host bridge */
- /*
- * Only skip configuration if "pciconfighost" is not set
- */
- if (getenv("pciconfighost") == NULL)
- return 1;
-#else
- return 1;
-#endif
- }
-
- return 0;
-}
-
#ifdef CONFIG_PCI_SCAN_SHOW
__weak int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
{
diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c
index 378efbf..e8da977 100644
--- a/drivers/pci/pci_auto.c
+++ b/drivers/pci/pci_auto.c
@@ -432,13 +432,20 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
switch (class) {
case PCI_CLASS_BRIDGE_PCI:
- hose->current_busno++;
+ DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n",
+ PCI_DEV(dev));
+
pciauto_setup_device(hose, dev, 2, hose->pci_mem,
hose->pci_prefetch, hose->pci_io);
- DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_DEV(dev));
-
+#ifdef CONFIG_DM_PCI
+ n = dm_pci_hose_probe_bus(hose, dev);
+ if (n < 0)
+ return n;
+ sub_bus = (unsigned int)n;
+#else
/* Passing in current_busno allows for sibling P2P bridges */
+ hose->current_busno++;
pciauto_prescan_setup_bridge(hose, dev, hose->current_busno);
/*
* need to figure out if this is a subordinate bridge on the bus
@@ -451,6 +458,7 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
pciauto_postscan_setup_bridge(hose, dev, sub_bus);
sub_bus = hose->current_busno;
+#endif
break;
case PCI_CLASS_STORAGE_IDE:
@@ -475,7 +483,9 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n",
PCI_DEV(dev));
+#ifndef CONFIG_DM_PCI
hose->current_busno++;
+#endif
break;
#if defined(CONFIG_PCIAUTO_SKIP_HOST_BRIDGE)
diff --git a/drivers/pci/pci_common.c b/drivers/pci/pci_common.c
new file mode 100644
index 0000000..24c66bb
--- /dev/null
+++ b/drivers/pci/pci_common.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+ *
+ * (C) Copyright 2002, 2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <pci.h>
+#include <asm/io.h>
+
+const char *pci_class_str(u8 class)
+{
+ switch (class) {
+ case PCI_CLASS_NOT_DEFINED:
+ return "Build before PCI Rev2.0";
+ break;
+ case PCI_BASE_CLASS_STORAGE:
+ return "Mass storage controller";
+ break;
+ case PCI_BASE_CLASS_NETWORK:
+ return "Network controller";
+ break;
+ case PCI_BASE_CLASS_DISPLAY:
+ return "Display controller";
+ break;
+ case PCI_BASE_CLASS_MULTIMEDIA:
+ return "Multimedia device";
+ break;
+ case PCI_BASE_CLASS_MEMORY:
+ return "Memory controller";
+ break;
+ case PCI_BASE_CLASS_BRIDGE:
+ return "Bridge device";
+ break;
+ case PCI_BASE_CLASS_COMMUNICATION:
+ return "Simple comm. controller";
+ break;
+ case PCI_BASE_CLASS_SYSTEM:
+ return "Base system peripheral";
+ break;
+ case PCI_BASE_CLASS_INPUT:
+ return "Input device";
+ break;
+ case PCI_BASE_CLASS_DOCKING:
+ return "Docking station";
+ break;
+ case PCI_BASE_CLASS_PROCESSOR:
+ return "Processor";
+ break;
+ case PCI_BASE_CLASS_SERIAL:
+ return "Serial bus controller";
+ break;
+ case PCI_BASE_CLASS_INTELLIGENT:
+ return "Intelligent controller";
+ break;
+ case PCI_BASE_CLASS_SATELLITE:
+ return "Satellite controller";
+ break;
+ case PCI_BASE_CLASS_CRYPT:
+ return "Cryptographic device";
+ break;
+ case PCI_BASE_CLASS_SIGNAL_PROCESSING:
+ return "DSP";
+ break;
+ case PCI_CLASS_OTHERS:
+ return "Does not fit any class";
+ break;
+ default:
+ return "???";
+ break;
+ };
+}
+
+pci_dev_t pci_find_class(uint find_class, int index)
+{
+ int bus;
+ int devnum;
+ pci_dev_t bdf;
+ uint32_t class;
+
+ for (bus = 0; bus <= pci_last_busno(); bus++) {
+ for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES - 1; devnum++) {
+ pci_read_config_dword(PCI_BDF(bus, devnum, 0),
+ PCI_CLASS_REVISION, &class);
+ if (class >> 16 == 0xffff)
+ continue;
+
+ for (bdf = PCI_BDF(bus, devnum, 0);
+ bdf <= PCI_BDF(bus, devnum,
+ PCI_MAX_PCI_FUNCTIONS - 1);
+ bdf += PCI_BDF(0, 0, 1)) {
+ pci_read_config_dword(bdf, PCI_CLASS_REVISION,
+ &class);
+ class >>= 8;
+
+ if (class != find_class)
+ continue;
+ /*
+ * Decrement the index. We want to return the
+ * correct device, so index is 0 for the first
+ * matching device, 1 for the second, etc.
+ */
+ if (index) {
+ index--;
+ continue;
+ }
+ /* Return index'th controller. */
+ return bdf;
+ }
+ }
+ }
+
+ return -ENODEV;
+}
+
+__weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
+{
+ /*
+ * Check if pci device should be skipped in configuration
+ */
+ if (dev == PCI_BDF(hose->first_busno, 0, 0)) {
+#if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE) /* don't skip host bridge */
+ /*
+ * Only skip configuration if "pciconfighost" is not set
+ */
+ if (getenv("pciconfighost") == NULL)
+ return 1;
+#else
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+/* Get a virtual address associated with a BAR region */
+void *pci_map_bar(pci_dev_t pdev, int bar, int flags)
+{
+ pci_addr_t pci_bus_addr;
+ u32 bar_response;
+
+ /* read BAR address */
+ pci_read_config_dword(pdev, bar, &bar_response);
+ pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
+
+ /*
+ * Pass "0" as the length argument to pci_bus_to_virt. The arg
+ * isn't actualy used on any platform because u-boot assumes a static
+ * linear mapping. In the future, this could read the BAR size
+ * and pass that as the size if needed.
+ */
+ return pci_bus_to_virt(pdev, pci_bus_addr, flags, 0, MAP_NOCACHE);
+}
+
+void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum,
+ u32 addr_and_ctrl)
+{
+ int bar;
+
+ bar = PCI_BASE_ADDRESS_0 + barnum * 4;
+ pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl);
+}
+
+u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum)
+{
+ u32 addr;
+ int bar;
+
+ bar = PCI_BASE_ADDRESS_0 + barnum * 4;
+ pci_hose_read_config_dword(hose, dev, bar, &addr);
+ if (addr & PCI_BASE_ADDRESS_SPACE_IO)
+ return addr & PCI_BASE_ADDRESS_IO_MASK;
+ else
+ return addr & PCI_BASE_ADDRESS_MEM_MASK;
+}
+
+int __pci_hose_bus_to_phys(struct pci_controller *hose,
+ pci_addr_t bus_addr,
+ unsigned long flags,
+ unsigned long skip_mask,
+ phys_addr_t *pa)
+{
+ struct pci_region *res;
+ int i;
+
+ for (i = 0; i < hose->region_count; i++) {
+ res = &hose->regions[i];
+
+ if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
+ continue;
+
+ if (res->flags & skip_mask)
+ continue;
+
+ if (bus_addr >= res->bus_start &&
+ (bus_addr - res->bus_start) < res->size) {
+ *pa = (bus_addr - res->bus_start + res->phys_start);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+phys_addr_t pci_hose_bus_to_phys(struct pci_controller *hose,
+ pci_addr_t bus_addr,
+ unsigned long flags)
+{
+ phys_addr_t phys_addr = 0;
+ int ret;
+
+ if (!hose) {
+ puts("pci_hose_bus_to_phys: invalid hose\n");
+ return phys_addr;
+ }
+
+ /*
+ * if PCI_REGION_MEM is set we do a two pass search with preference
+ * on matches that don't have PCI_REGION_SYS_MEMORY set
+ */
+ if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {
+ ret = __pci_hose_bus_to_phys(hose, bus_addr,
+ flags, PCI_REGION_SYS_MEMORY, &phys_addr);
+ if (!ret)
+ return phys_addr;
+ }
+
+ ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr);
+
+ if (ret)
+ puts("pci_hose_bus_to_phys: invalid physical address\n");
+
+ return phys_addr;
+}
+
+pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)
+{
+ struct pci_device_id ids[2] = { {}, {0, 0} };
+
+ ids[0].vendor = vendor;
+ ids[0].device = device;
+
+ return pci_find_devices(ids, index);
+}
+
+pci_dev_t pci_hose_find_devices(struct pci_controller *hose, int busnum,
+ struct pci_device_id *ids, int *indexp)
+{
+ int found_multi = 0;
+ u16 vendor, device;
+ u8 header_type;
+ pci_dev_t bdf;
+ int i;
+
+ for (bdf = PCI_BDF(busnum, 0, 0);
+ bdf < PCI_BDF(busnum + 1, 0, 0);
+ bdf += PCI_BDF(0, 0, 1)) {
+ if (pci_skip_dev(hose, bdf))
+ continue;
+
+ if (!PCI_FUNC(bdf)) {
+ pci_read_config_byte(bdf, PCI_HEADER_TYPE,
+ &header_type);
+ found_multi = header_type & 0x80;
+ } else {
+ if (!found_multi)
+ continue;
+ }
+
+ pci_read_config_word(bdf, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word(bdf, PCI_DEVICE_ID, &device);
+
+ for (i = 0; ids[i].vendor != 0; i++) {
+ if (vendor == ids[i].vendor &&
+ device == ids[i].device) {
+ if ((*indexp) <= 0)
+ return bdf;
+
+ (*indexp)--;
+ }
+ }
+ }
+
+ return -1;
+}
diff --git a/drivers/pci/pci_compat.c b/drivers/pci/pci_compat.c
new file mode 100644
index 0000000..d6938c1
--- /dev/null
+++ b/drivers/pci/pci_compat.c
@@ -0,0 +1,43 @@
+/*
+ * Compatibility functions for pre-driver-model code
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#define DEBUG
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <pci.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+
+#define PCI_HOSE_OP(rw, name, size, type) \
+int pci_hose_##rw##_config_##name(struct pci_controller *hose, \
+ pci_dev_t dev, \
+ int offset, type value) \
+{ \
+ return pci_##rw##_config##size(dev, offset, value); \
+}
+
+PCI_HOSE_OP(read, byte, 8, u8 *)
+PCI_HOSE_OP(read, word, 16, u16 *)
+PCI_HOSE_OP(read, dword, 32, u32 *)
+PCI_HOSE_OP(write, byte, 8, u8)
+PCI_HOSE_OP(write, word, 16, u16)
+PCI_HOSE_OP(write, dword, 32, u32)
+
+pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
+{
+ struct pci_child_platdata *pplat;
+ struct udevice *bus, *dev;
+
+ if (pci_find_device_id(ids, index, &dev))
+ return -1;
+ bus = dev->parent;
+ pplat = dev_get_parent_platdata(dev);
+
+ return PCI_ADD_BUS(bus->seq, pplat->devfn);
+}
diff --git a/drivers/pci/pci_sandbox.c b/drivers/pci/pci_sandbox.c
new file mode 100644
index 0000000..6de5130
--- /dev/null
+++ b/drivers/pci/pci_sandbox.c
@@ -0,0 +1,79 @@
+/*
+ * 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 <inttypes.h>
+#include <pci.h>
+#include <dm/root.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+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;
+ int ret;
+
+ ret = sandbox_pci_get_emul(bus, devfn, &emul);
+ if (ret)
+ return ret == -ENODEV ? 0 : ret;
+ ops = pci_get_emul_ops(emul);
+ if (!ops || !ops->write_config)
+ return -ENOSYS;
+
+ return ops->write_config(emul, offset, value, size);
+}
+
+static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct dm_pci_emul_ops *ops;
+ struct udevice *emul;
+ 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;
+ ops = pci_get_emul_ops(emul);
+ if (!ops || !ops->read_config)
+ return -ENOSYS;
+
+ return ops->read_config(emul, offset, valuep, size);
+}
+
+static int sandbox_pci_child_post_bind(struct udevice *dev)
+{
+ /* Attach an emulator if we can */
+ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
+static const struct dm_pci_ops sandbox_pci_ops = {
+ .read_config = sandbox_pci_read_config,
+ .write_config = sandbox_pci_write_config,
+};
+
+static const struct udevice_id sandbox_pci_ids[] = {
+ { .compatible = "sandbox,pci" },
+ { }
+};
+
+U_BOOT_DRIVER(pci_sandbox) = {
+ .name = "pci_sandbox",
+ .id = UCLASS_PCI,
+ .of_match = sandbox_pci_ids,
+ .ops = &sandbox_pci_ops,
+ .child_post_bind = sandbox_pci_child_post_bind,
+ .per_child_platdata_auto_alloc_size =
+ sizeof(struct pci_child_platdata),
+};
diff --git a/drivers/pci/pci_x86.c b/drivers/pci/pci_x86.c
new file mode 100644
index 0000000..901bdca
--- /dev/null
+++ b/drivers/pci/pci_x86.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pci.h>
+
+static const struct dm_pci_ops x86_pci_ops = {
+};
+
+static const struct udevice_id x86_pci_ids[] = {
+ { .compatible = "x86,pci" },
+ { }
+};
+
+U_BOOT_DRIVER(pci_x86) = {
+ .name = "pci_x86",
+ .id = UCLASS_PCI,
+ .of_match = x86_pci_ids,
+ .ops = &x86_pci_ops,
+};
diff --git a/drivers/pci/pcie_layerscape.c b/drivers/pci/pcie_layerscape.c
index bcad8f2..402c519 100644
--- a/drivers/pci/pcie_layerscape.c
+++ b/drivers/pci/pcie_layerscape.c
@@ -11,7 +11,6 @@
#include <asm/io.h>
#include <errno.h>
#include <malloc.h>
-#include <asm/pcie_layerscape.h>
#ifndef CONFIG_SYS_PCI_MEMORY_BUS
#define CONFIG_SYS_PCI_MEMORY_BUS CONFIG_SYS_SDRAM_BASE
@@ -50,11 +49,20 @@
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
#define PCIE_ATU_UPPER_TARGET 0x91C
+/* LUT registers */
+#define PCIE_LUT_BASE 0x80000
+#define PCIE_LUT_DBG 0x7FC
+
+#define PCIE_DBI_RO_WR_EN 0x8bc
+
#define PCIE_LINK_CAP 0x7c
#define PCIE_LINK_SPEED_MASK 0xf
#define PCIE_LINK_STA 0x82
-#define PCIE_DBI_SIZE (4 * 1024) /* 4K */
+#define LTSSM_STATE_MASK 0x3f
+#define LTSSM_PCIE_L0 0x11 /* L0 state */
+
+#define PCIE_DBI_SIZE 0x100000 /* 1M */
struct ls_pcie {
int idx;
@@ -104,8 +112,6 @@ struct ls_pcie_info {
/* PEX1/2 Misc Ports Status Register */
#define LTSSM_STATE_SHIFT 20
-#define LTSSM_STATE_MASK 0x3f
-#define LTSSM_PCIE_L0 0x11 /* L0 state */
static int ls_pcie_link_state(struct ls_pcie *pcie)
{
@@ -122,18 +128,18 @@ static int ls_pcie_link_state(struct ls_pcie *pcie)
return 1;
}
#else
-#define PCIE_LDBG 0x7FC
-
static int ls_pcie_link_state(struct ls_pcie *pcie)
{
u32 state;
- state = readl(pcie->dbi + PCIE_LDBG);
- if (state)
- return 1;
+ state = readl(pcie->dbi + PCIE_LUT_BASE + PCIE_LUT_DBG) &
+ LTSSM_STATE_MASK;
+ if (state < LTSSM_PCIE_L0) {
+ debug("....PCIe link error. LTSSM=0x%02x.\n", state);
+ return 0;
+ }
- debug("....PCIe link error.\n");
- return 0;
+ return 1;
}
#endif
@@ -149,7 +155,11 @@ static int ls_pcie_link_up(struct ls_pcie *pcie)
/* Try to download speed to gen1 */
cap = readl(pcie->dbi + PCIE_LINK_CAP);
writel((cap & (~PCIE_LINK_SPEED_MASK)) | 1, pcie->dbi + PCIE_LINK_CAP);
- udelay(2000);
+ /*
+ * Notice: the following delay has critical impact on link training
+ * if too short (<30ms) the link doesn't get up.
+ */
+ mdelay(100);
state = ls_pcie_link_state(pcie);
if (state)
return state;
@@ -251,6 +261,10 @@ static int ls_pcie_addr_valid(struct pci_controller *hose, pci_dev_t d)
if (PCI_DEV(d) > 0)
return -EINVAL;
+ /* Controller does not support multi-function in RC mode */
+ if ((PCI_BUS(d) == hose->first_busno) && (PCI_FUNC(d) > 0))
+ return -EINVAL;
+
return 0;
}
@@ -327,8 +341,12 @@ static void ls_pcie_setup_ctrl(struct ls_pcie *pcie,
pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, 0);
/* program correct class for RC */
+ writel(1, pcie->dbi + PCIE_DBI_RO_WR_EN);
pci_hose_write_config_word(hose, dev, PCI_CLASS_DEVICE,
PCI_CLASS_BRIDGE_PCI);
+#ifndef CONFIG_LS102XA
+ writel(0, pcie->dbi + PCIE_DBI_RO_WR_EN);
+#endif
}
int ls_pcie_init_ctrl(int busno, enum srds_prtcl dev, struct ls_pcie_info *info)
@@ -417,9 +435,9 @@ int ls_pcie_init_ctrl(int busno, enum srds_prtcl dev, struct ls_pcie_info *info)
}
/* Print the negotiated PCIe link width */
- pci_hose_read_config_word(hose, dev, PCIE_LINK_STA, &temp16);
- printf("x%d gen%d, regs @ 0x%lx\n", (temp16 & 0x3f0) >> 4,
- (temp16 & 0xf), info->regs);
+ pci_hose_read_config_word(hose, pdev, PCIE_LINK_STA, &temp16);
+ printf("x%d gen%d, regs @ 0x%lx\n", (temp16 & 0x3f0) >> 4,
+ (temp16 & 0xf), info->regs);
if (ep_mode)
return busno;
@@ -486,7 +504,7 @@ static void ft_pcie_ls_setup(void *blob, const char *pci_compat,
fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
}
-void ft_pcie_setup(void *blob, bd_t *bd)
+void ft_pci_setup(void *blob, bd_t *bd)
{
#ifdef CONFIG_PCIE1
ft_pcie_ls_setup(blob, FSL_PCIE_COMPAT, CONFIG_SYS_PCIE1_ADDR, PCIE1);
@@ -506,7 +524,7 @@ void ft_pcie_setup(void *blob, bd_t *bd)
}
#else
-void ft_pcie_setup(void *blob, bd_t *bd)
+void ft_pci_setup(void *blob, bd_t *bd)
{
}
#endif