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 *
libie_find_mmio_region(const struct list_head * mmio_list,resource_size_t offset,resource_size_t size,int bar_idx)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 */
__libie_pci_get_mmio_addr(struct libie_mmio_info * mmio_info,resource_size_t offset,int num_args,...)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 */
__libie_pci_map_mmio_region(struct libie_mmio_info * mmio_info,resource_size_t offset,resource_size_t size,int num_args,...)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 */
libie_pci_unmap_fltr_regs(struct libie_mmio_info * mmio_info,bool (* fltr)(struct libie_mmio_info * mmio_info,struct libie_pci_mmio_region * reg))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 */
libie_pci_unmap_all_mmio_regions(struct libie_mmio_info * mmio_info)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 */
libie_pci_init_dev(struct pci_dev * pdev)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