xref: /linux/drivers/pci/rebar.c (revision a337869885083131e575c6367c679f4da4b68bb0)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * PCI Resizable BAR Extended Capability handling.
4  */
5 
6 #include <linux/bitfield.h>
7 #include <linux/errno.h>
8 #include <linux/export.h>
9 #include <linux/ioport.h>
10 #include <linux/log2.h>
11 #include <linux/pci.h>
12 #include <linux/sizes.h>
13 #include <linux/types.h>
14 
15 #include "pci.h"
16 
17 #define PCI_REBAR_MIN_SIZE	((resource_size_t)SZ_1M)
18 
19 /**
20  * pci_rebar_bytes_to_size - Convert size in bytes to PCI BAR Size
21  * @bytes: size in bytes
22  *
23  * Convert size in bytes to encoded BAR Size in Resizable BAR Capability
24  * (PCIe r6.2, sec. 7.8.6.3).
25  *
26  * Return: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB)
27  */
28 int pci_rebar_bytes_to_size(u64 bytes)
29 {
30 	int rebar_minsize = ilog2(PCI_REBAR_MIN_SIZE);
31 
32 	bytes = roundup_pow_of_two(bytes);
33 
34 	return max(ilog2(bytes), rebar_minsize) - rebar_minsize;
35 }
36 EXPORT_SYMBOL_GPL(pci_rebar_bytes_to_size);
37 
38 /**
39  * pci_rebar_size_to_bytes - Convert encoded BAR Size to size in bytes
40  * @size: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB)
41  *
42  * Return: BAR size in bytes
43  */
44 resource_size_t pci_rebar_size_to_bytes(int size)
45 {
46 	return 1ULL << (size + ilog2(PCI_REBAR_MIN_SIZE));
47 }
48 EXPORT_SYMBOL_GPL(pci_rebar_size_to_bytes);
49 
50 void pci_rebar_init(struct pci_dev *pdev)
51 {
52 	pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
53 }
54 
55 /**
56  * pci_rebar_find_pos - find position of resize ctrl reg for BAR
57  * @pdev: PCI device
58  * @bar: BAR to find
59  *
60  * Helper to find the position of the ctrl register for a BAR.
61  * Returns -ENOTSUPP if resizable BARs are not supported at all.
62  * Returns -ENOENT if no ctrl register for the BAR could be found.
63  */
64 static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
65 {
66 	unsigned int pos, nbars, i;
67 	u32 ctrl;
68 
69 	if (pci_resource_is_iov(bar)) {
70 		pos = pci_iov_vf_rebar_cap(pdev);
71 		bar = pci_resource_num_to_vf_bar(bar);
72 	} else {
73 		pos = pdev->rebar_cap;
74 	}
75 
76 	if (!pos)
77 		return -ENOTSUPP;
78 
79 	pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
80 	nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
81 
82 	for (i = 0; i < nbars; i++, pos += 8) {
83 		int bar_idx;
84 
85 		pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
86 		bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
87 		if (bar_idx == bar)
88 			return pos;
89 	}
90 
91 	return -ENOENT;
92 }
93 
94 /**
95  * pci_rebar_get_possible_sizes - get possible sizes for BAR
96  * @pdev: PCI device
97  * @bar: BAR to query
98  *
99  * Get the possible sizes of a resizable BAR as bitmask defined in the spec
100  * (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable.
101  */
102 u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
103 {
104 	int pos;
105 	u32 cap;
106 
107 	pos = pci_rebar_find_pos(pdev, bar);
108 	if (pos < 0)
109 		return 0;
110 
111 	pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
112 	cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
113 
114 	/* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
115 	if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
116 	    bar == 0 && cap == 0x700)
117 		return 0x3f00;
118 
119 	return cap;
120 }
121 EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
122 
123 /**
124  * pci_rebar_get_current_size - get the current size of a BAR
125  * @pdev: PCI device
126  * @bar: BAR to set size to
127  *
128  * Read the size of a BAR from the resizable BAR config.
129  * Returns size if found or negative error code.
130  */
131 int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
132 {
133 	int pos;
134 	u32 ctrl;
135 
136 	pos = pci_rebar_find_pos(pdev, bar);
137 	if (pos < 0)
138 		return pos;
139 
140 	pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
141 	return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
142 }
143 
144 /**
145  * pci_rebar_set_size - set a new size for a BAR
146  * @pdev: PCI device
147  * @bar: BAR to set size to
148  * @size: new size as defined in the spec (0=1MB, 31=128TB)
149  *
150  * Set the new size of a BAR as defined in the spec.
151  * Returns zero if resizing was successful, error code otherwise.
152  */
153 int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
154 {
155 	int pos;
156 	u32 ctrl;
157 
158 	pos = pci_rebar_find_pos(pdev, bar);
159 	if (pos < 0)
160 		return pos;
161 
162 	pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
163 	ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
164 	ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
165 	pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
166 
167 	if (pci_resource_is_iov(bar))
168 		pci_iov_resource_set_size(pdev, bar, size);
169 
170 	return 0;
171 }
172 
173 void pci_restore_rebar_state(struct pci_dev *pdev)
174 {
175 	unsigned int pos, nbars, i;
176 	u32 ctrl;
177 
178 	pos = pdev->rebar_cap;
179 	if (!pos)
180 		return;
181 
182 	pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
183 	nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
184 
185 	for (i = 0; i < nbars; i++, pos += 8) {
186 		struct resource *res;
187 		int bar_idx, size;
188 
189 		pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
190 		bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
191 		res = pci_resource_n(pdev, bar_idx);
192 		size = pci_rebar_bytes_to_size(resource_size(res));
193 		ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
194 		ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
195 		pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
196 	}
197 }
198 
199 static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
200 						  int resno)
201 {
202 	u16 cmd;
203 
204 	if (pci_resource_is_iov(resno))
205 		return pci_iov_is_memory_decoding_enabled(dev);
206 
207 	pci_read_config_word(dev, PCI_COMMAND, &cmd);
208 
209 	return cmd & PCI_COMMAND_MEMORY;
210 }
211 
212 void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size)
213 {
214 	resource_size_t res_size = pci_rebar_size_to_bytes(size);
215 	struct resource *res = pci_resource_n(dev, resno);
216 
217 	if (pci_resource_is_iov(resno))
218 		res_size *= pci_sriov_get_totalvfs(dev);
219 
220 	resource_set_size(res, res_size);
221 }
222 
223 /**
224  * pci_resize_resource - reconfigure a Resizable BAR and resources
225  * @dev: the PCI device
226  * @resno: index of the BAR to be resized
227  * @size: new size as defined in the spec (0=1MB, 31=128TB)
228  * @exclude_bars: a mask of BARs that should not be released
229  *
230  * Reconfigure @resno to @size and re-run resource assignment algorithm
231  * with the new size.
232  *
233  * Prior to resize, release @dev resources that share a bridge window with
234  * @resno.  This unpins the bridge window resource to allow changing it.
235  *
236  * The caller may prevent releasing a particular BAR by providing
237  * @exclude_bars mask, but this may result in the resize operation failing
238  * due to insufficient space.
239  *
240  * Return: 0 on success, or negative on error. In case of an error, the
241  *         resources are restored to their original places.
242  */
243 int pci_resize_resource(struct pci_dev *dev, int resno, int size,
244 			int exclude_bars)
245 {
246 	struct pci_host_bridge *host;
247 	int old, ret;
248 	u32 sizes;
249 
250 	/* Check if we must preserve the firmware's resource assignment */
251 	host = pci_find_host_bridge(dev->bus);
252 	if (host->preserve_config)
253 		return -ENOTSUPP;
254 
255 	if (pci_resize_is_memory_decoding_enabled(dev, resno))
256 		return -EBUSY;
257 
258 	sizes = pci_rebar_get_possible_sizes(dev, resno);
259 	if (!sizes)
260 		return -ENOTSUPP;
261 
262 	if (!(sizes & BIT(size)))
263 		return -EINVAL;
264 
265 	old = pci_rebar_get_current_size(dev, resno);
266 	if (old < 0)
267 		return old;
268 
269 	ret = pci_rebar_set_size(dev, resno, size);
270 	if (ret)
271 		return ret;
272 
273 	ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
274 	if (ret)
275 		goto error_resize;
276 	return 0;
277 
278 error_resize:
279 	pci_rebar_set_size(dev, resno, old);
280 	return ret;
281 }
282 EXPORT_SYMBOL(pci_resize_resource);
283