X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fpci%2Fpci-uclass.c;h=2cf55cb743d71ba2532d3a41029011349167b14d;hb=9450ab2ba8d720bd9f73bccc0af2e2b5a2c2aaf1;hp=42230405412c6406c4563770952511f135f53b00;hpb=878d68c0c357ff62120d5783d950f34ecd1065d9;p=platform%2Fkernel%2Fu-boot.git diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 4223040..2cf55cb 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1,14 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2014 Google, Inc * Written by Simon Glass - * - * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include -#include #include #include #include @@ -92,6 +90,27 @@ int pci_get_ff(enum pci_size_t size) } } +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) { @@ -518,6 +537,64 @@ int pci_auto_config_devices(struct udevice *bus) return sub_bus; } +int pci_generic_mmap_write_config( + struct udevice *bus, + int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + pci_dev_t bdf, + uint offset, + ulong value, + enum pci_size_t size) +{ + void *address; + + if (addr_f(bus, bdf, offset, &address) < 0) + return 0; + + switch (size) { + case PCI_SIZE_8: + writeb(value, address); + return 0; + case PCI_SIZE_16: + writew(value, address); + return 0; + case PCI_SIZE_32: + writel(value, address); + return 0; + default: + return -EINVAL; + } +} + +int pci_generic_mmap_read_config( + struct udevice *bus, + int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + pci_dev_t bdf, + uint offset, + ulong *valuep, + enum pci_size_t size) +{ + void *address; + + if (addr_f(bus, bdf, offset, &address) < 0) { + *valuep = pci_get_ff(size); + return 0; + } + + switch (size) { + case PCI_SIZE_8: + *valuep = readb(address); + return 0; + case PCI_SIZE_16: + *valuep = readw(address); + return 0; + case PCI_SIZE_32: + *valuep = readl(address); + return 0; + default: + return -EINVAL; + } +} + int dm_pci_hose_probe_bus(struct udevice *bus) { int sub_bus; @@ -585,6 +662,7 @@ static int pci_find_and_bind_driver(struct udevice *parent, 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; @@ -595,6 +673,10 @@ static int pci_find_and_bind_driver(struct udevice *parent, 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++) { @@ -628,12 +710,12 @@ static int pci_find_and_bind_driver(struct udevice *parent, * 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; } @@ -656,7 +738,7 @@ static int pci_find_and_bind_driver(struct udevice *parent, 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); @@ -688,18 +770,23 @@ 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 */ - 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; @@ -753,19 +840,21 @@ error: 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; - phys_addr_t base = 0, size; int cells_per_record; const u32 *prop; int len; int i; - prop = ofnode_read_prop(node, "ranges", &len); - if (!prop) - return -EINVAL; + prop = ofnode_get_property(node, "ranges", &len); + 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); @@ -793,9 +882,8 @@ static int decode_regions(struct pci_controller *hose, ofnode parent_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; @@ -804,6 +892,13 @@ static int decode_regions(struct pci_controller *hose, ofnode parent_node, } 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) @@ -816,22 +911,41 @@ static int decode_regions(struct pci_controller *hose, ofnode parent_node, } /* Add a region for our local memory */ +#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++, + bd->bi_dram[i].start, + bd->bi_dram[i].start, + bd->bi_dram[i].size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + } + } +#else + phys_addr_t base = 0, size; + size = gd->ram_size; #ifdef CONFIG_SYS_SDRAM_BASE base = CONFIG_SYS_SDRAM_BASE; #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); @@ -840,12 +954,7 @@ static int pci_uclass_pre_probe(struct udevice *bus) /* 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; @@ -907,19 +1016,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; } @@ -1102,6 +1210,11 @@ static int _dm_pci_bus_to_phys(struct udevice *ctlr, 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]; @@ -1165,6 +1278,11 @@ int _dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, 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]; @@ -1229,6 +1347,95 @@ 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); } +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",