Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PCI: dwc: Use multiple iATU windows for mapping large bridge windows and DMA ranges

The DWC driver tries to use a single iATU region for mapping the individual
entries of the bridge window and DMA range. If a bridge window/DMA range is
larger than the iATU inbound/outbound window size, then the mapping will
fail.

Hence, avoid this failure by using multiple iATU windows to map the whole
region. If the region runs out of iATU windows, then return failure.

Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Co-developed-by: Randolph Lin <randolph@andestech.com>
Signed-off-by: Randolph Lin <randolph@andestech.com>
[mani: reworded description, minor code cleanup]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Charles Mirabile <cmirabil@redhat.com>
Link: https://patch.msgid.link/20260109113430.2767264-1-randolph@andestech.com

authored by

Samuel Holland and committed by
Manivannan Sadhasivam
e9a5415a 86291f77

+57 -17
+57 -17
drivers/pci/controller/dwc/pcie-designware-host.c
··· 912 912 913 913 i = 0; 914 914 resource_list_for_each_entry(entry, &pp->bridge->windows) { 915 + resource_size_t res_size; 916 + 915 917 if (resource_type(entry->res) != IORESOURCE_MEM) 916 918 continue; 917 919 918 - if (pci->num_ob_windows <= ++i) 920 + if (pci->num_ob_windows <= i + 1) 919 921 break; 920 922 921 - atu.index = i; 922 923 atu.type = PCIE_ATU_TYPE_MEM; 923 924 atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset; 924 925 atu.pci_addr = entry->res->start - entry->offset; 925 926 926 927 /* Adjust iATU size if MSG TLP region was allocated before */ 927 928 if (pp->msg_res && pp->msg_res->parent == entry->res) 928 - atu.size = resource_size(entry->res) - 929 + res_size = resource_size(entry->res) - 929 930 resource_size(pp->msg_res); 930 931 else 931 - atu.size = resource_size(entry->res); 932 + res_size = resource_size(entry->res); 932 933 933 - ret = dw_pcie_prog_outbound_atu(pci, &atu); 934 - if (ret) { 935 - dev_err(pci->dev, "Failed to set MEM range %pr\n", 936 - entry->res); 937 - return ret; 934 + while (res_size > 0) { 935 + /* 936 + * Return failure if we run out of windows in the 937 + * middle. Otherwise, we would end up only partially 938 + * mapping a single resource. 939 + */ 940 + if (pci->num_ob_windows <= ++i) { 941 + dev_err(pci->dev, "Exhausted outbound windows for region: %pr\n", 942 + entry->res); 943 + return -ENOMEM; 944 + } 945 + 946 + atu.index = i; 947 + atu.size = MIN(pci->region_limit + 1, res_size); 948 + 949 + ret = dw_pcie_prog_outbound_atu(pci, &atu); 950 + if (ret) { 951 + dev_err(pci->dev, "Failed to set MEM range %pr\n", 952 + entry->res); 953 + return ret; 954 + } 955 + 956 + atu.parent_bus_addr += atu.size; 957 + atu.pci_addr += atu.size; 958 + res_size -= atu.size; 938 959 } 939 960 } 940 961 ··· 986 965 987 966 i = 0; 988 967 resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { 968 + resource_size_t res_start, res_size, window_size; 969 + 989 970 if (resource_type(entry->res) != IORESOURCE_MEM) 990 971 continue; 991 972 992 973 if (pci->num_ib_windows <= i) 993 974 break; 994 975 995 - ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, 996 - entry->res->start, 997 - entry->res->start - entry->offset, 998 - resource_size(entry->res)); 999 - if (ret) { 1000 - dev_err(pci->dev, "Failed to set DMA range %pr\n", 1001 - entry->res); 1002 - return ret; 976 + res_size = resource_size(entry->res); 977 + res_start = entry->res->start; 978 + while (res_size > 0) { 979 + /* 980 + * Return failure if we run out of windows in the 981 + * middle. Otherwise, we would end up only partially 982 + * mapping a single resource. 983 + */ 984 + if (pci->num_ib_windows <= i) { 985 + dev_err(pci->dev, "Exhausted inbound windows for region: %pr\n", 986 + entry->res); 987 + return -ENOMEM; 988 + } 989 + 990 + window_size = MIN(pci->region_limit + 1, res_size); 991 + ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, res_start, 992 + res_start - entry->offset, window_size); 993 + if (ret) { 994 + dev_err(pci->dev, "Failed to set DMA range %pr\n", 995 + entry->res); 996 + return ret; 997 + } 998 + 999 + res_start += window_size; 1000 + res_size -= window_size; 1003 1001 } 1004 1002 } 1005 1003