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