aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c')
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c b/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c
new file mode 100644
index 0000000000..a8e65f061a
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c
@@ -0,0 +1,721 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+*
+* * Neither the name of Cavium Inc. nor the names of
+* its contributors may be used to endorse or promote products
+* derived from this software without specific prior written
+* permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <string.h>
+#include "libbdk-arch/bdk-csrs-ap.h"
+#include "libbdk-arch/bdk-csrs-pccpf.h"
+#include "libbdk-hal/bdk-ecam.h"
+#include "libbdk-hal/device/bdk-device.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-driver/bdk-driver.h"
+#include "libbdk-hal/bdk-utils.h"
+
+static struct bdk_driver_s *driver_list = NULL;
+
+#define DEVICE_GROW 64
+static bdk_device_t *device_list = NULL;
+static int device_list_count = 0;
+static int device_list_max = 0;
+
+/**
+ * Called to register a new driver with the bdk-device system. Drivers are probed
+ * and initialized as device are found for them. If devices have already been
+ * added before the driver was registered, the driver will be probed and
+ * initialized before this function returns.
+ *
+ * @param driver Driver functions
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_device_add_driver(struct bdk_driver_s *driver)
+{
+ driver->next = driver_list;
+ driver_list = driver;
+ BDK_TRACE(DEVICE, "Added driver for %08x\n", driver->id);
+ return 0;
+}
+
+/**
+ * Lookup the correct driver for a device
+ *
+ * @param device Device to lookup
+ *
+ * @return Driver, or NULL on failure
+ */
+static const bdk_driver_t *lookup_driver(const bdk_device_t *device)
+{
+ const bdk_driver_t *drv = driver_list;
+ while (drv)
+ {
+ if (drv->id == device->id)
+ return drv;
+ drv = drv->next;
+ }
+ return NULL;
+}
+
+/**
+ * Populate the fields of a new device from the ECAM
+ *
+ * @param device Device to populate
+ */
+static void populate_device(bdk_device_t *device)
+{
+ /* The default name may be replaced by the driver with something easier to read */
+ snprintf(device->name, sizeof(device->name), "N%d.E%d:%d:%d.%d",
+ device->node, device->ecam, device->bus, device->dev, device->func);
+
+ BDK_TRACE(DEVICE_SCAN, "%s: Populating device\n", device->name);
+
+ /* Get the current chip ID and pass. We'll need this to fill in version
+ information for the device */
+ bdk_ap_midr_el1_t midr_el1;
+ BDK_MRS(MIDR_EL1, midr_el1.u);
+
+ /* PCCPF_XXX_VSEC_SCTL[RID] with the revision of the chip,
+ read from fuses */
+ BDK_CSR_DEFINE(sctl, BDK_PCCPF_XXX_VSEC_SCTL);
+ sctl.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_VSEC_SCTL);
+ sctl.s.rid = midr_el1.s.revision | (midr_el1.s.variant<<3);
+ sctl.s.node = device->node; /* Program node bits */
+ sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
+ if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
+ sctl.s.ea = 0; /* EA is not supported on CN88XX pass 1.x */
+ else
+ sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
+ bdk_ecam_write32(device, BDK_PCCPF_XXX_VSEC_SCTL, sctl.u);
+
+ /* Read the Device ID */
+ device->id = bdk_ecam_read32(device, BDK_PCCPF_XXX_ID);
+
+ /* Read the Device Type so we know how to handle BARs */
+ bdk_pccpf_xxx_clsize_t clsize;
+ clsize.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CLSIZE);
+ int isbridge = (clsize.s.hdrtype & 0x7f) == 1;
+
+ BDK_TRACE(DEVICE_SCAN, "%s: Device ID: 0x%08x%s\n", device->name, device->id,
+ (isbridge) ? " (Bridge)" : "");
+
+ /* Loop through all the BARs */
+ int max_bar = (isbridge) ? BDK_PCCPF_XXX_BAR0U : BDK_PCCPF_XXX_BAR4U;
+ int bar = BDK_PCCPF_XXX_BAR0L;
+ unsigned guess_instance = 0;
+ while (bar <= max_bar)
+ {
+ int bar_index = (bar - BDK_PCCPF_XXX_BAR0L) / 8;
+ /* Read the BAR address and config bits [3:0] */
+ uint64_t address = bdk_ecam_read32(device, bar);
+ int ismem = !(address & 1); /* Bit 0: 0 = mem, 1 = io */
+ int is64 = ismem && (address & 4); /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
+ /* Bit 3: 1 = Is prefetchable. We on't care for now */
+
+ /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
+ it is using Enhanced Allocation (EA) */
+ if (!is64)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
+ bar += 8;
+ continue;
+ }
+
+ /* Get the upper part of 64bit BARs */
+ address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+
+ /* Write the bits to determine the size */
+ bdk_ecam_write32(device, bar, -1);
+ bdk_ecam_write32(device, bar + 4, -1);
+ uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+ size_mask |= bdk_ecam_read32(device, bar);
+ /* Make sure the node bits are correct in the address */
+ address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
+ /* Restore address value */
+ bdk_ecam_write32(device, bar, address);
+ bdk_ecam_write32(device, bar + 4, address >> 32);
+
+ /* Convert the size into a power of 2 bits */
+ int size_bits = bdk_dpop(~size_mask | 0xf);
+ if (size_bits <= 4)
+ size_bits = 0;
+
+ /* Store the BAR info */
+ device->bar[bar_index].address = address & ~0xfull;
+ device->bar[bar_index].size2 = size_bits;
+ device->bar[bar_index].flags = address & 0xf;
+ BDK_TRACE(DEVICE_SCAN, "%s: BAR%d 0x%llx/%d flags=0x%x\n",
+ device->name, bar_index, device->bar[bar_index].address,
+ device->bar[bar_index].size2, device->bar[bar_index].flags);
+ /* Move to the next BAR */
+ bar += 8;
+ }
+
+ /* Walk the PCI capabilities looking for PCIe support and EA headers */
+ BDK_TRACE(DEVICE_SCAN, "%s: Walking PCI capabilites\n", device->name);
+ int has_pcie = 0;
+ bdk_pccpf_xxx_cap_ptr_t cap_ptr;
+ cap_ptr.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CAP_PTR);
+ int cap_loc = cap_ptr.s.cp;
+ while (cap_loc)
+ {
+ uint32_t cap = bdk_ecam_read32(device, cap_loc);
+ int cap_id = cap & 0xff;
+ int cap_next = (cap >> 8) & 0xff;
+
+ BDK_TRACE(DEVICE_SCAN, "%s: PCI Capability 0x%02x ID:0x%02x Next:0x%02x\n",
+ device->name, cap_loc, cap_id, cap_next);
+
+ if (cap_id == 0x10)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: PCIe\n", device->name);
+ has_pcie = 1;
+ }
+ else if (cap_id == 0x01)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: PCI Power Management Interface\n", device->name);
+ /* Do nothing for now */
+ }
+ else if (cap_id == 0x11)
+ {
+ bdk_pccpf_xxx_msix_cap_hdr_t msix_cap_hdr;
+ bdk_pccpf_xxx_msix_table_t msix_table;
+ bdk_pccpf_xxx_msix_pba_t msix_pba;
+ msix_cap_hdr.u = cap;
+ msix_table.u = bdk_ecam_read32(device, cap_loc + 4);
+ msix_pba.u = bdk_ecam_read32(device, cap_loc + 8);
+ BDK_TRACE(DEVICE_SCAN, "%s: MSI-X Entries:%d, Func Mask:%d, Enable:%d\n",
+ device->name, msix_cap_hdr.s.msixts + 1, msix_cap_hdr.s.funm, msix_cap_hdr.s.msixen);
+ BDK_TRACE(DEVICE_SCAN, "%s: Table BAR%d, Offset:0x%x\n",
+ device->name, msix_table.s.msixtbir, msix_table.s.msixtoffs * 8);
+ BDK_TRACE(DEVICE_SCAN, "%s: PBA BAR%d, Offset:0x%x\n",
+ device->name, msix_pba.s.msixpbir, msix_pba.s.msixpoffs * 8);
+ }
+ else if (cap_id == 0x05)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: MSI\n", device->name);
+ /* Do nothing for now */
+ }
+ else if (cap_id == 0x14)
+ {
+ bdk_pccpf_xxx_ea_cap_hdr_t ea_cap_hdr;
+ ea_cap_hdr.u = cap;
+ cap_loc += 4;
+ BDK_TRACE(DEVICE_SCAN, "%s: Enhanced Allocation, %d entries\n",
+ device->name, ea_cap_hdr.s.num_entries);
+ if (isbridge)
+ {
+ cap = bdk_ecam_read32(device, cap_loc);
+ cap_loc += 4;
+ int fixed_secondary_bus = cap & 0xff;
+ int fixed_subordinate_bus = cap & 0xff;
+ BDK_TRACE(DEVICE_SCAN, "%s: Fixed Secondary Bus:0x%02x Fixed Subordinate Bus:0x%02x\n",
+ device->name, fixed_secondary_bus, fixed_subordinate_bus);
+ }
+ for (int entry = 0; entry < ea_cap_hdr.s.num_entries; entry++)
+ {
+ union bdk_pcc_ea_entry_s ea_entry;
+ memset(&ea_entry, 0, sizeof(ea_entry));
+ uint32_t *ptr = (uint32_t *)&ea_entry;
+ *ptr++ = bdk_ecam_read32(device, cap_loc);
+#if __BYTE_ORDER == __BIG_ENDIAN
+ /* For big endian we actually need the previous data
+ shifted 32 bits */
+ *ptr = ptr[-1];
+#endif
+ asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
+ int entry_size = ea_entry.s.entry_size;
+ for (int i = 0; i < entry_size; i++)
+ {
+ *ptr++ = bdk_ecam_read32(device, cap_loc + 4*i + 4);
+ }
+#if __BYTE_ORDER == __BIG_ENDIAN
+ /* The upper and lower 32bits need to be swapped */
+ ea_entry.u[0] = (ea_entry.u[0] >> 32) | (ea_entry.u[0] << 32);
+ ea_entry.u[1] = (ea_entry.u[1] >> 32) | (ea_entry.u[1] << 32);
+ ea_entry.u[2] = (ea_entry.u[2] >> 32) | (ea_entry.u[2] << 32);
+#endif
+ asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
+ BDK_TRACE(DEVICE_SCAN, "%s: Enable:%d Writeable:%d Secondary Prop:0x%02x Primary Prop:0x%02x BEI:%d Size:%d\n",
+ 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);
+ if (ea_entry.s.entry_size > 0)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: Base:0x%08x 64bit:%d\n",
+ device->name, ea_entry.s.basel << 2, ea_entry.s.base64);
+ }
+ if (ea_entry.s.entry_size > 1)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: MaxOffset:0x%08x 64bit:%d\n",
+ device->name, (ea_entry.s.offsetl << 2) | 3, ea_entry.s.offset64);
+ }
+ if (ea_entry.s.entry_size > 2)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: BaseUpper:0x%08x\n",
+ device->name, ea_entry.s.baseh);
+ }
+ if (ea_entry.s.entry_size > 3)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: MaxOffsetUpper:0x%08x\n",
+ device->name, ea_entry.s.offseth);
+ }
+ if (ea_entry.s.enable)
+ {
+ uint64_t base = (uint64_t)ea_entry.s.baseh << 32;
+ base |= (uint64_t)ea_entry.s.basel << 2;
+ /* Make sure the node bits are correct in the address */
+ base = (base & ~(3UL << 44)) | ((uint64_t)device->node << 44);
+ uint64_t offset = (uint64_t)ea_entry.s.offseth << 32;
+ offset |= ((uint64_t)ea_entry.s.offsetl << 2) | 3;
+ switch (ea_entry.s.bei)
+ {
+ case 0: /* BAR 0 */
+ case 2: /* BAR 1 */
+ case 4: /* BAR 2 */
+ {
+ int bar_index = ea_entry.s.bei/2;
+ device->bar[bar_index].address = base;
+ device->bar[bar_index].size2 = bdk_dpop(offset);
+ device->bar[bar_index].flags = ea_entry.s.base64 << 2;
+ BDK_TRACE(DEVICE_SCAN, "%s: Updated BAR%d 0x%llx/%d flags=0x%x\n",
+ device->name, bar_index, device->bar[bar_index].address,
+ device->bar[bar_index].size2, device->bar[bar_index].flags);
+ if (0 == ea_entry.s.bei) {
+ /* PEMs eg PCIEEP and PCIERC do not have instance id
+ ** We can calculate it for PCIERC based on BAR0 allocation.
+ ** PCIEEP will be dropped by probe
+ */
+ guess_instance = (device->bar[bar_index].address >> 24) & 7;
+ }
+ break;
+ }
+ case 9: /* SR-IOV BAR 0 */
+ case 11: /* SR-IOV BAR 1 */
+ case 13: /* SR-IOV BAR 2 */
+ // FIXME
+ break;
+ }
+ }
+ cap_loc += ea_entry.s.entry_size * 4 + 4;
+ }
+ }
+ else
+ {
+ /* Unknown PCI capability */
+ bdk_warn("%s: ECAM device unknown PCI capability 0x%x\n", device->name, cap_id);
+ }
+ cap_loc = cap_next;
+ }
+
+ /* Walk the PCIe capabilities looking for instance header */
+ if (has_pcie)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: Walking PCIe capabilites\n", device->name);
+ cap_loc = 0x100;
+ while (cap_loc)
+ {
+ uint32_t cap = bdk_ecam_read32(device, cap_loc);
+ int cap_id = cap & 0xffff;
+ int cap_ver = (cap >> 16) & 0xf;
+ int cap_next = cap >> 20;
+ BDK_TRACE(DEVICE_SCAN, "%s: PCIe Capability 0x%03x ID:0x%04x Version:0x%x Next:0x%03x\n",
+ device->name, cap_loc, cap_id, cap_ver, cap_next);
+ if (cap_id == 0xe)
+ {
+ /* ARI. Do nothing for now */
+ BDK_TRACE(DEVICE_SCAN, "%s: ARI\n", device->name);
+ }
+ else if (cap_id == 0xb)
+ {
+ /* Vendor specific*/
+ int vsec_id = bdk_ecam_read32(device, cap_loc + 4);
+ int vsec_id_id = vsec_id & 0xffff;
+ int vsec_id_rev = (vsec_id >> 16) & 0xf;
+ int vsec_id_len = vsec_id >> 20;
+ BDK_TRACE(DEVICE_SCAN, "%s: Vendor ID: 0x%04x Rev: 0x%x Size 0x%03x\n",
+ device->name, vsec_id_id, vsec_id_rev, vsec_id_len);
+ switch (vsec_id_id)
+ {
+ case 0x0001: /* RAS Data Path */
+ BDK_TRACE(DEVICE_SCAN, "%s: Vendor RAS Data Path\n", device->name);
+ break;
+
+ case 0x0002: /* RAS DES */
+ BDK_TRACE(DEVICE_SCAN, "%s: Vendor RAS DES\n", device->name);
+ break;
+
+ case 0x00a0: /* Cavium common */
+ case 0x00a1: /* Cavium CN88XX */
+ case 0x00a2: /* Cavium CN81XX */
+ case 0x00a3: /* Cavium CN83XX */
+ if ((vsec_id_rev == 1) || (vsec_id_rev == 2))
+ {
+ int vsec_ctl = bdk_ecam_read32(device, cap_loc + 8);
+ int vsec_ctl_inst_num = vsec_ctl & 0xff;
+ int vsec_ctl_subnum = (vsec_ctl >> 8) & 0xff;
+ BDK_TRACE(DEVICE_SCAN, "%s: Cavium Instance: 0x%02x Static Bus: 0x%02x\n",
+ device->name, vsec_ctl_inst_num, vsec_ctl_subnum);
+ int vsec_sctl = bdk_ecam_read32(device, cap_loc + 12);
+ int vsec_sctl_rid = (vsec_sctl >> 16) & 0xff;
+ if (vsec_id_rev == 2)
+ {
+ int vsec_sctl_pi = (vsec_sctl >> 24) & 0xff; /* Only in Rev 2 */
+ BDK_TRACE(DEVICE_SCAN, "%s: Revision ID: 0x%02x Programming Interface: 0x%02x\n",
+ device->name, vsec_sctl_rid, vsec_sctl_pi);
+ }
+ else
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: Revision ID: 0x%02x\n",
+ device->name, vsec_sctl_rid);
+ }
+ /* Record the device instance */
+ device->instance = vsec_ctl_inst_num;
+ }
+ else
+ {
+ bdk_warn("%s: ECAM device Unknown Cavium extension revision\n", device->name);
+ }
+ break;
+
+ default: /* Unknown Vendor extension */
+ bdk_warn("%s: ECAM device unknown vendor extension ID 0x%x\n", device->name, vsec_id_id);
+ break;
+ }
+ }
+ else if (cap_id == 0x10)
+ {
+ /* Single Root I/O Virtualization (SR-IOV) */
+ BDK_TRACE(DEVICE_SCAN, "%s: SR-IOV\n", device->name);
+ /* Loop through all the SR-IOV BARs */
+ bar = cap_loc + 0x24;
+ while (bar <= (cap_loc + 0x3c))
+ {
+ int bar_index = (bar - 0x24 - cap_loc) / 8;
+ /* Read the BAR address and config bits [3:0] */
+ uint64_t address = bdk_ecam_read32(device, bar);
+ int ismem = !(address & 1); /* Bit 0: 0 = mem, 1 = io */
+ int is64 = ismem && (address & 4); /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
+ /* Bit 3: 1 = Is prefetchable. We don't care for now */
+
+ /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
+ it is using Enhanced Allocation (EA) */
+ if (!is64)
+ {
+ BDK_TRACE(DEVICE_SCAN, "%s: SR-IOV BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
+ bar += 8;
+ continue;
+ }
+
+ /* Get the upper part of 64bit BARs */
+ address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+
+ /* Write the bits to determine the size */
+ bdk_ecam_write32(device, bar, -1);
+ bdk_ecam_write32(device, bar + 4, -1);
+ uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+ size_mask |= bdk_ecam_read32(device, bar);
+ /* Make sure the node bits are correct in the address */
+ address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
+ /* Restore address value */
+ bdk_ecam_write32(device, bar, address);
+ bdk_ecam_write32(device, bar + 4, address >> 32);
+
+ /* Convert the size into a power of 2 bits */
+ int size_bits = bdk_dpop(size_mask | 0xf);
+ if (size_bits <= 4)
+ size_bits = 0;
+
+ BDK_TRACE(DEVICE_SCAN, "%s: SR-IOV BAR%d 0x%llx/%d flags=0x%llx\n",
+ device->name, bar_index, address & ~0xfull,
+ size_bits, address & 0xf);
+ /* Move to the next BAR */
+ bar += 8;
+ }
+ }
+ else if (cap_id == 0x01)
+ {
+ /* Advanced Error Reporting Capability */
+ BDK_TRACE(DEVICE_SCAN, "%s: Advanced Error Reporting\n", device->name);
+ }
+ else if (cap_id == 0x19)
+ {
+ /* Secondary PCI Express Extended Capability */
+ BDK_TRACE(DEVICE_SCAN, "%s: Secondary PCI Express Extended\n", device->name);
+ }
+ else if (cap_id == 0x15)
+ {
+ /* PCI Express Resizable BAR (RBAR) Capability */
+ BDK_TRACE(DEVICE_SCAN, "%s: PCI Express Resizable BAR (RBAR)\n", device->name);
+ }
+ else if (cap_id == 0x0d)
+ {
+ /* Extended access control := ACS Extended Capability */
+ BDK_TRACE(DEVICE_SCAN, "%s: ACS\n", device->name);
+ }
+ else
+ {
+ /* Unknown PCIe capability */
+ bdk_warn("%s: ECAM device unknown PCIe capability 0x%x\n", device->name, cap_id);
+ }
+ cap_loc = cap_next;
+ }
+ }
+ else
+ {
+ bdk_error("%s: ECAM device didn't have a PCIe capability\n", device->name);
+ }
+ if (BDK_NO_DEVICE_INSTANCE == device->instance) {
+ device->instance = guess_instance;
+ }
+ BDK_TRACE(DEVICE_SCAN, "%s: Device populated\n", device->name);
+}
+
+/**
+ * Called by the ECAM code whan a new device is detected in the system
+ *
+ * @param node Node the ECAM is on
+ * @param ecam ECAM the device is on
+ * @param bus Bus number for the device
+ * @param dev Device number
+ * @param func Function number
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_device_add(bdk_node_t node, int ecam, int bus, int dev, int func)
+{
+ if (device_list_count == device_list_max)
+ {
+ int grow = device_list_max + DEVICE_GROW;
+ bdk_device_t *tmp = malloc(grow * sizeof(bdk_device_t));
+ if (!tmp)
+ memcpy(tmp, device_list, device_list_max * sizeof(bdk_device_t));
+ free(device_list);
+ if (tmp == NULL)
+ {
+ bdk_error("bdk-device: Failed to allocate space for device\n");
+ return -1;
+ }
+ device_list = tmp;
+ device_list_max = grow;
+ }
+
+ bdk_device_t *device = &device_list[device_list_count++];
+ memset(device, 0, sizeof(*device));
+
+ device->state = BDK_DEVICE_STATE_NOT_PROBED;
+ device->node = node;
+ device->ecam = ecam;
+ device->bus = bus;
+ device->dev = dev;
+ device->func = func;
+ device->instance = BDK_NO_DEVICE_INSTANCE;
+ populate_device(device);
+
+ const bdk_driver_t *drv = lookup_driver(device);
+ if (drv)
+ BDK_TRACE(DEVICE, "%s: Added device\n", device->name);
+ else
+ BDK_TRACE(DEVICE, "%s: Added device without driver (0x%08x)\n", device->name, device->id);
+ return 0;
+}
+
+/**
+ * Rename a device. Called by driver to give devices friendly names
+ *
+ * @param device Device to rename
+ * @param format Printf style format string
+ */
+void bdk_device_rename(bdk_device_t *device, const char *format, ...)
+{
+ char tmp[sizeof(device->name)];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(tmp, sizeof(tmp), format, args);
+ va_end(args);
+ tmp[sizeof(tmp) - 1] = 0;
+ BDK_TRACE(DEVICE, "%s: Renamed to %s\n", device->name, tmp);
+ strcpy(device->name, tmp);
+}
+
+/**
+ * Called by the ECAM code once all devices have been added
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_device_init(void)
+{
+ /* Probe all devices first */
+ for (int i = 0; i < device_list_count; i++)
+ {
+ bdk_device_t *dev = &device_list[i];
+ const bdk_driver_t *drv = lookup_driver(dev);
+ if (drv == NULL)
+ continue;
+ if (dev->state == BDK_DEVICE_STATE_NOT_PROBED)
+ {
+ BDK_TRACE(DEVICE, "%s: Probing\n", dev->name);
+ if (drv->probe(dev))
+ {
+ BDK_TRACE(DEVICE, "%s: Probe failed\n", dev->name);
+ dev->state = BDK_DEVICE_STATE_PROBE_FAIL;
+ }
+ else
+ {
+ BDK_TRACE(DEVICE, "%s: Probe complete\n", dev->name);
+ dev->state = BDK_DEVICE_STATE_PROBED;
+ }
+ }
+ }
+
+ /* Do init() after all the probes. See comments in top of bdk-device.h */
+ for (int i = 0; i < device_list_count; i++)
+ {
+ bdk_device_t *dev = &device_list[i];
+ const bdk_driver_t *drv = lookup_driver(dev);
+ if (drv == NULL)
+ continue;
+ if (dev->state == BDK_DEVICE_STATE_PROBED)
+ {
+ BDK_TRACE(DEVICE, "%s: Initializing\n", dev->name);
+ if (drv->init(dev))
+ {
+ BDK_TRACE(DEVICE, "%s: Init failed\n", dev->name);
+ dev->state = BDK_DEVICE_STATE_INIT_FAIL;
+ }
+ else
+ {
+ BDK_TRACE(DEVICE, "%s: Init complete\n", dev->name);
+ dev->state = BDK_DEVICE_STATE_READY;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Lookup a device by ECAM ID and internal instance number. This can be used by
+ * one device to find a handle to an associated device. For example, PKI would
+ * use this function to get a handle to the FPA.
+ *
+ * @param node Node to lookup for
+ * @param id ECAM ID
+ * @param instance Cavium internal instance number
+ *
+ * @return Device pointer, or NULL if the device isn't found
+ */
+const bdk_device_t *bdk_device_lookup(bdk_node_t node, uint32_t id, int instance)
+{
+ for (int i = 0; i < device_list_count; i++)
+ {
+ bdk_device_t *dev = &device_list[i];
+ if ((dev->node == node) && (dev->id == id) && (dev->instance == instance))
+ return dev;
+ }
+ BDK_TRACE(DEVICE, "No device found for node %d, ID %08x, instance %d\n", node, id, instance);
+ return NULL;
+}
+
+/**
+ * Read from a device BAR
+ *
+ * @param device Device to read from
+ * @param bar Which BAR to read from (0-3)
+ * @param size Size of the read
+ * @param offset Offset into the BAR
+ *
+ * @return Value read
+ */
+uint64_t bdk_bar_read(const bdk_device_t *device, int bar, int size, uint64_t offset)
+{
+ uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
+ address += device->bar[bar/2].address;
+ if (offset+size > (1ULL << device->bar[bar/2].size2)) {
+ /* The CSR address passed in offset doesn't contain the node number. Copy it
+ from the BAR address */
+ offset |= address & (0x3ull << 44);
+ if (address != offset)
+ bdk_fatal("BAR read address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
+ }
+ switch (size)
+ {
+ case 1:
+ return bdk_read64_uint8(address);
+ case 2:
+ return bdk_le16_to_cpu(bdk_read64_uint16(address));
+ case 4:
+ return bdk_le32_to_cpu(bdk_read64_uint32(address));
+ case 8:
+ return bdk_le64_to_cpu(bdk_read64_uint64(address));
+ }
+ bdk_fatal("%s: Unexpected read size %d\n", device->name, size);
+}
+
+/**
+ * Write to a device BAR
+ *
+ * @param device Device to write to
+ * @param bar Which BAR to read from (0-3)
+ * @param size Size of the write
+ * @param offset Offset into the BAR
+ * @param value Value to write
+ */
+void bdk_bar_write(const bdk_device_t *device, int bar, int size, uint64_t offset, uint64_t value)
+{
+ uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
+ address += device->bar[bar/2].address;
+ if (offset+size > (1ULL << device->bar[bar/2].size2)) {
+ /* The CSR address passed in offset doesn't contain the node number. Copy it
+ from the BAR address */
+ offset |= address & (0x3ull << 44);
+ if (address != offset)
+ bdk_fatal("BAR write address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
+ }
+ switch (size)
+ {
+ case 1:
+ bdk_write64_uint8(address, value);
+ return;
+ case 2:
+ bdk_write64_uint16(address, bdk_cpu_to_le16(value));
+ return;
+ case 4:
+ bdk_write64_uint32(address, bdk_cpu_to_le32(value));
+ return;
+ case 8:
+ bdk_write64_uint64(address, bdk_cpu_to_le64(value));
+ return;
+ }
+ bdk_fatal("%s: Unexpected write size %d\n", device->name, size);
+}