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: ep: Support BAR subrange inbound mapping via Address Match Mode iATU

Extend dw_pcie_ep_set_bar() to support inbound mappings for BAR
subranges using Address Match Mode IB iATU when pci_epf_bar.num_submap
is non-zero.

Rename the existing BAR-match helper into dw_pcie_ep_ib_atu_bar() and
introduce dw_pcie_ep_ib_atu_addr() for Address Match Mode. When
num_submap is non-zero, read the assigned BAR base address and program
one inbound iATU window per subrange. Validate the submap array before
programming:
- each subrange is aligned to pci->region_align
- subranges cover the whole BAR (no gaps and no overlaps)

Track Address Match Mode mappings and tear them down on clear_bar() and
on set_bar() error paths to avoid leaving half-programmed state or
untranslated BAR holes.

Advertise this capability by extending the common feature bit
initializer macro (DWC_EPC_COMMON_FEATURES).

This enables multiple inbound windows within a single BAR, which is
useful on platforms where usable BARs are scarce but EPFs need multiple
inbound regions.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Link: https://patch.msgid.link/20260124145012.2794108-5-den@valinux.co.jp

authored by

Koichiro Den and committed by
Bjorn Helgaas
cc839bef c0f1506f

+209 -11
+203 -10
drivers/pci/controller/dwc/pcie-designware-ep.c
··· 100 100 return 0; 101 101 } 102 102 103 - static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, 104 - dma_addr_t parent_bus_addr, enum pci_barno bar, 105 - size_t size) 103 + /* BAR Match Mode inbound iATU mapping */ 104 + static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type, 105 + dma_addr_t parent_bus_addr, enum pci_barno bar, 106 + size_t size) 106 107 { 107 108 int ret; 108 109 u32 free_win; ··· 136 135 return 0; 137 136 } 138 137 138 + static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, enum pci_barno bar) 139 + { 140 + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); 141 + struct device *dev = pci->dev; 142 + unsigned int i, num; 143 + u32 atu_index; 144 + u32 *indexes; 145 + 146 + /* Tear down the BAR Match Mode mapping, if any. */ 147 + if (ep->bar_to_atu[bar]) { 148 + atu_index = ep->bar_to_atu[bar] - 1; 149 + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); 150 + clear_bit(atu_index, ep->ib_window_map); 151 + ep->bar_to_atu[bar] = 0; 152 + } 153 + 154 + /* Tear down all Address Match Mode mappings, if any. */ 155 + indexes = ep->ib_atu_indexes[bar]; 156 + num = ep->num_ib_atu_indexes[bar]; 157 + ep->ib_atu_indexes[bar] = NULL; 158 + ep->num_ib_atu_indexes[bar] = 0; 159 + if (!indexes) 160 + return; 161 + for (i = 0; i < num; i++) { 162 + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, indexes[i]); 163 + clear_bit(indexes[i], ep->ib_window_map); 164 + } 165 + devm_kfree(dev, indexes); 166 + } 167 + 168 + static u64 dw_pcie_ep_read_bar_assigned(struct dw_pcie_ep *ep, u8 func_no, 169 + enum pci_barno bar, int flags) 170 + { 171 + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); 172 + u32 lo, hi; 173 + u64 addr; 174 + 175 + lo = dw_pcie_ep_readl_dbi(ep, func_no, reg); 176 + 177 + if (flags & PCI_BASE_ADDRESS_SPACE) 178 + return lo & PCI_BASE_ADDRESS_IO_MASK; 179 + 180 + addr = lo & PCI_BASE_ADDRESS_MEM_MASK; 181 + if (!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) 182 + return addr; 183 + 184 + hi = dw_pcie_ep_readl_dbi(ep, func_no, reg + 4); 185 + return addr | ((u64)hi << 32); 186 + } 187 + 188 + static int dw_pcie_ep_validate_submap(struct dw_pcie_ep *ep, 189 + const struct pci_epf_bar_submap *submap, 190 + unsigned int num_submap, size_t bar_size) 191 + { 192 + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); 193 + u32 align = pci->region_align; 194 + size_t off = 0; 195 + unsigned int i; 196 + size_t size; 197 + 198 + if (!align || !IS_ALIGNED(bar_size, align)) 199 + return -EINVAL; 200 + 201 + /* 202 + * The submap array order defines the BAR layout (submap[0] starts 203 + * at offset 0 and each entry immediately follows the previous 204 + * one). Here, validate that it forms a strict, gapless 205 + * decomposition of the BAR: 206 + * - each entry has a non-zero size 207 + * - sizes, implicit offsets and phys_addr are aligned to 208 + * pci->region_align 209 + * - each entry lies within the BAR range 210 + * - the entries exactly cover the whole BAR 211 + * 212 + * Note: dw_pcie_prog_inbound_atu() also checks alignment for the 213 + * PCI address and the target phys_addr, but validating up-front 214 + * avoids partially programming iATU windows in vain. 215 + */ 216 + for (i = 0; i < num_submap; i++) { 217 + size = submap[i].size; 218 + 219 + if (!size) 220 + return -EINVAL; 221 + 222 + if (!IS_ALIGNED(size, align) || !IS_ALIGNED(off, align)) 223 + return -EINVAL; 224 + 225 + if (!IS_ALIGNED(submap[i].phys_addr, align)) 226 + return -EINVAL; 227 + 228 + if (off > bar_size || size > bar_size - off) 229 + return -EINVAL; 230 + 231 + off += size; 232 + } 233 + if (off != bar_size) 234 + return -EINVAL; 235 + 236 + return 0; 237 + } 238 + 239 + /* Address Match Mode inbound iATU mapping */ 240 + static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type, 241 + const struct pci_epf_bar *epf_bar) 242 + { 243 + const struct pci_epf_bar_submap *submap = epf_bar->submap; 244 + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); 245 + enum pci_barno bar = epf_bar->barno; 246 + struct device *dev = pci->dev; 247 + u64 pci_addr, parent_bus_addr; 248 + u64 size, base, off = 0; 249 + int free_win, ret; 250 + unsigned int i; 251 + u32 *indexes; 252 + 253 + if (!epf_bar->num_submap || !submap || !epf_bar->size) 254 + return -EINVAL; 255 + 256 + ret = dw_pcie_ep_validate_submap(ep, submap, epf_bar->num_submap, 257 + epf_bar->size); 258 + if (ret) 259 + return ret; 260 + 261 + base = dw_pcie_ep_read_bar_assigned(ep, func_no, bar, epf_bar->flags); 262 + if (!base) { 263 + dev_err(dev, 264 + "BAR%u not assigned, cannot set up sub-range mappings\n", 265 + bar); 266 + return -EINVAL; 267 + } 268 + 269 + indexes = devm_kcalloc(dev, epf_bar->num_submap, sizeof(*indexes), 270 + GFP_KERNEL); 271 + if (!indexes) 272 + return -ENOMEM; 273 + 274 + ep->ib_atu_indexes[bar] = indexes; 275 + ep->num_ib_atu_indexes[bar] = 0; 276 + 277 + for (i = 0; i < epf_bar->num_submap; i++) { 278 + size = submap[i].size; 279 + parent_bus_addr = submap[i].phys_addr; 280 + 281 + if (off > (~0ULL) - base) { 282 + ret = -EINVAL; 283 + goto err; 284 + } 285 + 286 + pci_addr = base + off; 287 + off += size; 288 + 289 + free_win = find_first_zero_bit(ep->ib_window_map, 290 + pci->num_ib_windows); 291 + if (free_win >= pci->num_ib_windows) { 292 + ret = -ENOSPC; 293 + goto err; 294 + } 295 + 296 + ret = dw_pcie_prog_inbound_atu(pci, free_win, type, 297 + parent_bus_addr, pci_addr, size); 298 + if (ret) 299 + goto err; 300 + 301 + set_bit(free_win, ep->ib_window_map); 302 + indexes[i] = free_win; 303 + ep->num_ib_atu_indexes[bar] = i + 1; 304 + } 305 + return 0; 306 + err: 307 + dw_pcie_ep_clear_ib_maps(ep, bar); 308 + return ret; 309 + } 310 + 139 311 static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, 140 312 struct dw_pcie_ob_atu_cfg *atu) 141 313 { ··· 339 165 struct dw_pcie_ep *ep = epc_get_drvdata(epc); 340 166 struct dw_pcie *pci = to_dw_pcie_from_ep(ep); 341 167 enum pci_barno bar = epf_bar->barno; 342 - u32 atu_index = ep->bar_to_atu[bar] - 1; 343 168 344 - if (!ep->bar_to_atu[bar]) 169 + if (!ep->epf_bar[bar]) 345 170 return; 346 171 347 172 __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags); 348 173 349 - dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); 350 - clear_bit(atu_index, ep->ib_window_map); 174 + dw_pcie_ep_clear_ib_maps(ep, bar); 175 + 351 176 ep->epf_bar[bar] = NULL; 352 - ep->bar_to_atu[bar] = 0; 353 177 } 354 178 355 179 static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci, ··· 504 332 return -EINVAL; 505 333 506 334 /* 335 + * When dynamically changing a BAR, tear down any existing 336 + * mappings before re-programming. 337 + */ 338 + if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap) 339 + dw_pcie_ep_clear_ib_maps(ep, bar); 340 + 341 + /* 507 342 * When dynamically changing a BAR, skip writing the BAR reg, as 508 343 * that would clear the BAR's PCI address assigned by the host. 509 344 */ 510 345 goto config_atu; 346 + } else { 347 + /* 348 + * Subrange mapping is an update-only operation. The BAR 349 + * must have been configured once without submaps so that 350 + * subsequent set_bar() calls can update inbound mappings 351 + * without touching the BAR register (and clobbering the 352 + * host-assigned address). 353 + */ 354 + if (epf_bar->num_submap) 355 + return -EINVAL; 511 356 } 512 357 513 358 bar_type = dw_pcie_ep_get_bar_type(ep, bar); ··· 558 369 else 559 370 type = PCIE_ATU_TYPE_IO; 560 371 561 - ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar, 562 - size); 372 + if (epf_bar->num_submap) 373 + ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar); 374 + else 375 + ret = dw_pcie_ep_ib_atu_bar(ep, func_no, type, 376 + epf_bar->phys_addr, bar, size); 377 + 563 378 if (ret) 564 379 return ret; 565 380
+6 -1
drivers/pci/controller/dwc/pcie-designware.h
··· 306 306 #define DMA_LLP_MEM_SIZE PAGE_SIZE 307 307 308 308 /* Common struct pci_epc_feature bits among DWC EP glue drivers */ 309 - #define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true 309 + #define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true, \ 310 + .subrange_mapping = true 310 311 311 312 struct dw_pcie; 312 313 struct dw_pcie_rp; ··· 487 486 void __iomem *msi_mem; 488 487 phys_addr_t msi_mem_phys; 489 488 struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; 489 + 490 + /* Only for Address Match Mode inbound iATU */ 491 + u32 *ib_atu_indexes[PCI_STD_NUM_BARS]; 492 + unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS]; 490 493 491 494 /* MSI outbound iATU state */ 492 495 bool msi_iatu_mapped;