diff options
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 121 |
1 files changed, 97 insertions, 24 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e2b05d8991..fffca49fce 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -116,6 +116,25 @@ 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); +} + /* * */ @@ -218,67 +237,121 @@ pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) * */ -pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose, - phys_addr_t phys_addr, - unsigned long flags) +int __pci_hose_phys_to_bus (struct pci_controller *hose, + phys_addr_t phys_addr, + unsigned long flags, + unsigned long skip_mask, + pci_addr_t *ba) { struct pci_region *res; pci_addr_t bus_addr; int i; - if (!hose) { - printf ("pci_hose_phys_to_bus: %s\n", "invalid hose"); - goto Done; - } - 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; + bus_addr = phys_addr - res->phys_start + res->bus_start; if (bus_addr >= res->bus_start && bus_addr < res->bus_start + res->size) { - return bus_addr; + *ba = bus_addr; + return 0; } } - printf ("pci_hose_phys_to_bus: %s\n", "invalid physical address"); - -Done: - return 0; + return 1; } -phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose, - pci_addr_t bus_addr, - unsigned long flags) +pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose, + phys_addr_t phys_addr, + unsigned long flags) { - struct pci_region *res; - int i; + pci_addr_t bus_addr = 0; + int ret; if (!hose) { - printf ("pci_hose_bus_to_phys: %s\n", "invalid hose"); - goto Done; + puts ("pci_hose_phys_to_bus: invalid hose\n"); + return bus_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_phys_to_bus(hose, phys_addr, + flags, PCI_REGION_SYS_MEMORY, &bus_addr); + if (!ret) + return bus_addr; + } + + ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr); + + if (ret) + puts ("pci_hose_phys_to_bus: invalid physical address\n"); + + 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) { - return bus_addr - res->bus_start + res->phys_start; + *pa = (bus_addr - res->bus_start + res->phys_start); + return 0; } } - printf ("pci_hose_bus_to_phys: %s\n", "invalid physical address"); + return 1; +} -Done: - return 0; +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; } /* |