blob: cff25076f752fbf8063252f6517df92fd16e207f [file] [log] [blame]
Patrick Rudolphd0c67972018-04-17 13:47:55 +02001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2018 Facebook, Inc.
5 * Copyright 2003-2017 Cavium Inc. <support@cavium.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
17 */
18
19#include <console/console.h>
20#include <arch/io.h>
21#include <device/pci.h>
22#include <device/pci_ops.h>
23#include <soc/addressmap.h>
24#include <soc/cavium/common/pci/chip.h>
25#include <assert.h>
26
27#define CAVM_PCCPF_XXX_VSEC_CTL 0x108
28#define CAVM_PCCPF_XXX_VSEC_SCTL 0x10c
29
30/*
31 * Hide PCI device function on BUS 1 in non secure world.
32 */
33static void disable_func(unsigned int devfn)
34{
35 u64 *addr;
36 printk(BIOS_DEBUG, "PCI: 01:%02x.%x is secure\n", devfn >> 3,
37 devfn & 7);
38
39 /* disable function */
40 addr = (void *)ECAM0_RSLX_SDIS;
41 u64 reg = read64(&addr[devfn]);
42 reg &= ~3;
43 reg |= 2;
44 write64(&addr[devfn], reg);
45}
46
47/*
48 * Show PCI device function on BUS 1 in non secure world.
49 */
50static void enable_func(unsigned int devfn)
51{
52 u64 *addr;
53
54 printk(BIOS_DEBUG, "PCI: 01:%02x.%x is insecure\n", devfn >> 3,
55 devfn & 7);
56
57 /* enable function */
58 addr = (void *)ECAM0_RSLX_SDIS;
59 u64 reg = read64(&addr[devfn]);
60 reg &= ~3;
61 write64(&addr[devfn], reg);
62
63 addr = (void *)ECAM0_RSLX_NSDIS;
64 reg = read64(&addr[devfn]);
65 reg &= ~1;
66 write64(&addr[devfn], reg);
67}
68
69/*
70 * Hide PCI device on BUS 0 in non secure world.
71 */
72static void disable_device(unsigned int dev)
73{
74 u64 *addr;
75
76 printk(BIOS_DEBUG, "PCI: 00:%02x.0 is secure\n", dev);
77
78 /* disable function */
79 addr = (void *)ECAM0_DEVX_SDIS;
80 u64 reg = read64(&addr[dev]);
81 reg &= ~3;
82 write64(&addr[dev], reg);
83
84 addr = (void *)ECAM0_DEVX_NSDIS;
85 reg = read64(&addr[dev]);
86 reg |= 1;
87 write64(&addr[dev], reg);
88}
89
90/*
91 * Show PCI device on BUS 0 in non secure world.
92 */
93static void enable_device(unsigned int dev)
94{
95 u64 *addr;
96
97 printk(BIOS_DEBUG, "PCI: 00:%02x.0 is insecure\n", dev);
98
99 /* enable function */
100 addr = (void *)ECAM0_DEVX_SDIS;
101 u64 reg = read64(&addr[dev]);
102 reg &= ~3;
103 write64(&addr[dev], reg);
104
105 addr = (void *)ECAM0_DEVX_NSDIS;
106 reg = read64(&addr[dev]);
107 reg &= ~1;
108 write64(&addr[dev], reg);
109}
110
111static void ecam0_read_resources(struct device *dev)
112{
113 /* There are no dynamic PCI resources on Cavium SoC */
114}
115
116static void ecam0_fix_missing_devices(struct bus *link)
117{
118 size_t i;
119
120 /**
121 * Cavium thinks it's a good idea to violate the PCI spec.
122 * Disabled multi-function PCI devices might have active functions.
123 * Add devices here manually, as coreboot's PCI allocator won't find
124 * them otherwise...
125 */
126 for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
127 struct device_path pci_path;
128 struct device *child;
129
130 pci_path.type = DEVICE_PATH_PCI;
131 pci_path.pci.devfn = i;
132
133 child = find_dev_path(link, &pci_path);
134 if (!child)
135 pci_probe_dev(NULL, link, i);
136 }
137}
138
139/**
140 * Get PCI BAR address from cavium specific extended capability.
141 * Use regular BAR if not found in extended capability space.
142 *
143 * @return The pyhsical address of the BAR, zero on error
144 */
145static uint64_t get_bar_val(struct device *dev, u8 bar)
146{
147 size_t cap_offset = pci_find_capability(dev, 0x14);
148 uint64_t h, l, ret = 0;
149 if (cap_offset) {
150 /* Found EA */
151 u8 es, bei;
152 u8 ne = pci_read_config8(dev, cap_offset + 2) & 0x3f;
153
154 cap_offset += 4;
155 while (ne) {
156 uint32_t dw0 = pci_read_config32(dev, cap_offset);
157
158 es = dw0 & 7;
159 bei = (dw0 >> 4) & 0xf;
160 if (bei == bar) {
161 h = 0;
162 l = pci_read_config32(dev, cap_offset + 4);
163 if (l & 2)
164 h = pci_read_config32(dev,
165 cap_offset + 12);
166 ret = (h << 32) | (l & ~0xfull);
167 break;
168 }
169 cap_offset += (es + 1) * 4;
170 ne--;
171 }
172 } else {
173 h = 0;
174 l = pci_read_config32(dev, bar * 4 + PCI_BASE_ADDRESS_0);
175 if (l & 4)
176 h = pci_read_config32(dev, bar * 4 + PCI_BASE_ADDRESS_0
177 + 4);
178 ret = (h << 32) | (l & ~0xfull);
179 }
180 return ret;
181}
182
183/**
184 * pci_enable_msix - configure device's MSI-X capability structure
185 * @dev: pointer to the pci_dev data structure of MSI-X device function
186 * @entries: pointer to an array of MSI-X entries
187 * @nvec: number of MSI-X irqs requested for allocation by device driver
188 *
189 * Setup the MSI-X capability structure of device function with the number
190 * of requested irqs upon its software driver call to request for
191 * MSI-X mode enabled on its hardware device function. A return of zero
192 * indicates the successful configuration of MSI-X capability structure.
193 * A return of < 0 indicates a failure.
194 * Or a return of > 0 indicates that driver request is exceeding the number
195 * of irqs or MSI-X vectors available. Driver should use the returned value to
196 * re-send its request.
197 **/
198static size_t ecam0_pci_enable_msix(struct device *dev,
199 struct msix_entry *entries, size_t nvec)
200{
201 struct msix_entry *msixtable;
202 u32 offset;
203 u8 bar_idx;
204 u64 bar;
205 size_t nr_entries;
206 size_t i;
207 u16 control;
208
209 if (!entries) {
210 printk(BIOS_ERR, "%s: No entries specified\n", __func__);
211 return -1;
212 }
213
214 const size_t pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
215 if (!pos) {
216 printk(BIOS_ERR, "%s: Device not MSI-X capable\n",
217 dev_path(dev));
218 return -1;
219 }
220 nr_entries = pci_msix_table_size(dev);
221 if (nvec > nr_entries) {
222 printk(BIOS_ERR, "ERROR: %s: Specified to many table entries\n",
223 dev_path(dev));
224 return nr_entries;
225 }
226
227 /* Ensure MSI-X is disabled while it is set up */
228 control = pci_read_config16(dev, pos + PCI_MSIX_FLAGS);
229 control &= ~PCI_MSIX_FLAGS_ENABLE;
230 pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
231
232 /* Find MSI-X table region */
233 offset = 0;
234 bar_idx = 0;
235 if (pci_msix_table_bar(dev, &offset, &bar_idx)) {
236 printk(BIOS_ERR, "ERROR: %s: Failed to find MSI-X entry\n",
237 dev_path(dev));
238 return -1;
239 }
240 bar = get_bar_val(dev, bar_idx);
241 if (!bar) {
242 printk(BIOS_ERR, "ERROR: %s: Failed to find MSI-X bar\n",
243 dev_path(dev));
244 return -1;
245 }
246 msixtable = (struct msix_entry *)((void *)bar + offset);
247
248 /*
249 * Some devices require MSI-X to be enabled before we can touch the
250 * MSI-X registers. We need to mask all the vectors to prevent
251 * interrupts coming in before they're fully set up.
252 */
253 control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE;
254 pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
255
256 for (i = 0; i < nvec; i++) {
257 write64(&msixtable[i].addr, entries[i].addr);
258 write32(&msixtable[i].data, entries[i].data);
259 write32(&msixtable[i].vec_control, entries[i].vec_control);
260 }
261
262 control &= ~PCI_MSIX_FLAGS_MASKALL;
263 pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
264
265 return 0;
266}
267
268static void ecam0_init(struct device *dev)
269{
270 struct soc_cavium_common_pci_config *config;
271 struct device *child, *child_last;
272 size_t i;
273 u32 reg32;
274
275 printk(BIOS_INFO, "ECAM0: init\n");
276 const struct device *bridge = dev_find_slot(0, PCI_DEVFN(1, 0));
277 if (!bridge) {
278 printk(BIOS_INFO, "ECAM0: ERROR: PCI 00:01.0 not found.\n");
279 return;
280 }
281 /**
282 * Search for missing devices on BUS 1.
283 * Only required for ARI capability programming.
284 */
285 ecam0_fix_missing_devices(bridge->link_list);
286
287 /* Program secure ARI capability on bus 1 */
288 child_last = NULL;
289 for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
290 child = dev_find_slot(bridge->link_list->secondary, i);
291 if (!child || !child->enabled)
292 continue;
293
294 if (child_last) {
295 /* Program ARI capability of the previous device */
296 reg32 = pci_read_config32(child_last,
297 CAVM_PCCPF_XXX_VSEC_SCTL);
298 reg32 &= ~(0xffU << 24);
299 reg32 |= child->path.pci.devfn << 24;
300 pci_write_config32(child_last, CAVM_PCCPF_XXX_VSEC_SCTL,
301 reg32);
302 }
303 child_last = child;
304 }
305
306 /* Program insecure ARI capability on bus 1 */
307 child_last = NULL;
308 for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
309 child = dev_find_slot(bridge->link_list->secondary, i);
310 if (!child)
311 continue;
312 config = child->chip_info;
313 if (!child->enabled || (config && config->secure))
314 continue;
315
316 if (child_last) {
317 /* Program ARI capability of the previous device */
318 reg32 = pci_read_config32(child_last,
319 CAVM_PCCPF_XXX_VSEC_CTL);
320 reg32 &= ~(0xffU << 24);
321 reg32 |= child->path.pci.devfn << 24;
322 pci_write_config32(child_last, CAVM_PCCPF_XXX_VSEC_CTL,
323 reg32);
324 }
325 child_last = child;
326 }
327
328 /* Enable / disable devices on bus 0 */
329 for (i = 0; i <= 0x1f; i++) {
330 child = dev_find_slot(0, PCI_DEVFN(i, 0));
331 config = child ? child->chip_info : NULL;
332 if (child && child->enabled && config && !config->secure)
333 enable_device(i);
334 else
335 disable_device(i);
336 }
337
338 /* Enable / disable devices and functions on bus 1 */
339 for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
340 child = dev_find_slot(bridge->link_list->secondary, i);
341 config = child ? child->chip_info : NULL;
342 if (child && child->enabled &&
343 ((config && !config->secure) || !config))
344 enable_func(i);
345 else
346 disable_func(i);
347 }
348
349 /* Apply IRQ on PCI devices */
350 /* UUA */
351 for (i = 0; i < 4; i++) {
352 child = dev_find_slot(bridge->link_list->secondary,
353 PCI_DEVFN(8, i));
354 if (!child)
355 continue;
356
357 struct msix_entry entry[2] = {
358 {.addr = CAVM_GICD_SETSPI_NSR, .data = 37 + i},
359 {.addr = CAVM_GICD_CLRSPI_NSR, .data = 37 + i},
360 };
361
362 ecam0_pci_enable_msix(child, entry, 2);
363 }
364
365 printk(BIOS_INFO, "ECAM0: done\n");
366}
367
368struct device_operations pci_domain_ops_ecam0 = {
369 .set_resources = NULL,
370 .enable_resources = NULL,
371 .read_resources = ecam0_read_resources,
372 .init = ecam0_init,
373 .scan_bus = pci_domain_scan_bus,
374};