| /* | 
|  * PCI auto-configuration library | 
|  * | 
|  * Author: Matt Porter <mporter@mvista.com> | 
|  * | 
|  * Copyright 2000 MontaVista Software Inc. | 
|  * | 
|  * Modifications for driver model: | 
|  * Copyright 2015 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 <pci.h> | 
|   | 
| void pciauto_region_init(struct pci_region *res) | 
| { | 
|     /* | 
|      * Avoid allocating PCI resources from address 0 -- this is illegal | 
|      * according to PCI 2.1 and moreover, this is known to cause Linux IDE | 
|      * drivers to fail. Use a reasonable starting value of 0x1000 instead. | 
|      */ | 
|     res->bus_lower = res->bus_start ? res->bus_start : 0x1000; | 
| } | 
|   | 
| void pciauto_region_align(struct pci_region *res, pci_size_t size) | 
| { | 
|     res->bus_lower = ((res->bus_lower - 1) | (size - 1)) + 1; | 
| } | 
|   | 
| int pciauto_region_allocate(struct pci_region *res, pci_size_t size, | 
|     pci_addr_t *bar) | 
| { | 
|     pci_addr_t addr; | 
|   | 
|     if (!res) { | 
|         debug("No resource"); | 
|         goto error; | 
|     } | 
|   | 
|     addr = ((res->bus_lower - 1) | (size - 1)) + 1; | 
|   | 
|     if (addr - res->bus_start + size > res->size) { | 
|         debug("No room in resource"); | 
|         goto error; | 
|     } | 
|   | 
|     res->bus_lower = addr + size; | 
|   | 
|     debug("address=0x%llx bus_lower=0x%llx", (unsigned long long)addr, | 
|           (unsigned long long)res->bus_lower); | 
|   | 
|     *bar = addr; | 
|     return 0; | 
|   | 
|  error: | 
|     *bar = (pci_addr_t)-1; | 
|     return -1; | 
| } | 
|   | 
| static void pciauto_show_region(const char *name, struct pci_region *region) | 
| { | 
|     pciauto_region_init(region); | 
|     debug("PCI Autoconfig: Bus %s region: [%llx-%llx],\n" | 
|           "\t\tPhysical Memory [%llx-%llxx]\n", name, | 
|           (unsigned long long)region->bus_start, | 
|           (unsigned long long)(region->bus_start + region->size - 1), | 
|           (unsigned long long)region->phys_start, | 
|           (unsigned long long)(region->phys_start + region->size - 1)); | 
| } | 
|   | 
| void pciauto_config_init(struct pci_controller *hose) | 
| { | 
|     int i; | 
|   | 
|     hose->pci_io = NULL; | 
|     hose->pci_mem = NULL; | 
|     hose->pci_prefetch = NULL; | 
|   | 
|     for (i = 0; i < hose->region_count; i++) { | 
|         switch (hose->regions[i].flags) { | 
|         case PCI_REGION_IO: | 
|             if (!hose->pci_io || | 
|                 hose->pci_io->size < hose->regions[i].size) | 
|                 hose->pci_io = hose->regions + i; | 
|             break; | 
|         case PCI_REGION_MEM: | 
|             if (!hose->pci_mem || | 
|                 hose->pci_mem->size < hose->regions[i].size) | 
|                 hose->pci_mem = hose->regions + i; | 
|             break; | 
|         case (PCI_REGION_MEM | PCI_REGION_PREFETCH): | 
|             if (!hose->pci_prefetch || | 
|                 hose->pci_prefetch->size < hose->regions[i].size) | 
|                 hose->pci_prefetch = hose->regions + i; | 
|             break; | 
|         } | 
|     } | 
|   | 
|   | 
|     if (hose->pci_mem) | 
|         pciauto_show_region("Memory", hose->pci_mem); | 
|     if (hose->pci_prefetch) | 
|         pciauto_show_region("Prefetchable Mem", hose->pci_prefetch); | 
|     if (hose->pci_io) | 
|         pciauto_show_region("I/O", hose->pci_io); | 
| } |