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

PCI: endpoint: Add support to specify alignment for buffers allocated to BARs

The address that is allocated using pci_epf_alloc_space() is
directly written to the target address of the Inbound Address
Translation unit (ie the HW component implementing inbound address
decoding) on endpoint controllers.

Designware IP [1] has a configuration parameter (CX_ATU_MIN_REGION_SIZE
[2]) which has 64KB as default value and the lower 16 bits of the Base,
Limit and Target registers of the Inbound ATU are fixed to zero. If the
programmed memory address is not aligned to 64 KB boundary this causes
memory corruption.

Modify pci_epf_alloc_space() API to take alignment size as argument in
order to allocate buffers to be mapped to BARs with an alignment that
suits the platform where they are used.

Add an 'align' parameter to epc_features which can be used by platform
drivers to specify the BAR allocation alignment requirements and use
this while invoking pci_epf_alloc_space().

[1] "I/O and MEM Match Modes" section in DesignWare Cores PCI Express
Controller Databook version 4.90a
[2] http://www.ti.com/lit/ug/spruid7c/spruid7c.pdf

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>

authored by

Kishon Vijay Abraham I and committed by
Lorenzo Pieralisi
2a9a8016 fbb2de89

+15 -5
+3 -2
drivers/pci/endpoint/functions/pci-epf-test.c
··· 438 438 epc_features = epf_test->epc_features; 439 439 440 440 base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg), 441 - test_reg_bar); 441 + test_reg_bar, epc_features->align); 442 442 if (!base) { 443 443 dev_err(dev, "Failed to allocated register space\n"); 444 444 return -ENOMEM; ··· 453 453 if (!!(epc_features->reserved_bar & (1 << bar))) 454 454 continue; 455 455 456 - base = pci_epf_alloc_space(epf, bar_size[bar], bar); 456 + base = pci_epf_alloc_space(epf, bar_size[bar], bar, 457 + epc_features->align); 457 458 if (!base) 458 459 dev_err(dev, "Failed to allocate space for BAR%d\n", 459 460 bar);
+8 -2
drivers/pci/endpoint/pci-epf-core.c
··· 109 109 * pci_epf_alloc_space() - allocate memory for the PCI EPF register space 110 110 * @size: the size of the memory that has to be allocated 111 111 * @bar: the BAR number corresponding to the allocated register space 112 + * @align: alignment size for the allocation region 112 113 * 113 114 * Invoke to allocate memory for the PCI EPF register space. 114 115 */ 115 - void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) 116 + void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, 117 + size_t align) 116 118 { 117 119 void *space; 118 120 struct device *dev = epf->epc->dev.parent; ··· 122 120 123 121 if (size < 128) 124 122 size = 128; 125 - size = roundup_pow_of_two(size); 123 + 124 + if (align) 125 + size = ALIGN(size, align); 126 + else 127 + size = roundup_pow_of_two(size); 126 128 127 129 space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL); 128 130 if (!space) {
+2
include/linux/pci-epc.h
··· 109 109 * @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver 110 110 * @bar_fixed_64bit: bitmap to indicate fixed 64bit BARs 111 111 * @bar_fixed_size: Array specifying the size supported by each BAR 112 + * @align: alignment size required for BAR buffer allocation 112 113 */ 113 114 struct pci_epc_features { 114 115 unsigned int linkup_notifier : 1; ··· 118 117 u8 reserved_bar; 119 118 u8 bar_fixed_64bit; 120 119 u64 bar_fixed_size[BAR_5 + 1]; 120 + size_t align; 121 121 }; 122 122 123 123 #define to_pci_epc(device) container_of((device), struct pci_epc, dev)
+2 -1
include/linux/pci-epf.h
··· 149 149 int __pci_epf_register_driver(struct pci_epf_driver *driver, 150 150 struct module *owner); 151 151 void pci_epf_unregister_driver(struct pci_epf_driver *driver); 152 - void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar); 152 + void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, 153 + size_t align); 153 154 void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar); 154 155 int pci_epf_bind(struct pci_epf *epf); 155 156 void pci_epf_unbind(struct pci_epf *epf);