X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fpci%2Fpci-uclass.c;h=9ab3539a49cae58c093d644c7a7f1cb93a275e1e;hb=c05ed00afb95fa5237f16962fccf5810437317bf;hp=0c52337f33a2315a435109638f09aa4783f2b3ab;hpb=1d6edcbfed2af33c748f2beb399810a0441888da;p=oweals%2Fu-boot.git diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 0c52337f33..9ab3539a49 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -14,6 +17,7 @@ #if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP) #include #endif +#include #include "pci_internal.h" DECLARE_GLOBAL_DATA_PTR; @@ -43,11 +47,24 @@ struct udevice *pci_get_controller(struct udevice *dev) return dev; } -pci_dev_t dm_pci_get_bdf(struct udevice *dev) +pci_dev_t dm_pci_get_bdf(const struct udevice *dev) { struct pci_child_platdata *pplat = dev_get_parent_platdata(dev); struct udevice *bus = dev->parent; + /* + * This error indicates that @dev is a device on an unprobed PCI bus. + * The bus likely has bus=seq == -1, so the PCI_ADD_BUS() macro below + * will produce a bad BDF> + * + * A common cause of this problem is that this function is called in the + * ofdata_to_platdata() method of @dev. Accessing the PCI bus in that + * method is not allowed, since it has not yet been probed. To fix this, + * move that access to the probe() method of @dev instead. + */ + if (!device_active(bus)) + log_err("PCI: Device '%s' on unprobed bus '%s'\n", dev->name, + bus->name); return PCI_ADD_BUS(bus->seq, pplat->devfn); } @@ -111,7 +128,7 @@ static void pci_dev_find_ofnode(struct udevice *bus, phys_addr_t bdf, } }; -int pci_bus_find_devfn(struct udevice *bus, pci_dev_t find_devfn, +int pci_bus_find_devfn(const struct udevice *bus, pci_dev_t find_devfn, struct udevice **devp) { struct udevice *dev; @@ -336,7 +353,7 @@ int dm_pci_write_config32(struct udevice *dev, int offset, u32 value) return dm_pci_write_config(dev, offset, value, PCI_SIZE_32); } -int pci_bus_read_config(struct udevice *bus, pci_dev_t bdf, int offset, +int pci_bus_read_config(const struct udevice *bus, pci_dev_t bdf, int offset, unsigned long *valuep, enum pci_size_t size) { struct dm_pci_ops *ops; @@ -360,10 +377,10 @@ int pci_read_config(pci_dev_t bdf, int offset, unsigned long *valuep, return pci_bus_read_config(bus, bdf, offset, valuep, size); } -int dm_pci_read_config(struct udevice *dev, int offset, unsigned long *valuep, - enum pci_size_t size) +int dm_pci_read_config(const struct udevice *dev, int offset, + unsigned long *valuep, enum pci_size_t size) { - struct udevice *bus; + const struct udevice *bus; for (bus = dev; device_is_on_pci_bus(bus);) bus = bus->parent; @@ -410,7 +427,7 @@ int pci_read_config8(pci_dev_t bdf, int offset, u8 *valuep) return 0; } -int dm_pci_read_config8(struct udevice *dev, int offset, u8 *valuep) +int dm_pci_read_config8(const struct udevice *dev, int offset, u8 *valuep) { unsigned long value; int ret; @@ -423,7 +440,7 @@ int dm_pci_read_config8(struct udevice *dev, int offset, u8 *valuep) return 0; } -int dm_pci_read_config16(struct udevice *dev, int offset, u16 *valuep) +int dm_pci_read_config16(const struct udevice *dev, int offset, u16 *valuep) { unsigned long value; int ret; @@ -436,7 +453,7 @@ int dm_pci_read_config16(struct udevice *dev, int offset, u16 *valuep) return 0; } -int dm_pci_read_config32(struct udevice *dev, int offset, u32 *valuep) +int dm_pci_read_config32(const struct udevice *dev, int offset, u32 *valuep) { unsigned long value; int ret; @@ -522,6 +539,8 @@ int pci_auto_config_devices(struct udevice *bus) int ret; debug("%s: device %s\n", __func__, dev->name); + if (dev_read_bool(dev, "pci,no-autoconfig")) + continue; ret = dm_pciauto_config_device(dev); if (ret < 0) return ret; @@ -538,8 +557,9 @@ int pci_auto_config_devices(struct udevice *bus) } int pci_generic_mmap_write_config( - struct udevice *bus, - int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + const struct udevice *bus, + int (*addr_f)(const struct udevice *bus, pci_dev_t bdf, uint offset, + void **addrp), pci_dev_t bdf, uint offset, ulong value, @@ -566,8 +586,9 @@ int pci_generic_mmap_write_config( } int pci_generic_mmap_read_config( - struct udevice *bus, - int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + const struct udevice *bus, + int (*addr_f)(const struct udevice *bus, pci_dev_t bdf, uint offset, + void **addrp), pci_dev_t bdf, uint offset, ulong *valuep, @@ -677,6 +698,11 @@ static int pci_find_and_bind_driver(struct udevice *parent, /* Determine optional OF node */ pci_dev_find_ofnode(parent, bdf, &node); + if (ofnode_valid(node) && !ofnode_is_available(node)) { + debug("%s: Ignoring disabled device\n", __func__); + return -EPERM; + } + 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++) { @@ -774,20 +800,23 @@ int pci_bind_bus_devices(struct udevice *bus) 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; - debug("%s: bus %d/%s: found device %x, function %d\n", __func__, + debug("%s: bus %d/%s: found device %x, function %d", __func__, bus->seq, bus->name, PCI_DEV(bdf), PCI_FUNC(bdf)); pci_bus_read_config(bus, bdf, PCI_DEVICE_ID, &device, PCI_SIZE_16); @@ -797,6 +826,7 @@ int pci_bind_bus_devices(struct udevice *bus) /* Find this device in the device tree */ ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), &dev); + debug(": find ret=%d\n", ret); /* If nothing in the device tree, bind a device */ if (ret == -ENODEV) { @@ -915,6 +945,11 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node, return; for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + if (hose->region_count == MAX_PCI_REGIONS) { + pr_err("maximum number of regions parsed, aborting\n"); + break; + } + if (bd->bi_dram[i].size) { pci_set_region(hose->regions + hose->region_count++, bd->bi_dram[i].start, @@ -961,12 +996,15 @@ static int pci_uclass_pre_probe(struct udevice *bus) hose->bus = bus; hose->first_busno = bus->seq; hose->last_busno = bus->seq; + hose->skip_auto_config_until_reloc = + dev_read_bool(bus, "u-boot,skip-auto-config-until-reloc"); return 0; } static int pci_uclass_post_probe(struct udevice *bus) { + struct pci_controller *hose = dev_get_uclass_priv(bus); int ret; debug("%s: probing bus %d\n", __func__, bus->seq); @@ -974,11 +1012,13 @@ static int pci_uclass_post_probe(struct udevice *bus) if (ret) return ret; -#ifdef CONFIG_PCI_PNP - ret = pci_auto_config_devices(bus); - if (ret < 0) - return ret; -#endif + if (CONFIG_IS_ENABLED(PCI_PNP) && ll_boot_init() && + (!hose->skip_auto_config_until_reloc || + (gd->flags & GD_FLG_RELOC))) { + ret = pci_auto_config_devices(bus); + if (ret < 0) + return log_msg_ret("pci auto-config", ret); + } #if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP) /* @@ -994,7 +1034,7 @@ static int pci_uclass_post_probe(struct udevice *bus) * Note we only call this 1) after U-Boot is relocated, and 2) * root bus has finished probing. */ - if ((gd->flags & GD_FLG_RELOC) && (bus->seq == 0)) { + if ((gd->flags & GD_FLG_RELOC) && bus->seq == 0 && ll_boot_init()) { ret = fsp_init_phase_pci(); if (ret) return ret; @@ -1007,8 +1047,6 @@ static int pci_uclass_post_probe(struct udevice *bus) 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_valid(dev)) return 0; @@ -1019,19 +1057,12 @@ static int pci_uclass_child_post_bind(struct udevice *dev) 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 { - pplat->devfn = addr.phys_hi & 0xff00; - } + pplat->devfn = pci_get_devfn(dev); return 0; } -static int pci_bridge_read_config(struct udevice *bus, pci_dev_t bdf, +static int pci_bridge_read_config(const struct udevice *bus, pci_dev_t bdf, uint offset, ulong *valuep, enum pci_size_t size) { @@ -1178,14 +1209,21 @@ int pci_get_regions(struct udevice *dev, struct pci_region **iop, return (*iop != NULL) + (*memp != NULL) + (*prefp != NULL); } -u32 dm_pci_read_bar32(struct udevice *dev, int barnum) +u32 dm_pci_read_bar32(const struct udevice *dev, int barnum) { u32 addr; int bar; bar = PCI_BASE_ADDRESS_0 + barnum * 4; dm_pci_read_config32(dev, bar, &addr); - if (addr & PCI_BASE_ADDRESS_SPACE_IO) + + /* + * If we get an invalid address, return this so that comparisons with + * FDT_ADDR_T_NONE work correctly + */ + if (addr == 0xffffffff) + return addr; + else if (addr & PCI_BASE_ADDRESS_SPACE_IO) return addr & PCI_BASE_ADDRESS_IO_MASK; else return addr & PCI_BASE_ADDRESS_MEM_MASK; @@ -1326,10 +1364,56 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, return bus_addr; } +static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags, + int ea_off) +{ + int ea_cnt, i, entry_size; + int bar_id = (bar - PCI_BASE_ADDRESS_0) >> 2; + u32 ea_entry; + phys_addr_t addr; + + /* EA capability structure header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + ea_cnt = (ea_entry >> 16) & PCI_EA_NUM_ENT_MASK; + ea_off += PCI_EA_FIRST_ENT; + + for (i = 0; i < ea_cnt; i++, ea_off += entry_size) { + /* Entry header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + entry_size = ((ea_entry & PCI_EA_ES) + 1) << 2; + + if (((ea_entry & PCI_EA_BEI) >> 4) != bar_id) + continue; + + /* Base address, 1st DW */ + dm_pci_read_config32(dev, ea_off + 4, &ea_entry); + addr = ea_entry & PCI_EA_FIELD_MASK; + if (ea_entry & PCI_EA_IS_64) { + /* Base address, 2nd DW, skip over 4B MaxOffset */ + dm_pci_read_config32(dev, ea_off + 12, &ea_entry); + addr |= ((u64)ea_entry) << 32; + } + + /* size ignored for now */ + return map_physmem(addr, flags, 0); + } + + return 0; +} + void *dm_pci_map_bar(struct udevice *dev, int bar, int flags) { pci_addr_t pci_bus_addr; u32 bar_response; + int ea_off; + + /* + * if the function supports Enhanced Allocation use that instead of + * BARs + */ + ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA); + if (ea_off) + return dm_pci_map_ea_bar(dev, bar, flags, ea_off); /* read BAR address */ dm_pci_read_config32(dev, bar, &bar_response); @@ -1433,6 +1517,30 @@ int dm_pci_find_ext_capability(struct udevice *dev, int cap) return dm_pci_find_next_ext_capability(dev, 0, cap); } +int dm_pci_flr(struct udevice *dev) +{ + int pcie_off; + u32 cap; + + /* look for PCI Express Capability */ + pcie_off = dm_pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pcie_off) + return -ENOENT; + + /* check FLR capability */ + dm_pci_read_config32(dev, pcie_off + PCI_EXP_DEVCAP, &cap); + if (!(cap & PCI_EXP_DEVCAP_FLR)) + return -ENOENT; + + dm_pci_clrset_config16(dev, pcie_off + PCI_EXP_DEVCTL, 0, + PCI_EXP_DEVCTL_BCR_FLR); + + /* wait 100ms, per PCI spec */ + mdelay(100); + + return 0; +} + UCLASS_DRIVER(pci) = { .id = UCLASS_PCI, .name = "pci", @@ -1487,9 +1595,9 @@ void pci_init(void) * Enumerate all known controller devices. Enumeration has the side- * effect of probing them, so PCIe devices will be enumerated too. */ - for (uclass_first_device(UCLASS_PCI, &bus); + for (uclass_first_device_check(UCLASS_PCI, &bus); bus; - uclass_next_device(&bus)) { + uclass_next_device_check(&bus)) { ; } }