1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (C) 2025 Intel Corporation */ 3 4 #include <linux/net/intel/libie/pci.h> 5 6 /** 7 * libie_find_mmio_region - find MMIO region containing a range 8 * @mmio_list: list that contains MMIO region info 9 * @offset: range start offset 10 * @size: range size 11 * @bar_idx: BAR index containing the range to search 12 * 13 * MMIO regions mappings are traversed from oldest to newest. 14 * 15 * Return: pointer to a MMIO region overlapping with the range in any way or 16 * NULL if no such region is mapped. 17 */ 18 static struct libie_pci_mmio_region * 19 libie_find_mmio_region(const struct list_head *mmio_list, 20 resource_size_t offset, resource_size_t size, 21 int bar_idx) 22 { 23 resource_size_t end_offset = offset + size; 24 struct libie_pci_mmio_region *mr; 25 26 list_for_each_entry(mr, mmio_list, list) { 27 resource_size_t mr_end = mr->offset + mr->size; 28 resource_size_t mr_start = mr->offset; 29 30 if (mr->bar_idx != bar_idx) 31 continue; 32 if (offset < mr_end && end_offset > mr_start) 33 return mr; 34 } 35 36 return NULL; 37 } 38 39 /** 40 * __libie_pci_get_mmio_addr - get the MMIO virtual address 41 * @mmio_info: contains list of MMIO regions 42 * @offset: register offset to find 43 * @num_args: number of additional arguments present 44 * @...: optional BAR index (0 by default) 45 * 46 * This function finds the virtual address of a register offset by iterating 47 * through the non-linear MMIO regions that are mapped by the driver. 48 * 49 * The list is traversed oldest to newest, so accessing an older mapping 50 * via this function while deleting a newer one is allowed. 51 * 52 * Return: valid MMIO virtual address or NULL. 53 */ 54 void __iomem *__libie_pci_get_mmio_addr(struct libie_mmio_info *mmio_info, 55 resource_size_t offset, 56 int num_args, ...) 57 { 58 struct libie_pci_mmio_region *mr; 59 int bar_idx = 0; 60 va_list args; 61 62 if (num_args) { 63 va_start(args, num_args); 64 bar_idx = va_arg(args, int); 65 va_end(args); 66 } 67 68 list_for_each_entry(mr, &mmio_info->mmio_list, list) 69 if (bar_idx == mr->bar_idx && offset >= mr->offset && 70 offset < mr->offset + mr->size) { 71 offset -= mr->offset; 72 73 return mr->addr + offset; 74 } 75 76 WARN_ONCE(true, "Access to an unmapped MMIO region (BAR%d, offset %pa)", 77 bar_idx, &offset); 78 79 return NULL; 80 } 81 EXPORT_SYMBOL_NS_GPL(__libie_pci_get_mmio_addr, "LIBIE_PCI"); 82 83 /** 84 * __libie_pci_map_mmio_region - map PCI device MMIO region 85 * @mmio_info: struct to store the mapped MMIO region 86 * @offset: MMIO region start offset 87 * @size: MMIO region size 88 * @num_args: number of additional arguments present 89 * @...: optional BAR index (0 by default) 90 * 91 * Return: true if the requested address range is accessible through 92 * new or existing mapping, false otherwise. 93 */ 94 bool __libie_pci_map_mmio_region(struct libie_mmio_info *mmio_info, 95 resource_size_t offset, 96 resource_size_t size, int num_args, ...) 97 { 98 struct pci_dev *pdev = mmio_info->pdev; 99 struct libie_pci_mmio_region *mr; 100 resource_size_t end_offset; 101 void __iomem *va; 102 int bar_idx = 0; 103 va_list args; 104 105 if (num_args) { 106 va_start(args, num_args); 107 bar_idx = va_arg(args, int); 108 va_end(args); 109 } 110 111 if (bar_idx >= PCI_STD_NUM_BARS || bar_idx < 0 || 112 !pci_resource_is_mem(pdev, bar_idx)) 113 return false; 114 115 /* pci_iomap_range() silently maps less 116 * if the requested length is too big 117 */ 118 if (!size || check_add_overflow(offset, size, &end_offset) || 119 end_offset > pci_resource_len(pdev, bar_idx)) 120 return false; 121 122 mr = libie_find_mmio_region(&mmio_info->mmio_list, offset, size, 123 bar_idx); 124 if (mr) { 125 pci_warn(pdev, 126 "Mapping of BAR%u (offset=%llu, size=%llu) intersecting region (offset=%llu, size=%llu) already exists\n", 127 bar_idx, (unsigned long long)mr->offset, 128 (unsigned long long)mr->size, 129 (unsigned long long)offset, (unsigned long long)size); 130 return mr->offset <= offset && 131 mr->offset + mr->size >= end_offset; 132 } 133 134 va = pci_iomap_range(mmio_info->pdev, bar_idx, offset, size); 135 if (!va) { 136 pci_err(pdev, "Failed to map BAR%u region\n", bar_idx); 137 return false; 138 } 139 140 mr = kvzalloc_obj(*mr); 141 if (!mr) { 142 pci_iounmap(pdev, va); 143 return false; 144 } 145 146 mr->addr = va; 147 mr->offset = offset; 148 mr->size = size; 149 mr->bar_idx = bar_idx; 150 151 list_add_tail(&mr->list, &mmio_info->mmio_list); 152 153 return true; 154 } 155 EXPORT_SYMBOL_NS_GPL(__libie_pci_map_mmio_region, "LIBIE_PCI"); 156 157 /** 158 * libie_pci_unmap_fltr_regs - unmap selected PCI device MMIO regions 159 * @mmio_info: contains list of MMIO regions to unmap 160 * @fltr: returns true, if region is to be unmapped 161 */ 162 void libie_pci_unmap_fltr_regs(struct libie_mmio_info *mmio_info, 163 bool (*fltr)(struct libie_mmio_info *mmio_info, 164 struct libie_pci_mmio_region *reg)) 165 { 166 struct libie_pci_mmio_region *mr, *tmp; 167 168 list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) { 169 if (!fltr(mmio_info, mr)) 170 continue; 171 list_del(&mr->list); 172 pci_iounmap(mmio_info->pdev, mr->addr); 173 kvfree(mr); 174 } 175 } 176 EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_fltr_regs, "LIBIE_PCI"); 177 178 /** 179 * libie_pci_unmap_all_mmio_regions - unmap all PCI device MMIO regions 180 * @mmio_info: contains list of MMIO regions to unmap 181 */ 182 void libie_pci_unmap_all_mmio_regions(struct libie_mmio_info *mmio_info) 183 { 184 struct libie_pci_mmio_region *mr, *tmp; 185 186 list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) { 187 list_del(&mr->list); 188 pci_iounmap(mmio_info->pdev, mr->addr); 189 kvfree(mr); 190 } 191 } 192 EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_all_mmio_regions, "LIBIE_PCI"); 193 194 /** 195 * libie_pci_init_dev - enable and configure the device 196 * @pdev: PCI device information 197 * 198 * Enable the device, request memory regions, set 64-bit DMA mask 199 * and coherent DMA mask, and enable bus-mastering 200 * 201 * Return: %0 on success, -%errno on failure. 202 */ 203 int libie_pci_init_dev(struct pci_dev *pdev) 204 { 205 int err; 206 207 err = pcim_enable_device(pdev); 208 if (err) 209 return err; 210 211 for (int bar = 0; bar < PCI_STD_NUM_BARS; bar++) 212 if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { 213 err = pcim_request_region(pdev, bar, pci_name(pdev)); 214 if (err) 215 return err; 216 } 217 218 err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 219 if (err) 220 return err; 221 222 pci_set_master(pdev); 223 224 return 0; 225 } 226 EXPORT_SYMBOL_NS_GPL(libie_pci_init_dev, "LIBIE_PCI"); 227 228 MODULE_DESCRIPTION("Common Ethernet PCI library"); 229 MODULE_LICENSE("GPL"); 230