|
@@ -11,6 +11,7 @@
|
|
|
#include <fdtdec.h>
|
|
|
#include <inttypes.h>
|
|
|
#include <pci.h>
|
|
|
+#include <asm/io.h>
|
|
|
#include <dm/lists.h>
|
|
|
#include <dm/root.h>
|
|
|
#include <dm/device-internal.h>
|
|
@@ -1068,6 +1069,141 @@ u32 dm_pci_read_bar32(struct udevice *dev, int barnum)
|
|
|
return addr & PCI_BASE_ADDRESS_MEM_MASK;
|
|
|
}
|
|
|
|
|
|
+static int _dm_pci_bus_to_phys(struct udevice *ctlr,
|
|
|
+ pci_addr_t bus_addr, unsigned long flags,
|
|
|
+ unsigned long skip_mask, phys_addr_t *pa)
|
|
|
+{
|
|
|
+ struct pci_controller *hose = dev_get_uclass_priv(ctlr);
|
|
|
+ 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) {
|
|
|
+ *pa = (bus_addr - res->bus_start + res->phys_start);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+phys_addr_t dm_pci_bus_to_phys(struct udevice *dev, pci_addr_t bus_addr,
|
|
|
+ unsigned long flags)
|
|
|
+{
|
|
|
+ phys_addr_t phys_addr = 0;
|
|
|
+ struct udevice *ctlr;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* The root controller has the region information */
|
|
|
+ ctlr = pci_get_controller(dev);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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_TYPE) == PCI_REGION_MEM) {
|
|
|
+ ret = _dm_pci_bus_to_phys(ctlr, bus_addr,
|
|
|
+ flags, PCI_REGION_SYS_MEMORY,
|
|
|
+ &phys_addr);
|
|
|
+ if (!ret)
|
|
|
+ return phys_addr;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = _dm_pci_bus_to_phys(ctlr, bus_addr, flags, 0, &phys_addr);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ puts("pci_hose_bus_to_phys: invalid physical address\n");
|
|
|
+
|
|
|
+ return phys_addr;
|
|
|
+}
|
|
|
+
|
|
|
+int _dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
|
|
|
+ unsigned long flags, unsigned long skip_mask,
|
|
|
+ pci_addr_t *ba)
|
|
|
+{
|
|
|
+ struct pci_region *res;
|
|
|
+ struct udevice *ctlr;
|
|
|
+ pci_addr_t bus_addr;
|
|
|
+ int i;
|
|
|
+ struct pci_controller *hose;
|
|
|
+
|
|
|
+ /* The root controller has the region information */
|
|
|
+ ctlr = pci_get_controller(dev);
|
|
|
+ hose = dev_get_uclass_priv(ctlr);
|
|
|
+
|
|
|
+ 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) {
|
|
|
+ *ba = bus_addr;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
|
|
|
+ unsigned long flags)
|
|
|
+{
|
|
|
+ pci_addr_t bus_addr = 0;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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_TYPE) == PCI_REGION_MEM) {
|
|
|
+ ret = _dm_pci_phys_to_bus(dev, phys_addr, flags,
|
|
|
+ PCI_REGION_SYS_MEMORY, &bus_addr);
|
|
|
+ if (!ret)
|
|
|
+ return bus_addr;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = _dm_pci_phys_to_bus(dev, phys_addr, flags, 0, &bus_addr);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ puts("pci_hose_phys_to_bus: invalid physical address\n");
|
|
|
+
|
|
|
+ return bus_addr;
|
|
|
+}
|
|
|
+
|
|
|
+void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
|
|
|
+{
|
|
|
+ pci_addr_t pci_bus_addr;
|
|
|
+ u32 bar_response;
|
|
|
+
|
|
|
+ /* read BAR address */
|
|
|
+ dm_pci_read_config32(dev, 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 dm_pci_bus_to_virt(dev, pci_bus_addr, flags, 0, MAP_NOCACHE);
|
|
|
+}
|
|
|
+
|
|
|
UCLASS_DRIVER(pci) = {
|
|
|
.id = UCLASS_PCI,
|
|
|
.name = "pci",
|