xref: /aosp_15_r20/external/coreboot/src/soc/cavium/cn81xx/ecam0.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /*
4  * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
5  */
6 
7 #include <console/console.h>
8 #include <device/mmio.h>
9 #include <device/pci.h>
10 #include <device/pci_ops.h>
11 #include <soc/addressmap.h>
12 #include <soc/cavium/common/pci/chip.h>
13 #include <soc/ecam.h>
14 
15 #define CAVM_PCCPF_XXX_VSEC_CTL 0x108
16 #define CAVM_PCCPF_XXX_VSEC_SCTL 0x10c
17 
18 /*
19  * Hide PCI device function on BUS 1 in non secure world.
20  */
disable_func(unsigned int devfn)21 static void disable_func(unsigned int devfn)
22 {
23 	u64 *addr;
24 	printk(BIOS_DEBUG, "PCI: 01:%02x.%x is secure\n", devfn >> 3,
25 	       devfn & 7);
26 
27 	/* disable function */
28 	addr = (void *)ECAM0_RSLX_SDIS;
29 	u64 reg = read64(&addr[devfn]);
30 	reg &= ~3;
31 	reg |= 2;
32 	write64(&addr[devfn], reg);
33 }
34 
35 /*
36  * Show PCI device function on BUS 1 in non secure world.
37  */
enable_func(unsigned int devfn)38 static void enable_func(unsigned int devfn)
39 {
40 	u64 *addr;
41 
42 	printk(BIOS_DEBUG, "PCI: 01:%02x.%x is insecure\n", devfn >> 3,
43 	       devfn & 7);
44 
45 	/* enable function */
46 	addr = (void *)ECAM0_RSLX_SDIS;
47 	u64 reg = read64(&addr[devfn]);
48 	reg &= ~3;
49 	write64(&addr[devfn], reg);
50 
51 	addr = (void *)ECAM0_RSLX_NSDIS;
52 	reg = read64(&addr[devfn]);
53 	reg &= ~1;
54 	write64(&addr[devfn], reg);
55 }
56 
57 /*
58  * Hide PCI device on BUS 0 in non secure world.
59  */
disable_device(unsigned int dev)60 static void disable_device(unsigned int dev)
61 {
62 	u64 *addr;
63 
64 	printk(BIOS_DEBUG, "PCI: 00:%02x.0 is secure\n", dev);
65 
66 	/* disable function */
67 	addr = (void *)ECAM0_DEVX_SDIS;
68 	u64 reg = read64(&addr[dev]);
69 	reg &= ~3;
70 	write64(&addr[dev], reg);
71 
72 	addr = (void *)ECAM0_DEVX_NSDIS;
73 	reg = read64(&addr[dev]);
74 	reg |= 1;
75 	write64(&addr[dev], reg);
76 }
77 
78 /*
79  * Show PCI device on BUS 0 in non secure world.
80  */
enable_device(unsigned int dev)81 static void enable_device(unsigned int dev)
82 {
83 	u64 *addr;
84 
85 	printk(BIOS_DEBUG, "PCI: 00:%02x.0 is insecure\n", dev);
86 
87 	/* enable function */
88 	addr = (void *)ECAM0_DEVX_SDIS;
89 	u64 reg = read64(&addr[dev]);
90 	reg &= ~3;
91 	write64(&addr[dev], reg);
92 
93 	addr = (void *)ECAM0_DEVX_NSDIS;
94 	reg = read64(&addr[dev]);
95 	reg &= ~1;
96 	write64(&addr[dev], reg);
97 }
98 
ecam0_read_resources(struct device * dev)99 static void ecam0_read_resources(struct device *dev)
100 {
101 	/* There are no dynamic PCI resources on Cavium SoC */
102 }
103 
ecam0_fix_missing_devices(struct bus * link)104 static void ecam0_fix_missing_devices(struct bus *link)
105 {
106 	size_t i;
107 
108 	/**
109 	 * Cavium thinks it's a good idea to violate the PCI spec.
110 	 * Disabled multi-function PCI devices might have active functions.
111 	 * Add devices here manually, as coreboot's PCI allocator won't find
112 	 * them otherwise...
113 	 */
114 	for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
115 		struct device_path pci_path;
116 		struct device *child;
117 
118 		pci_path.type = DEVICE_PATH_PCI;
119 		pci_path.pci.devfn = i;
120 
121 		child = find_dev_path(link, &pci_path);
122 		if (!child)
123 			pci_probe_dev(NULL, link, i);
124 	}
125 }
126 
127 /**
128  * pci_enable_msix - configure device's MSI-X capability structure
129  * @dev: pointer to the pci_dev data structure of MSI-X device function
130  * @entries: pointer to an array of MSI-X entries
131  * @nvec: number of MSI-X irqs requested for allocation by device driver
132  *
133  * Setup the MSI-X capability structure of device function with the number
134  * of requested irqs upon its software driver call to request for
135  * MSI-X mode enabled on its hardware device function. A return of zero
136  * indicates the successful configuration of MSI-X capability structure.
137  * A return of < 0 indicates a failure.
138  * Or a return of > 0 indicates that driver request is exceeding the number
139  * of irqs or MSI-X vectors available. Driver should use the returned value to
140  * re-send its request.
141  **/
ecam0_pci_enable_msix(struct device * dev,struct msix_entry * entries,size_t nvec)142 static size_t ecam0_pci_enable_msix(struct device *dev,
143 				    struct msix_entry *entries, size_t nvec)
144 {
145 	struct msix_entry *msixtable;
146 	u32 offset;
147 	u8 bar_idx;
148 	u64 bar;
149 	size_t nr_entries;
150 	size_t i;
151 	u16 control;
152 
153 	if (!entries) {
154 		printk(BIOS_ERR, "%s: No entries specified\n", __func__);
155 		return -1;
156 	}
157 
158 	const size_t pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
159 	if (!pos) {
160 		printk(BIOS_ERR, "%s: Device not MSI-X capable\n",
161 		       dev_path(dev));
162 		return -1;
163 	}
164 	nr_entries = pci_msix_table_size(dev);
165 	if (nvec > nr_entries) {
166 		printk(BIOS_ERR, "%s: Specified to many table entries\n",
167 		       dev_path(dev));
168 		return nr_entries;
169 	}
170 
171 	/* Ensure MSI-X is disabled while it is set up */
172 	control = pci_read_config16(dev, pos + PCI_MSIX_FLAGS);
173 	control &= ~PCI_MSIX_FLAGS_ENABLE;
174 	pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
175 
176 	/* Find MSI-X table region */
177 	offset = 0;
178 	bar_idx = 0;
179 	if (pci_msix_table_bar(dev, &offset, &bar_idx)) {
180 		printk(BIOS_ERR, "%s: Failed to find MSI-X entry\n",
181 		       dev_path(dev));
182 		return -1;
183 	}
184 	bar = ecam0_get_bar_val(dev, bar_idx);
185 	if (!bar) {
186 		printk(BIOS_ERR, "%s: Failed to find MSI-X bar\n",
187 		       dev_path(dev));
188 		return -1;
189 	}
190 	msixtable = (struct msix_entry *)((void *)bar + offset);
191 
192 	/*
193 	 * Some devices require MSI-X to be enabled before we can touch the
194 	 * MSI-X registers.  We need to mask all the vectors to prevent
195 	 * interrupts coming in before they're fully set up.
196 	 */
197 	control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE;
198 	pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
199 
200 	for (i = 0; i < nvec; i++) {
201 		write64(&msixtable[i].addr, entries[i].addr);
202 		write32(&msixtable[i].data, entries[i].data);
203 		write32(&msixtable[i].vec_control, entries[i].vec_control);
204 	}
205 
206 	control &= ~PCI_MSIX_FLAGS_MASKALL;
207 	pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
208 
209 	return 0;
210 }
211 
ecam0_init(struct device * dev)212 static void ecam0_init(struct device *dev)
213 {
214 	struct soc_cavium_common_pci_config *config;
215 	struct device *child, *child_last;
216 	size_t i;
217 	u32 reg32;
218 
219 	printk(BIOS_INFO, "ECAM0: init\n");
220 	const struct device *bridge = pcidev_on_root(1, 0);
221 	if (!bridge) {
222 		printk(BIOS_INFO, "ECAM0: ERROR: PCI 00:01.0 not found.\n");
223 		return;
224 	}
225 	/**
226 	 * Search for missing devices on BUS 1.
227 	 * Only required for ARI capability programming.
228 	 */
229 	ecam0_fix_missing_devices(bridge->downstream);
230 
231 	/* Program secure ARI capability on bus 1 */
232 	child_last = NULL;
233 	for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
234 		child = pcidev_path_behind(bridge->downstream, i);
235 		if (!child || !child->enabled)
236 			continue;
237 
238 		if (child_last) {
239 			/* Program ARI capability of the previous device */
240 			reg32 = pci_read_config32(child_last,
241 						  CAVM_PCCPF_XXX_VSEC_SCTL);
242 			reg32 &= ~(0xffU << 24);
243 			reg32 |= child->path.pci.devfn << 24;
244 			pci_write_config32(child_last, CAVM_PCCPF_XXX_VSEC_SCTL,
245 					   reg32);
246 		}
247 		child_last = child;
248 	}
249 
250 	/* Program insecure ARI capability on bus 1 */
251 	child_last = NULL;
252 	for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
253 		child = pcidev_path_behind(bridge->downstream, i);
254 		if (!child)
255 			continue;
256 		config = child->chip_info;
257 		if (!child->enabled || (config && config->secure))
258 			continue;
259 
260 		if (child_last) {
261 			/* Program ARI capability of the previous device */
262 			reg32 = pci_read_config32(child_last,
263 						  CAVM_PCCPF_XXX_VSEC_CTL);
264 			reg32 &= ~(0xffU << 24);
265 			reg32 |= child->path.pci.devfn << 24;
266 			pci_write_config32(child_last, CAVM_PCCPF_XXX_VSEC_CTL,
267 					   reg32);
268 		}
269 		child_last = child;
270 	}
271 
272 	/* Enable / disable devices on bus 0 */
273 	for (i = 0; i <= 0x1f; i++) {
274 		child = pcidev_on_root(i, 0);
275 		config = child ? child->chip_info : NULL;
276 		if (child && child->enabled && config && !config->secure)
277 			enable_device(i);
278 		else
279 			disable_device(i);
280 	}
281 
282 	/* Enable / disable devices and functions on bus 1 */
283 	for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
284 		child = pcidev_path_behind(bridge->downstream, i);
285 		config = child ? child->chip_info : NULL;
286 		if (child && child->enabled &&
287 		    ((config && !config->secure) || !config))
288 			enable_func(i);
289 		else
290 			disable_func(i);
291 	}
292 
293 	/* Apply IRQ on PCI devices */
294 	/* UUA */
295 	for (i = 0; i < 4; i++) {
296 		child = pcidev_path_behind(bridge->downstream,
297 				      PCI_DEVFN(8, i));
298 		if (!child)
299 			continue;
300 
301 		struct msix_entry entry[2] = {
302 			{.addr = CAVM_GICD_SETSPI_NSR, .data = 37 + i},
303 			{.addr = CAVM_GICD_CLRSPI_NSR, .data = 37 + i},
304 		};
305 
306 		ecam0_pci_enable_msix(child, entry, 2);
307 	}
308 
309 	printk(BIOS_INFO, "ECAM0: done\n");
310 }
311 
312 struct device_operations pci_domain_ops_ecam0 = {
313 	.read_resources   = ecam0_read_resources,
314 	.init             = ecam0_init,
315 	.scan_bus         = pci_host_bridge_scan_bus,
316 };
317