xref: /linux/drivers/net/ethernet/intel/libie/pci.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
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