aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c')
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c
new file mode 100644
index 0000000000..8120820626
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c
@@ -0,0 +1,373 @@
+/***********************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 "libbdk-arch/bdk-csrs-ecam.h"
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-pccpf.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-hal/device/bdk-device.h"
+#include "libbdk-hal/bdk-ecam.h"
+
+#if 1 /* Support CN88XX pass 1.0 */
+/*******************************************************************
+ *******************************************************************
+ These functions are related to CN88XX pass 1.0 errata and do not
+ apply to any other chip
+ *******************************************************************
+ *******************************************************************/
+
+/**
+ * Errata (ECAM-22630) ECAM function accesses can fault
+ * For some errata workaround we need a check to tell if a ECAM access is to a
+ * valid intenral device. This function decodes a pcc_dev_con_e enumeration and
+ * checks if the supplied arguments match it. This should only
+ * ever be called on CN88XX pass 1.0.
+ *
+ * @param ecam ECAM to check
+ * @param bus ECAM bus number
+ * @param dev Device to check
+ * @param fn sub function of device
+ * @param dev_con Enumeration to match against
+ *
+ * @return Non zero if the device matches
+ */
+static int is_internal_cn88xxp1_0(const bdk_device_t *device, int dev_con)
+{
+ union bdk_pcc_dev_con_s d = { .u = dev_con };
+ return (d.cn8.ecam == device->ecam) && (d.s.bus == device->bus) && (d.s.func == ((device->dev<<3)|device->func));
+}
+
+/**
+ * Errata (ECAM-22630) ECAM function accesses can fault
+ * This is a companion to the function above to determine if the ECAM device is
+ * any of the valid internal devices. This should only ever be
+ * called on CN88XX pass 1.0.
+ *
+ * @param ecam ECAM to check
+ * @param bus ECAM bus number
+ * @param dev Device to check
+ * @param fn sub function of device
+ *
+ * @return Non zero if the device matches
+ */
+static int is_any_internal_cn88xxp1_0(const bdk_device_t *device)
+{
+ /* Errata (ECAM-22630) ECAM function accesses can fault
+ CN88XXP1.0: The ECAM has a bug where accessing a non-existent
+ device causes an exception. This is a list of all valid devices
+ for CN88XX pass 1.0 */
+ static const uint32_t INTERNAL_DEVICES_CN88XXP1_0[] = {
+ BDK_PCC_DEV_CON_E_BGXX(0),
+ BDK_PCC_DEV_CON_E_BGXX(1),
+ BDK_PCC_DEV_CON_E_DAP,
+ BDK_PCC_DEV_CON_E_DFA,
+ BDK_PCC_DEV_CON_E_FUSF,
+ BDK_PCC_DEV_CON_E_GIC_CN8,
+ BDK_PCC_DEV_CON_E_GPIO_CN8,
+ BDK_PCC_DEV_CON_E_GSERX(0),
+ BDK_PCC_DEV_CON_E_GSERX(1),
+ BDK_PCC_DEV_CON_E_GSERX(10),
+ BDK_PCC_DEV_CON_E_GSERX(11),
+ BDK_PCC_DEV_CON_E_GSERX(12),
+ BDK_PCC_DEV_CON_E_GSERX(13),
+ BDK_PCC_DEV_CON_E_GSERX(2),
+ BDK_PCC_DEV_CON_E_GSERX(3),
+ BDK_PCC_DEV_CON_E_GSERX(4),
+ BDK_PCC_DEV_CON_E_GSERX(5),
+ BDK_PCC_DEV_CON_E_GSERX(6),
+ BDK_PCC_DEV_CON_E_GSERX(7),
+ BDK_PCC_DEV_CON_E_GSERX(8),
+ BDK_PCC_DEV_CON_E_GSERX(9),
+ BDK_PCC_DEV_CON_E_GTI_CN8,
+ BDK_PCC_DEV_CON_E_IOBNX(0),
+ BDK_PCC_DEV_CON_E_IOBNX(1),
+ BDK_PCC_DEV_CON_E_KEY,
+ BDK_PCC_DEV_CON_E_L2C,
+ BDK_PCC_DEV_CON_E_L2C_CBCX(0),
+ BDK_PCC_DEV_CON_E_L2C_CBCX(1),
+ BDK_PCC_DEV_CON_E_L2C_CBCX(2),
+ BDK_PCC_DEV_CON_E_L2C_CBCX(3),
+ BDK_PCC_DEV_CON_E_L2C_MCIX(0),
+ BDK_PCC_DEV_CON_E_L2C_MCIX(1),
+ BDK_PCC_DEV_CON_E_L2C_MCIX(2),
+ BDK_PCC_DEV_CON_E_L2C_MCIX(3),
+ BDK_PCC_DEV_CON_E_L2C_TADX(0),
+ BDK_PCC_DEV_CON_E_L2C_TADX(1),
+ BDK_PCC_DEV_CON_E_L2C_TADX(2),
+ BDK_PCC_DEV_CON_E_L2C_TADX(3),
+ BDK_PCC_DEV_CON_E_L2C_TADX(4),
+ BDK_PCC_DEV_CON_E_L2C_TADX(5),
+ BDK_PCC_DEV_CON_E_L2C_TADX(6),
+ BDK_PCC_DEV_CON_E_L2C_TADX(7),
+ BDK_PCC_DEV_CON_E_LMCX(0),
+ BDK_PCC_DEV_CON_E_LMCX(1),
+ BDK_PCC_DEV_CON_E_LMCX(2),
+ BDK_PCC_DEV_CON_E_LMCX(3),
+ BDK_PCC_DEV_CON_E_MIO_BOOT,
+ BDK_PCC_DEV_CON_E_MIO_EMM,
+ BDK_PCC_DEV_CON_E_MIO_FUS,
+ BDK_PCC_DEV_CON_E_MIO_PTP,
+ BDK_PCC_DEV_CON_E_MIO_TWSX(0),
+ BDK_PCC_DEV_CON_E_MIO_TWSX(1),
+ BDK_PCC_DEV_CON_E_MIO_TWSX(2),
+ BDK_PCC_DEV_CON_E_MIO_TWSX(3),
+ BDK_PCC_DEV_CON_E_MIO_TWSX(4),
+ BDK_PCC_DEV_CON_E_MIO_TWSX(5),
+ BDK_PCC_DEV_CON_E_MPI,
+ BDK_PCC_DEV_CON_E_MRML,
+ BDK_PCC_DEV_CON_E_NCSI,
+ BDK_PCC_DEV_CON_E_NIC_CN88XX,
+ BDK_PCC_DEV_CON_E_OCLAX_CN8(0),
+ BDK_PCC_DEV_CON_E_OCLAX_CN8(1),
+ BDK_PCC_DEV_CON_E_OCLAX_CN8(2),
+ BDK_PCC_DEV_CON_E_OCLAX_CN8(3),
+ BDK_PCC_DEV_CON_E_OCLAX_CN8(4),
+ BDK_PCC_DEV_CON_E_OCX,
+ BDK_PCC_DEV_CON_E_PCCBR_DFA,
+ BDK_PCC_DEV_CON_E_PCCBR_MRML,
+ BDK_PCC_DEV_CON_E_PCCBR_NIC_CN88XX,
+ BDK_PCC_DEV_CON_E_PCCBR_RAD_CN88XX,
+ BDK_PCC_DEV_CON_E_PCCBR_ZIP_CN88XX,
+ BDK_PCC_DEV_CON_E_PCIERC0_CN88XX,
+ BDK_PCC_DEV_CON_E_PCIERC1_CN88XX,
+ BDK_PCC_DEV_CON_E_PCIERC2_CN88XX,
+ BDK_PCC_DEV_CON_E_PCIERC3_CN88XX,
+ BDK_PCC_DEV_CON_E_PCIERC4,
+ BDK_PCC_DEV_CON_E_PCIERC5,
+ BDK_PCC_DEV_CON_E_PEMX(0),
+ BDK_PCC_DEV_CON_E_PEMX(1),
+ BDK_PCC_DEV_CON_E_PEMX(2),
+ BDK_PCC_DEV_CON_E_PEMX(3),
+ BDK_PCC_DEV_CON_E_PEMX(4),
+ BDK_PCC_DEV_CON_E_PEMX(5),
+ BDK_PCC_DEV_CON_E_RAD_CN88XX,
+ BDK_PCC_DEV_CON_E_RNM_CN88XX,
+ BDK_PCC_DEV_CON_E_RST,
+ BDK_PCC_DEV_CON_E_SATA0_CN88XX,
+ BDK_PCC_DEV_CON_E_SATA1_CN88XX,
+ BDK_PCC_DEV_CON_E_SATA10,
+ BDK_PCC_DEV_CON_E_SATA11,
+ BDK_PCC_DEV_CON_E_SATA12,
+ BDK_PCC_DEV_CON_E_SATA13,
+ BDK_PCC_DEV_CON_E_SATA14,
+ BDK_PCC_DEV_CON_E_SATA15,
+ BDK_PCC_DEV_CON_E_SATA2,
+ BDK_PCC_DEV_CON_E_SATA3,
+ BDK_PCC_DEV_CON_E_SATA4,
+ BDK_PCC_DEV_CON_E_SATA5,
+ BDK_PCC_DEV_CON_E_SATA6,
+ BDK_PCC_DEV_CON_E_SATA7,
+ BDK_PCC_DEV_CON_E_SATA8,
+ BDK_PCC_DEV_CON_E_SATA9,
+ BDK_PCC_DEV_CON_E_SGP,
+ BDK_PCC_DEV_CON_E_SLI0_CN88XX,
+ BDK_PCC_DEV_CON_E_SLI1,
+ BDK_PCC_DEV_CON_E_SMI,
+ BDK_PCC_DEV_CON_E_SMMU0_CN8,
+ BDK_PCC_DEV_CON_E_SMMU1,
+ BDK_PCC_DEV_CON_E_SMMU2,
+ BDK_PCC_DEV_CON_E_SMMU3,
+ BDK_PCC_DEV_CON_E_TNS,
+ BDK_PCC_DEV_CON_E_UAAX_CN8(0),
+ BDK_PCC_DEV_CON_E_UAAX_CN8(1),
+ BDK_PCC_DEV_CON_E_USBHX(0),
+ BDK_PCC_DEV_CON_E_USBHX(1),
+ BDK_PCC_DEV_CON_E_VRMX(0),
+ BDK_PCC_DEV_CON_E_VRMX(1),
+ BDK_PCC_DEV_CON_E_ZIP_CN88XX,
+ 0,
+ };
+
+ int loc = 0;
+ while (INTERNAL_DEVICES_CN88XXP1_0[loc])
+ {
+ if (is_internal_cn88xxp1_0(device, INTERNAL_DEVICES_CN88XXP1_0[loc]))
+ return 1;
+ loc++;
+ }
+ return 0;
+}
+
+static int is_accessable_cn88xxp1_0(const bdk_device_t *device)
+{
+ /* Errata (ECAM-22630) ECAM function accesses can fault */
+ /* Skip internal devices that don't exists */
+ if (!is_any_internal_cn88xxp1_0(device))
+ return 0;
+
+ /* Errata (ECAM-23020) PCIERC transactions fault unless PEM is
+ out of reset. The PCIe ports don't work until the PEM is
+ turned on. Check for one of the PCIe ports */
+ int pem = -1;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC0_CN88XX))
+ pem = 0;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC1_CN88XX))
+ pem = 1;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC2_CN88XX))
+ pem = 2;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC3_CN88XX))
+ pem = 3;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC4))
+ pem = 4;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC5))
+ pem = 5;
+ if (pem != -1)
+ {
+ BDK_CSR_INIT(pem_on, device->node, BDK_PEMX_ON(pem));
+ if (!pem_on.s.pemon || !pem_on.s.pemoor)
+ return 0;
+ }
+
+ {
+ /* SATA ports should be hidden if they aren't configured at the QLM */
+ int qlm = -1;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA0_CN88XX) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA1_CN88XX) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA2) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA3))
+ qlm = 2;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA4) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA5) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA6) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA7))
+ qlm = 3;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA8) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA9) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA10) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA11))
+ qlm = 6;
+ if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA12) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA13) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA14) ||
+ is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA15))
+ qlm = 7;
+ if (qlm != -1)
+ {
+ BDK_CSR_INIT(cfg, device->node, BDK_GSERX_CFG(qlm));
+ if (!cfg.s.sata)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#endif /* Support CN88XX pass 1.0 */
+
+/**
+ * Build an ECAM config space request address for a device
+ *
+ * @param device Device being accessed
+ * @param reg Register to access
+ *
+ * @return 64bit IO address
+ */
+uint64_t __bdk_ecam_build_address(const bdk_device_t *device, int reg)
+{
+ /* CN88XX pass 1.0 had a plethora of errata related to ECAM access. This
+ checks to make sure we're allowed to access this location based on
+ the various errata */
+ if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_0) && !is_accessable_cn88xxp1_0(device))
+ return 0;
+
+ if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
+ {
+ /* Build the address */
+ union bdk_ecam_cfg_addr_s address;
+ address.u = BDK_ECAM_BAR_E_ECAMX_PF_BAR2(device->ecam);
+ address.s.node = device->node;
+ address.s.bus = device->bus;
+ address.s.func = device->dev << 3 | device->func;
+ address.s.addr = reg;
+ return address.u;
+ }
+ else
+ {
+ /* Build the address. The architects decided to make it different
+ from CN8XXX for no obvious reason */
+ union bdk_ecam_cfg_addr_s address;
+ address.u = BDK_ECAM_BAR_E_ECAMX_PF_BAR2(0);
+ address.s.node = device->node;
+ address.s.dmn = device->ecam;
+ address.s.bus = device->bus;
+ address.s.func = device->dev << 3 | device->func;
+ address.s.addr = reg;
+ return address.u;
+ }
+}
+
+/**
+ * Read from an ECAM
+ *
+ * @param device Device to read from
+ * @param reg Register to read
+ *
+ * @return Result of the read of -1 on failure
+ */
+uint32_t bdk_ecam_read32(const bdk_device_t *device, int reg)
+{
+ uint64_t address = __bdk_ecam_build_address(device, reg);
+ uint32_t result;
+ if (address)
+ result = bdk_le32_to_cpu(bdk_read64_uint32(address));
+ else
+ result = 0xffffffff;
+
+ /* Errata ECAM-22630: CN88XX pass 1.x, except pass 1.0, will return zero
+ for non-existent devices instead of ones. We look for this special case
+ for 32bit reads for reg=0 so we can scan device properly */
+ if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X) && (reg == 0) && (result == 0))
+ result = 0xffffffff;
+
+ return result;
+}
+
+/**
+ * Write to an ECAM register
+ *
+ * @param device Device to write to
+ * @param reg Register to write
+ * @param value Value to write
+ */
+void bdk_ecam_write32(const bdk_device_t *device, int reg, uint32_t value)
+{
+ uint64_t address = __bdk_ecam_build_address(device, reg);
+ if (address)
+ bdk_write64_uint32(address, bdk_cpu_to_le32(value));
+}
+