+// SPDX-License-Identifier: GPL-2.0+
/*
* 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 <inttypes.h>
#include <pci.h>
#include <asm/io.h>
#include <dm/device-internal.h>
}
}
+static void pci_dev_find_ofnode(struct udevice *bus, phys_addr_t bdf,
+ ofnode *rnode)
+{
+ struct fdt_pci_addr addr;
+ ofnode node;
+ int ret;
+
+ dev_for_each_subnode(node, bus) {
+ ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg",
+ &addr);
+ if (ret)
+ continue;
+
+ if (PCI_MASK_BUS(addr.phys_hi) != PCI_MASK_BUS(bdf))
+ continue;
+
+ *rnode = node;
+ break;
+ }
+};
+
int pci_bus_find_devfn(struct udevice *bus, pci_dev_t find_devfn,
struct udevice **devp)
{
pci_dev_t bdf, struct udevice **devp)
{
struct pci_driver_entry *start, *entry;
+ ofnode node = ofnode_null();
const char *drv;
int n_ents;
int ret;
debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
find_id->vendor, find_id->device);
+
+ /* Determine optional OF node */
+ pci_dev_find_ofnode(parent, bdf, &node);
+
start = ll_entry_start(struct pci_driver_entry, pci_driver_entry);
n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry);
for (entry = start; entry != start + n_ents; entry++) {
* find another driver. For now this doesn't seem
* necesssary, so just bind the first match.
*/
- ret = device_bind(parent, drv, drv->name, NULL, -1,
- &dev);
+ ret = device_bind_ofnode(parent, drv, drv->name, NULL,
+ node, &dev);
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;
}
return -ENOMEM;
drv = bridge ? "pci_bridge_drv" : "pci_generic_drv";
- ret = device_bind_driver(parent, drv, str, devp);
+ ret = device_bind_driver_to_node(parent, drv, str, node, devp);
if (ret) {
debug("%s: Failed to bind generic driver: %d\n", __func__, ret);
free(str);
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 */
- ret = pci_bus_read_config(bus, bdf, PCI_HEADER_TYPE,
- &header_type, PCI_SIZE_8);
+ ret = pci_bus_read_config(bus, bdf, PCI_VENDOR_ID, &vendor,
+ PCI_SIZE_16);
if (ret)
goto error;
- pci_bus_read_config(bus, bdf, PCI_VENDOR_ID, &vendor,
- PCI_SIZE_16);
+
if (vendor == 0xffff || vendor == 0x0000)
continue;
+ pci_bus_read_config(bus, bdf, PCI_HEADER_TYPE,
+ &header_type, PCI_SIZE_8);
+
if (!PCI_FUNC(bdf))
found_multi = header_type & 0x80;
return ret;
}
-static int decode_regions(struct pci_controller *hose, ofnode parent_node,
- ofnode node)
+static void decode_regions(struct pci_controller *hose, ofnode parent_node,
+ ofnode node)
{
int pci_addr_cells, addr_cells, size_cells;
int cells_per_record;
int i;
prop = ofnode_get_property(node, "ranges", &len);
- if (!prop)
- return -EINVAL;
+ if (!prop) {
+ debug("%s: Cannot decode regions\n", __func__);
+ return;
+ }
+
pci_addr_cells = ofnode_read_simple_addr_cells(node);
addr_cells = ofnode_read_simple_addr_cells(parent_node);
size_cells = ofnode_read_simple_size_cells(node);
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);
+ debug("%s: region %d, pci_addr=%llx, addr=%llx, size=%llx, 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 {
continue;
}
+
+ if (!IS_ENABLED(CONFIG_SYS_PCI_64BIT) &&
+ type == PCI_REGION_MEM && upper_32_bits(pci_addr)) {
+ debug(" - beyond the 32-bit boundary, ignoring\n");
+ continue;
+ }
+
pos = -1;
for (i = 0; i < hose->region_count; i++) {
if (hose->regions[i].flags == type)
#ifdef CONFIG_NR_DRAM_BANKS
bd_t *bd = gd->bd;
+ if (!bd)
+ return;
+
for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) {
if (bd->bi_dram[i].size) {
pci_set_region(hose->regions + hose->region_count++,
#endif
if (gd->pci_ram_top && gd->pci_ram_top < base + size)
size = gd->pci_ram_top - base;
- pci_set_region(hose->regions + hose->region_count++, base, base,
- size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+ if (size)
+ pci_set_region(hose->regions + hose->region_count++, base,
+ base, size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
#endif
- return 0;
+ return;
}
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);
/* For bridges, use the top-level PCI controller */
if (!device_is_on_pci_bus(bus)) {
hose->ctlr = bus;
- ret = decode_regions(hose, dev_ofnode(bus->parent),
- dev_ofnode(bus));
- if (ret) {
- debug("%s: Cannot decode regions\n", __func__);
- return ret;
- }
+ decode_regions(hose, dev_ofnode(bus->parent), dev_ofnode(bus));
} else {
struct pci_controller *parent_hose;
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;
}
struct pci_region *res;
int i;
+ if (hose->region_count == 0) {
+ *pa = bus_addr;
+ return 0;
+ }
+
for (i = 0; i < hose->region_count; i++) {
res = &hose->regions[i];
ctlr = pci_get_controller(dev);
hose = dev_get_uclass_priv(ctlr);
+ if (hose->region_count == 0) {
+ *ba = phys_addr;
+ return 0;
+ }
+
for (i = 0; i < hose->region_count; i++) {
res = &hose->regions[i];
return dm_pci_bus_to_virt(dev, pci_bus_addr, flags, 0, MAP_NOCACHE);
}
+static int _dm_pci_find_next_capability(struct udevice *dev, u8 pos, int cap)
+{
+ int ttl = PCI_FIND_CAP_TTL;
+ u8 id;
+ u16 ent;
+
+ 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_next_capability(struct udevice *dev, u8 start, int cap)
+{
+ return _dm_pci_find_next_capability(dev, start + PCI_CAP_LIST_NEXT,
+ cap);
+}
+
+int dm_pci_find_capability(struct udevice *dev, int cap)
+{
+ u16 status;
+ u8 header_type;
+ 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;
+
+ return _dm_pci_find_next_capability(dev, pos, cap);
+}
+
+int dm_pci_find_next_ext_capability(struct udevice *dev, int start, 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;
+
+ if (start)
+ pos = start;
+
+ 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;
+}
+
+int dm_pci_find_ext_capability(struct udevice *dev, int cap)
+{
+ return dm_pci_find_next_ext_capability(dev, 0, cap);
+}
+
UCLASS_DRIVER(pci) = {
.id = UCLASS_PCI,
.name = "pci",