1 /***********************license start***********************************
2 * Copyright (c) 2003-2017 Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * * Neither the name of Cavium Inc. nor the names of
19 * its contributors may be used to endorse or promote products
20 * derived from this software without specific prior written
21 * permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export
24 * control laws, including the U.S. Export Administration Act and its
25 * associated regulations, and may be subject to export or import
26 * regulations in other countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
31 * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
32 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
33 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
34 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
35 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
36 * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK
37 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39 #include <bdk.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include "libbdk-arch/bdk-csrs-ap.h"
44 #include "libbdk-arch/bdk-csrs-pccpf.h"
45 #include "libbdk-hal/bdk-ecam.h"
46 #include "libbdk-hal/device/bdk-device.h"
47 #include "libbdk-hal/bdk-config.h"
48 #include "libbdk-driver/bdk-driver.h"
49 #include "libbdk-hal/bdk-utils.h"
50
51 static struct bdk_driver_s *driver_list = NULL;
52
53 #define DEVICE_GROW 64
54 static bdk_device_t *device_list = NULL;
55 static int device_list_count = 0;
56 static int device_list_max = 0;
57
58 /**
59 * Called to register a new driver with the bdk-device system. Drivers are probed
60 * and initialized as device are found for them. If devices have already been
61 * added before the driver was registered, the driver will be probed and
62 * initialized before this function returns.
63 *
64 * @param driver Driver functions
65 *
66 * @return Zero on success, negative on failure
67 */
bdk_device_add_driver(struct bdk_driver_s * driver)68 int bdk_device_add_driver(struct bdk_driver_s *driver)
69 {
70 driver->next = driver_list;
71 driver_list = driver;
72 BDK_TRACE(DEVICE, "Added driver for %08x\n", driver->id);
73 return 0;
74 }
75
76 /**
77 * Lookup the correct driver for a device
78 *
79 * @param device Device to lookup
80 *
81 * @return Driver, or NULL on failure
82 */
lookup_driver(const bdk_device_t * device)83 static const bdk_driver_t *lookup_driver(const bdk_device_t *device)
84 {
85 const bdk_driver_t *drv = driver_list;
86 while (drv)
87 {
88 if (drv->id == device->id)
89 return drv;
90 drv = drv->next;
91 }
92 return NULL;
93 }
94
95 /**
96 * Populate the fields of a new device from the ECAM
97 *
98 * @param device Device to populate
99 */
populate_device(bdk_device_t * device)100 static void populate_device(bdk_device_t *device)
101 {
102 /* The default name may be replaced by the driver with something easier to read */
103 snprintf(device->name, sizeof(device->name), "N%d.E%d:%d:%d.%d",
104 device->node, device->ecam, device->bus, device->dev, device->func);
105
106 BDK_TRACE(DEVICE_SCAN, "%s: Populating device\n", device->name);
107
108 /* Get the current chip ID and pass. We'll need this to fill in version
109 information for the device */
110 bdk_ap_midr_el1_t midr_el1;
111 BDK_MRS(MIDR_EL1, midr_el1.u);
112
113 /* PCCPF_XXX_VSEC_SCTL[RID] with the revision of the chip,
114 read from fuses */
115 BDK_CSR_DEFINE(sctl, BDK_PCCPF_XXX_VSEC_SCTL);
116 sctl.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_VSEC_SCTL);
117 sctl.s.rid = midr_el1.s.revision | (midr_el1.s.variant<<3);
118 sctl.s.node = device->node; /* Program node bits */
119 sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
120 if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
121 sctl.s.ea = 0; /* EA is not supported on CN88XX pass 1.x */
122 else
123 sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
124 bdk_ecam_write32(device, BDK_PCCPF_XXX_VSEC_SCTL, sctl.u);
125
126 /* Read the Device ID */
127 device->id = bdk_ecam_read32(device, BDK_PCCPF_XXX_ID);
128
129 /* Read the Device Type so we know how to handle BARs */
130 bdk_pccpf_xxx_clsize_t clsize;
131 clsize.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CLSIZE);
132 int isbridge = (clsize.s.hdrtype & 0x7f) == 1;
133
134 BDK_TRACE(DEVICE_SCAN, "%s: Device ID: 0x%08x%s\n", device->name, device->id,
135 (isbridge) ? " (Bridge)" : "");
136
137 /* Loop through all the BARs */
138 int max_bar = (isbridge) ? BDK_PCCPF_XXX_BAR0U : BDK_PCCPF_XXX_BAR4U;
139 int bar = BDK_PCCPF_XXX_BAR0L;
140 unsigned guess_instance = 0;
141 while (bar <= max_bar)
142 {
143 int bar_index = (bar - BDK_PCCPF_XXX_BAR0L) / 8;
144 /* Read the BAR address and config bits [3:0] */
145 uint64_t address = bdk_ecam_read32(device, bar);
146 int ismem = !(address & 1); /* Bit 0: 0 = mem, 1 = io */
147 int is64 = ismem && (address & 4); /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
148 /* Bit 3: 1 = Is prefetchable. We on't care for now */
149
150 /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
151 it is using Enhanced Allocation (EA) */
152 if (!is64)
153 {
154 BDK_TRACE(DEVICE_SCAN, "%s: BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
155 bar += 8;
156 continue;
157 }
158
159 /* Get the upper part of 64bit BARs */
160 address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
161
162 /* Write the bits to determine the size */
163 bdk_ecam_write32(device, bar, -1);
164 bdk_ecam_write32(device, bar + 4, -1);
165 uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
166 size_mask |= bdk_ecam_read32(device, bar);
167 /* Make sure the node bits are correct in the address */
168 address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
169 /* Restore address value */
170 bdk_ecam_write32(device, bar, address);
171 bdk_ecam_write32(device, bar + 4, address >> 32);
172
173 /* Convert the size into a power of 2 bits */
174 int size_bits = bdk_dpop(~size_mask | 0xf);
175 if (size_bits <= 4)
176 size_bits = 0;
177
178 /* Store the BAR info */
179 device->bar[bar_index].address = address & ~0xfull;
180 device->bar[bar_index].size2 = size_bits;
181 device->bar[bar_index].flags = address & 0xf;
182 BDK_TRACE(DEVICE_SCAN, "%s: BAR%d 0x%llx/%d flags=0x%x\n",
183 device->name, bar_index, device->bar[bar_index].address,
184 device->bar[bar_index].size2, device->bar[bar_index].flags);
185 /* Move to the next BAR */
186 bar += 8;
187 }
188
189 /* Walk the PCI capabilities looking for PCIe support and EA headers */
190 BDK_TRACE(DEVICE_SCAN, "%s: Walking PCI capabilites\n", device->name);
191 int has_pcie = 0;
192 bdk_pccpf_xxx_cap_ptr_t cap_ptr;
193 cap_ptr.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CAP_PTR);
194 int cap_loc = cap_ptr.s.cp;
195 while (cap_loc)
196 {
197 uint32_t cap = bdk_ecam_read32(device, cap_loc);
198 int cap_id = cap & 0xff;
199 int cap_next = (cap >> 8) & 0xff;
200
201 BDK_TRACE(DEVICE_SCAN, "%s: PCI Capability 0x%02x ID:0x%02x Next:0x%02x\n",
202 device->name, cap_loc, cap_id, cap_next);
203
204 if (cap_id == 0x10)
205 {
206 BDK_TRACE(DEVICE_SCAN, "%s: PCIe\n", device->name);
207 has_pcie = 1;
208 }
209 else if (cap_id == 0x01)
210 {
211 BDK_TRACE(DEVICE_SCAN, "%s: PCI Power Management Interface\n", device->name);
212 /* Do nothing for now */
213 }
214 else if (cap_id == 0x11)
215 {
216 bdk_pccpf_xxx_msix_cap_hdr_t msix_cap_hdr;
217 bdk_pccpf_xxx_msix_table_t msix_table;
218 bdk_pccpf_xxx_msix_pba_t msix_pba;
219 msix_cap_hdr.u = cap;
220 msix_table.u = bdk_ecam_read32(device, cap_loc + 4);
221 msix_pba.u = bdk_ecam_read32(device, cap_loc + 8);
222 BDK_TRACE(DEVICE_SCAN, "%s: MSI-X Entries:%d, Func Mask:%d, Enable:%d\n",
223 device->name, msix_cap_hdr.s.msixts + 1, msix_cap_hdr.s.funm, msix_cap_hdr.s.msixen);
224 BDK_TRACE(DEVICE_SCAN, "%s: Table BAR%d, Offset:0x%x\n",
225 device->name, msix_table.s.msixtbir, msix_table.s.msixtoffs * 8);
226 BDK_TRACE(DEVICE_SCAN, "%s: PBA BAR%d, Offset:0x%x\n",
227 device->name, msix_pba.s.msixpbir, msix_pba.s.msixpoffs * 8);
228 }
229 else if (cap_id == 0x05)
230 {
231 BDK_TRACE(DEVICE_SCAN, "%s: MSI\n", device->name);
232 /* Do nothing for now */
233 }
234 else if (cap_id == 0x14)
235 {
236 bdk_pccpf_xxx_ea_cap_hdr_t ea_cap_hdr;
237 ea_cap_hdr.u = cap;
238 cap_loc += 4;
239 BDK_TRACE(DEVICE_SCAN, "%s: Enhanced Allocation, %d entries\n",
240 device->name, ea_cap_hdr.s.num_entries);
241 if (isbridge)
242 {
243 cap = bdk_ecam_read32(device, cap_loc);
244 cap_loc += 4;
245 int fixed_secondary_bus = cap & 0xff;
246 int fixed_subordinate_bus = cap & 0xff;
247 BDK_TRACE(DEVICE_SCAN, "%s: Fixed Secondary Bus:0x%02x Fixed Subordinate Bus:0x%02x\n",
248 device->name, fixed_secondary_bus, fixed_subordinate_bus);
249 }
250 for (int entry = 0; entry < ea_cap_hdr.s.num_entries; entry++)
251 {
252 union bdk_pcc_ea_entry_s ea_entry;
253 memset(&ea_entry, 0, sizeof(ea_entry));
254 uint32_t *ptr = (uint32_t *)&ea_entry;
255 *ptr++ = bdk_ecam_read32(device, cap_loc);
256 #if __BYTE_ORDER == __BIG_ENDIAN
257 /* For big endian we actually need the previous data
258 shifted 32 bits */
259 *ptr = ptr[-1];
260 #endif
261 asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
262 int entry_size = ea_entry.s.entry_size;
263 for (int i = 0; i < entry_size; i++)
264 {
265 *ptr++ = bdk_ecam_read32(device, cap_loc + 4*i + 4);
266 }
267 #if __BYTE_ORDER == __BIG_ENDIAN
268 /* The upper and lower 32bits need to be swapped */
269 ea_entry.u[0] = (ea_entry.u[0] >> 32) | (ea_entry.u[0] << 32);
270 ea_entry.u[1] = (ea_entry.u[1] >> 32) | (ea_entry.u[1] << 32);
271 ea_entry.u[2] = (ea_entry.u[2] >> 32) | (ea_entry.u[2] << 32);
272 #endif
273 asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
274 BDK_TRACE(DEVICE_SCAN, "%s: Enable:%d Writeable:%d Secondary Prop:0x%02x Primary Prop:0x%02x BEI:%d Size:%d\n",
275 device->name, ea_entry.s.enable, ea_entry.s.w, ea_entry.s.sec_prop, ea_entry.s.pri_prop, ea_entry.s.bei, ea_entry.s.entry_size);
276 if (ea_entry.s.entry_size > 0)
277 {
278 BDK_TRACE(DEVICE_SCAN, "%s: Base:0x%08x 64bit:%d\n",
279 device->name, ea_entry.s.basel << 2, ea_entry.s.base64);
280 }
281 if (ea_entry.s.entry_size > 1)
282 {
283 BDK_TRACE(DEVICE_SCAN, "%s: MaxOffset:0x%08x 64bit:%d\n",
284 device->name, (ea_entry.s.offsetl << 2) | 3, ea_entry.s.offset64);
285 }
286 if (ea_entry.s.entry_size > 2)
287 {
288 BDK_TRACE(DEVICE_SCAN, "%s: BaseUpper:0x%08x\n",
289 device->name, ea_entry.s.baseh);
290 }
291 if (ea_entry.s.entry_size > 3)
292 {
293 BDK_TRACE(DEVICE_SCAN, "%s: MaxOffsetUpper:0x%08x\n",
294 device->name, ea_entry.s.offseth);
295 }
296 if (ea_entry.s.enable)
297 {
298 uint64_t base = (uint64_t)ea_entry.s.baseh << 32;
299 base |= (uint64_t)ea_entry.s.basel << 2;
300 /* Make sure the node bits are correct in the address */
301 base = (base & ~(3UL << 44)) | ((uint64_t)device->node << 44);
302 uint64_t offset = (uint64_t)ea_entry.s.offseth << 32;
303 offset |= ((uint64_t)ea_entry.s.offsetl << 2) | 3;
304 switch (ea_entry.s.bei)
305 {
306 case 0: /* BAR 0 */
307 case 2: /* BAR 1 */
308 case 4: /* BAR 2 */
309 {
310 int bar_index = ea_entry.s.bei/2;
311 device->bar[bar_index].address = base;
312 device->bar[bar_index].size2 = bdk_dpop(offset);
313 device->bar[bar_index].flags = ea_entry.s.base64 << 2;
314 BDK_TRACE(DEVICE_SCAN, "%s: Updated BAR%d 0x%llx/%d flags=0x%x\n",
315 device->name, bar_index, device->bar[bar_index].address,
316 device->bar[bar_index].size2, device->bar[bar_index].flags);
317 if (0 == ea_entry.s.bei) {
318 /* PEMs eg PCIEEP and PCIERC do not have instance id
319 ** We can calculate it for PCIERC based on BAR0 allocation.
320 ** PCIEEP will be dropped by probe
321 */
322 guess_instance = (device->bar[bar_index].address >> 24) & 7;
323 }
324 break;
325 }
326 case 9: /* SR-IOV BAR 0 */
327 case 11: /* SR-IOV BAR 1 */
328 case 13: /* SR-IOV BAR 2 */
329 // FIXME
330 break;
331 }
332 }
333 cap_loc += ea_entry.s.entry_size * 4 + 4;
334 }
335 }
336 else
337 {
338 /* Unknown PCI capability */
339 bdk_warn("%s: ECAM device unknown PCI capability 0x%x\n", device->name, cap_id);
340 }
341 cap_loc = cap_next;
342 }
343
344 /* Walk the PCIe capabilities looking for instance header */
345 if (has_pcie)
346 {
347 BDK_TRACE(DEVICE_SCAN, "%s: Walking PCIe capabilites\n", device->name);
348 cap_loc = 0x100;
349 while (cap_loc)
350 {
351 uint32_t cap = bdk_ecam_read32(device, cap_loc);
352 int cap_id = cap & 0xffff;
353 int cap_ver = (cap >> 16) & 0xf;
354 int cap_next = cap >> 20;
355 BDK_TRACE(DEVICE_SCAN, "%s: PCIe Capability 0x%03x ID:0x%04x Version:0x%x Next:0x%03x\n",
356 device->name, cap_loc, cap_id, cap_ver, cap_next);
357 if (cap_id == 0xe)
358 {
359 /* ARI. Do nothing for now */
360 BDK_TRACE(DEVICE_SCAN, "%s: ARI\n", device->name);
361 }
362 else if (cap_id == 0xb)
363 {
364 /* Vendor specific*/
365 int vsec_id = bdk_ecam_read32(device, cap_loc + 4);
366 int vsec_id_id = vsec_id & 0xffff;
367 int vsec_id_rev = (vsec_id >> 16) & 0xf;
368 int vsec_id_len = vsec_id >> 20;
369 BDK_TRACE(DEVICE_SCAN, "%s: Vendor ID: 0x%04x Rev: 0x%x Size 0x%03x\n",
370 device->name, vsec_id_id, vsec_id_rev, vsec_id_len);
371 switch (vsec_id_id)
372 {
373 case 0x0001: /* RAS Data Path */
374 BDK_TRACE(DEVICE_SCAN, "%s: Vendor RAS Data Path\n", device->name);
375 break;
376
377 case 0x0002: /* RAS DES */
378 BDK_TRACE(DEVICE_SCAN, "%s: Vendor RAS DES\n", device->name);
379 break;
380
381 case 0x00a0: /* Cavium common */
382 case 0x00a1: /* Cavium CN88XX */
383 case 0x00a2: /* Cavium CN81XX */
384 case 0x00a3: /* Cavium CN83XX */
385 if ((vsec_id_rev == 1) || (vsec_id_rev == 2))
386 {
387 int vsec_ctl = bdk_ecam_read32(device, cap_loc + 8);
388 int vsec_ctl_inst_num = vsec_ctl & 0xff;
389 int vsec_ctl_subnum = (vsec_ctl >> 8) & 0xff;
390 BDK_TRACE(DEVICE_SCAN, "%s: Cavium Instance: 0x%02x Static Bus: 0x%02x\n",
391 device->name, vsec_ctl_inst_num, vsec_ctl_subnum);
392 int vsec_sctl = bdk_ecam_read32(device, cap_loc + 12);
393 int vsec_sctl_rid = (vsec_sctl >> 16) & 0xff;
394 if (vsec_id_rev == 2)
395 {
396 int vsec_sctl_pi = (vsec_sctl >> 24) & 0xff; /* Only in Rev 2 */
397 BDK_TRACE(DEVICE_SCAN, "%s: Revision ID: 0x%02x Programming Interface: 0x%02x\n",
398 device->name, vsec_sctl_rid, vsec_sctl_pi);
399 }
400 else
401 {
402 BDK_TRACE(DEVICE_SCAN, "%s: Revision ID: 0x%02x\n",
403 device->name, vsec_sctl_rid);
404 }
405 /* Record the device instance */
406 device->instance = vsec_ctl_inst_num;
407 }
408 else
409 {
410 bdk_warn("%s: ECAM device Unknown Cavium extension revision\n", device->name);
411 }
412 break;
413
414 default: /* Unknown Vendor extension */
415 bdk_warn("%s: ECAM device unknown vendor extension ID 0x%x\n", device->name, vsec_id_id);
416 break;
417 }
418 }
419 else if (cap_id == 0x10)
420 {
421 /* Single Root I/O Virtualization (SR-IOV) */
422 BDK_TRACE(DEVICE_SCAN, "%s: SR-IOV\n", device->name);
423 /* Loop through all the SR-IOV BARs */
424 bar = cap_loc + 0x24;
425 while (bar <= (cap_loc + 0x3c))
426 {
427 int bar_index = (bar - 0x24 - cap_loc) / 8;
428 /* Read the BAR address and config bits [3:0] */
429 uint64_t address = bdk_ecam_read32(device, bar);
430 int ismem = !(address & 1); /* Bit 0: 0 = mem, 1 = io */
431 int is64 = ismem && (address & 4); /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
432 /* Bit 3: 1 = Is prefetchable. We don't care for now */
433
434 /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
435 it is using Enhanced Allocation (EA) */
436 if (!is64)
437 {
438 BDK_TRACE(DEVICE_SCAN, "%s: SR-IOV BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
439 bar += 8;
440 continue;
441 }
442
443 /* Get the upper part of 64bit BARs */
444 address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
445
446 /* Write the bits to determine the size */
447 bdk_ecam_write32(device, bar, -1);
448 bdk_ecam_write32(device, bar + 4, -1);
449 uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
450 size_mask |= bdk_ecam_read32(device, bar);
451 /* Make sure the node bits are correct in the address */
452 address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
453 /* Restore address value */
454 bdk_ecam_write32(device, bar, address);
455 bdk_ecam_write32(device, bar + 4, address >> 32);
456
457 /* Convert the size into a power of 2 bits */
458 int size_bits = bdk_dpop(size_mask | 0xf);
459 if (size_bits <= 4)
460 size_bits = 0;
461
462 BDK_TRACE(DEVICE_SCAN, "%s: SR-IOV BAR%d 0x%llx/%d flags=0x%llx\n",
463 device->name, bar_index, address & ~0xfull,
464 size_bits, address & 0xf);
465 /* Move to the next BAR */
466 bar += 8;
467 }
468 }
469 else if (cap_id == 0x01)
470 {
471 /* Advanced Error Reporting Capability */
472 BDK_TRACE(DEVICE_SCAN, "%s: Advanced Error Reporting\n", device->name);
473 }
474 else if (cap_id == 0x19)
475 {
476 /* Secondary PCI Express Extended Capability */
477 BDK_TRACE(DEVICE_SCAN, "%s: Secondary PCI Express Extended\n", device->name);
478 }
479 else if (cap_id == 0x15)
480 {
481 /* PCI Express Resizable BAR (RBAR) Capability */
482 BDK_TRACE(DEVICE_SCAN, "%s: PCI Express Resizable BAR (RBAR)\n", device->name);
483 }
484 else if (cap_id == 0x0d)
485 {
486 /* Extended access control := ACS Extended Capability */
487 BDK_TRACE(DEVICE_SCAN, "%s: ACS\n", device->name);
488 }
489 else
490 {
491 /* Unknown PCIe capability */
492 bdk_warn("%s: ECAM device unknown PCIe capability 0x%x\n", device->name, cap_id);
493 }
494 cap_loc = cap_next;
495 }
496 }
497 else
498 {
499 bdk_error("%s: ECAM device didn't have a PCIe capability\n", device->name);
500 }
501 if (BDK_NO_DEVICE_INSTANCE == device->instance) {
502 device->instance = guess_instance;
503 }
504 BDK_TRACE(DEVICE_SCAN, "%s: Device populated\n", device->name);
505 }
506
507 /**
508 * Called by the ECAM code whan a new device is detected in the system
509 *
510 * @param node Node the ECAM is on
511 * @param ecam ECAM the device is on
512 * @param bus Bus number for the device
513 * @param dev Device number
514 * @param func Function number
515 *
516 * @return Zero on success, negative on failure
517 */
bdk_device_add(bdk_node_t node,int ecam,int bus,int dev,int func)518 int bdk_device_add(bdk_node_t node, int ecam, int bus, int dev, int func)
519 {
520 if (device_list_count == device_list_max)
521 {
522 int grow = device_list_max + DEVICE_GROW;
523 bdk_device_t *tmp = malloc(grow * sizeof(bdk_device_t));
524 if (tmp)
525 memcpy(tmp, device_list, device_list_max * sizeof(bdk_device_t));
526 free(device_list);
527 if (tmp == NULL)
528 {
529 bdk_error("bdk-device: Failed to allocate space for device\n");
530 return -1;
531 }
532 device_list = tmp;
533 device_list_max = grow;
534 }
535
536 bdk_device_t *device = &device_list[device_list_count++];
537 memset(device, 0, sizeof(*device));
538
539 device->state = BDK_DEVICE_STATE_NOT_PROBED;
540 device->node = node;
541 device->ecam = ecam;
542 device->bus = bus;
543 device->dev = dev;
544 device->func = func;
545 device->instance = BDK_NO_DEVICE_INSTANCE;
546 populate_device(device);
547
548 const bdk_driver_t *drv = lookup_driver(device);
549 if (drv)
550 BDK_TRACE(DEVICE, "%s: Added device\n", device->name);
551 else
552 BDK_TRACE(DEVICE, "%s: Added device without driver (0x%08x)\n", device->name, device->id);
553 return 0;
554 }
555
556 /**
557 * Rename a device. Called by driver to give devices friendly names
558 *
559 * @param device Device to rename
560 * @param format Printf style format string
561 */
bdk_device_rename(bdk_device_t * device,const char * format,...)562 void bdk_device_rename(bdk_device_t *device, const char *format, ...)
563 {
564 char tmp[sizeof(device->name)];
565 va_list args;
566 va_start(args, format);
567 vsnprintf(tmp, sizeof(tmp), format, args);
568 va_end(args);
569 tmp[sizeof(tmp) - 1] = 0;
570 BDK_TRACE(DEVICE, "%s: Renamed to %s\n", device->name, tmp);
571 strcpy(device->name, tmp);
572 }
573
574 /**
575 * Called by the ECAM code once all devices have been added
576 *
577 * @return Zero on success, negative on failure
578 */
bdk_device_init(void)579 int bdk_device_init(void)
580 {
581 /* Probe all devices first */
582 for (int i = 0; i < device_list_count; i++)
583 {
584 bdk_device_t *dev = &device_list[i];
585 const bdk_driver_t *drv = lookup_driver(dev);
586 if (drv == NULL)
587 continue;
588 if (dev->state == BDK_DEVICE_STATE_NOT_PROBED)
589 {
590 BDK_TRACE(DEVICE, "%s: Probing\n", dev->name);
591 if (drv->probe(dev))
592 {
593 BDK_TRACE(DEVICE, "%s: Probe failed\n", dev->name);
594 dev->state = BDK_DEVICE_STATE_PROBE_FAIL;
595 }
596 else
597 {
598 BDK_TRACE(DEVICE, "%s: Probe complete\n", dev->name);
599 dev->state = BDK_DEVICE_STATE_PROBED;
600 }
601 }
602 }
603
604 /* Do init() after all the probes. See comments in top of bdk-device.h */
605 for (int i = 0; i < device_list_count; i++)
606 {
607 bdk_device_t *dev = &device_list[i];
608 const bdk_driver_t *drv = lookup_driver(dev);
609 if (drv == NULL)
610 continue;
611 if (dev->state == BDK_DEVICE_STATE_PROBED)
612 {
613 BDK_TRACE(DEVICE, "%s: Initializing\n", dev->name);
614 if (drv->init(dev))
615 {
616 BDK_TRACE(DEVICE, "%s: Init failed\n", dev->name);
617 dev->state = BDK_DEVICE_STATE_INIT_FAIL;
618 }
619 else
620 {
621 BDK_TRACE(DEVICE, "%s: Init complete\n", dev->name);
622 dev->state = BDK_DEVICE_STATE_READY;
623 }
624 }
625 }
626 return 0;
627 }
628
629 /**
630 * Lookup a device by ECAM ID and internal instance number. This can be used by
631 * one device to find a handle to an associated device. For example, PKI would
632 * use this function to get a handle to the FPA.
633 *
634 * @param node Node to lookup for
635 * @param id ECAM ID
636 * @param instance Cavium internal instance number
637 *
638 * @return Device pointer, or NULL if the device isn't found
639 */
bdk_device_lookup(bdk_node_t node,uint32_t id,int instance)640 const bdk_device_t *bdk_device_lookup(bdk_node_t node, uint32_t id, int instance)
641 {
642 for (int i = 0; i < device_list_count; i++)
643 {
644 bdk_device_t *dev = &device_list[i];
645 if ((dev->node == node) && (dev->id == id) && (dev->instance == instance))
646 return dev;
647 }
648 BDK_TRACE(DEVICE, "No device found for node %d, ID %08x, instance %d\n", node, id, instance);
649 return NULL;
650 }
651
652 /**
653 * Read from a device BAR
654 *
655 * @param device Device to read from
656 * @param bar Which BAR to read from (0-3)
657 * @param size Size of the read
658 * @param offset Offset into the BAR
659 *
660 * @return Value read
661 */
bdk_bar_read(const bdk_device_t * device,int bar,int size,uint64_t offset)662 uint64_t bdk_bar_read(const bdk_device_t *device, int bar, int size, uint64_t offset)
663 {
664 uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
665 address += device->bar[bar/2].address;
666 if (offset+size > (1ULL << device->bar[bar/2].size2)) {
667 /* The CSR address passed in offset doesn't contain the node number. Copy it
668 from the BAR address */
669 offset |= address & (0x3ull << 44);
670 if (address != offset)
671 bdk_fatal("BAR read address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
672 }
673 switch (size)
674 {
675 case 1:
676 return bdk_read64_uint8(address);
677 case 2:
678 return bdk_le16_to_cpu(bdk_read64_uint16(address));
679 case 4:
680 return bdk_le32_to_cpu(bdk_read64_uint32(address));
681 case 8:
682 return bdk_le64_to_cpu(bdk_read64_uint64(address));
683 }
684 bdk_fatal("%s: Unexpected read size %d\n", device->name, size);
685 }
686
687 /**
688 * Write to a device BAR
689 *
690 * @param device Device to write to
691 * @param bar Which BAR to read from (0-3)
692 * @param size Size of the write
693 * @param offset Offset into the BAR
694 * @param value Value to write
695 */
bdk_bar_write(const bdk_device_t * device,int bar,int size,uint64_t offset,uint64_t value)696 void bdk_bar_write(const bdk_device_t *device, int bar, int size, uint64_t offset, uint64_t value)
697 {
698 uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
699 address += device->bar[bar/2].address;
700 if (offset+size > (1ULL << device->bar[bar/2].size2)) {
701 /* The CSR address passed in offset doesn't contain the node number. Copy it
702 from the BAR address */
703 offset |= address & (0x3ull << 44);
704 if (address != offset)
705 bdk_fatal("BAR write address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
706 }
707 switch (size)
708 {
709 case 1:
710 bdk_write64_uint8(address, value);
711 return;
712 case 2:
713 bdk_write64_uint16(address, bdk_cpu_to_le16(value));
714 return;
715 case 4:
716 bdk_write64_uint32(address, bdk_cpu_to_le32(value));
717 return;
718 case 8:
719 bdk_write64_uint64(address, bdk_cpu_to_le64(value));
720 return;
721 }
722 bdk_fatal("%s: Unexpected write size %d\n", device->name, size);
723 }
724