New upstream version 18.08
[deb_dpdk.git] / drivers / net / nfp / nfpcore / nfp_nffw.c
diff --git a/drivers/net/nfp/nfpcore/nfp_nffw.c b/drivers/net/nfp/nfpcore/nfp_nffw.c
new file mode 100644 (file)
index 0000000..8bec0e3
--- /dev/null
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Netronome Systems, Inc.
+ * All rights reserved.
+ */
+
+#include "nfp_cpp.h"
+#include "nfp_nffw.h"
+#include "nfp_mip.h"
+#include "nfp6000/nfp6000.h"
+#include "nfp_resource.h"
+
+/*
+ * flg_info_version = flags[0]<27:16>
+ * This is a small version counter intended only to detect if the current
+ * implementation can read the current struct. Struct changes should be very
+ * rare and as such a 12-bit counter should cover large spans of time. By the
+ * time it wraps around, we don't expect to have 4096 versions of this struct
+ * to be in use at the same time.
+ */
+static uint32_t
+nffw_res_info_version_get(const struct nfp_nffw_info_data *res)
+{
+       return (res->flags[0] >> 16) & 0xfff;
+}
+
+/* flg_init = flags[0]<0> */
+static uint32_t
+nffw_res_flg_init_get(const struct nfp_nffw_info_data *res)
+{
+       return (res->flags[0] >> 0) & 1;
+}
+
+/* loaded = loaded__mu_da__mip_off_hi<31:31> */
+static uint32_t
+nffw_fwinfo_loaded_get(const struct nffw_fwinfo *fi)
+{
+       return (fi->loaded__mu_da__mip_off_hi >> 31) & 1;
+}
+
+/* mip_cppid = mip_cppid */
+static uint32_t
+nffw_fwinfo_mip_cppid_get(const struct nffw_fwinfo *fi)
+{
+       return fi->mip_cppid;
+}
+
+/* loaded = loaded__mu_da__mip_off_hi<8:8> */
+static uint32_t
+nffw_fwinfo_mip_mu_da_get(const struct nffw_fwinfo *fi)
+{
+       return (fi->loaded__mu_da__mip_off_hi >> 8) & 1;
+}
+
+/* mip_offset = (loaded__mu_da__mip_off_hi<7:0> << 8) | mip_offset_lo */
+static uint64_t
+nffw_fwinfo_mip_offset_get(const struct nffw_fwinfo *fi)
+{
+       uint64_t mip_off_hi = fi->loaded__mu_da__mip_off_hi;
+
+       return (mip_off_hi & 0xFF) << 32 | fi->mip_offset_lo;
+}
+
+#define NFP_IMB_TGTADDRESSMODECFG_MODE_of(_x)          (((_x) >> 13) & 0x7)
+#define NFP_IMB_TGTADDRESSMODECFG_ADDRMODE             BIT(12)
+#define   NFP_IMB_TGTADDRESSMODECFG_ADDRMODE_32_BIT    0
+#define   NFP_IMB_TGTADDRESSMODECFG_ADDRMODE_40_BIT    BIT(12)
+
+static int
+nfp_mip_mu_locality_lsb(struct nfp_cpp *cpp)
+{
+       unsigned int mode, addr40;
+       uint32_t xpbaddr, imbcppat;
+       int err;
+
+       /* Hardcoded XPB IMB Base, island 0 */
+       xpbaddr = 0x000a0000 + NFP_CPP_TARGET_MU * 4;
+       err = nfp_xpb_readl(cpp, xpbaddr, &imbcppat);
+       if (err < 0)
+               return err;
+
+       mode = NFP_IMB_TGTADDRESSMODECFG_MODE_of(imbcppat);
+       addr40 = !!(imbcppat & NFP_IMB_TGTADDRESSMODECFG_ADDRMODE);
+
+       return nfp_cppat_mu_locality_lsb(mode, addr40);
+}
+
+static unsigned int
+nffw_res_fwinfos(struct nfp_nffw_info_data *fwinf, struct nffw_fwinfo **arr)
+{
+       /*
+        * For the this code, version 0 is most likely to be version 1 in this
+        * case. Since the kernel driver does not take responsibility for
+        * initialising the nfp.nffw resource, any previous code (CA firmware or
+        * userspace) that left the version 0 and did set the init flag is going
+        * to be version 1.
+        */
+       switch (nffw_res_info_version_get(fwinf)) {
+       case 0:
+       case 1:
+               *arr = &fwinf->info.v1.fwinfo[0];
+               return NFFW_FWINFO_CNT_V1;
+       case 2:
+               *arr = &fwinf->info.v2.fwinfo[0];
+               return NFFW_FWINFO_CNT_V2;
+       default:
+               *arr = NULL;
+               return 0;
+       }
+}
+
+/*
+ * nfp_nffw_info_open() - Acquire the lock on the NFFW table
+ * @cpp:       NFP CPP handle
+ *
+ * Return: 0, or -ERRNO
+ */
+struct nfp_nffw_info *
+nfp_nffw_info_open(struct nfp_cpp *cpp)
+{
+       struct nfp_nffw_info_data *fwinf;
+       struct nfp_nffw_info *state;
+       uint32_t info_ver;
+       int err;
+
+       state = malloc(sizeof(*state));
+       if (!state)
+               return NULL;
+
+       memset(state, 0, sizeof(*state));
+
+       state->res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_NFFW);
+       if (!state->res)
+               goto err_free;
+
+       fwinf = &state->fwinf;
+
+       if (sizeof(*fwinf) > nfp_resource_size(state->res))
+               goto err_release;
+
+       err = nfp_cpp_read(cpp, nfp_resource_cpp_id(state->res),
+                          nfp_resource_address(state->res),
+                          fwinf, sizeof(*fwinf));
+       if (err < (int)sizeof(*fwinf))
+               goto err_release;
+
+       if (!nffw_res_flg_init_get(fwinf))
+               goto err_release;
+
+       info_ver = nffw_res_info_version_get(fwinf);
+       if (info_ver > NFFW_INFO_VERSION_CURRENT)
+               goto err_release;
+
+       state->cpp = cpp;
+       return state;
+
+err_release:
+       nfp_resource_release(state->res);
+err_free:
+       free(state);
+       return NULL;
+}
+
+/*
+ * nfp_nffw_info_release() - Release the lock on the NFFW table
+ * @state:     NFP FW info state
+ *
+ * Return: 0, or -ERRNO
+ */
+void
+nfp_nffw_info_close(struct nfp_nffw_info *state)
+{
+       nfp_resource_release(state->res);
+       free(state);
+}
+
+/*
+ * nfp_nffw_info_fwid_first() - Return the first firmware ID in the NFFW
+ * @state:     NFP FW info state
+ *
+ * Return: First NFFW firmware info, NULL on failure
+ */
+static struct nffw_fwinfo *
+nfp_nffw_info_fwid_first(struct nfp_nffw_info *state)
+{
+       struct nffw_fwinfo *fwinfo;
+       unsigned int cnt, i;
+
+       cnt = nffw_res_fwinfos(&state->fwinf, &fwinfo);
+       if (!cnt)
+               return NULL;
+
+       for (i = 0; i < cnt; i++)
+               if (nffw_fwinfo_loaded_get(&fwinfo[i]))
+                       return &fwinfo[i];
+
+       return NULL;
+}
+
+/*
+ * nfp_nffw_info_mip_first() - Retrieve the location of the first FW's MIP
+ * @state:     NFP FW info state
+ * @cpp_id:    Pointer to the CPP ID of the MIP
+ * @off:       Pointer to the CPP Address of the MIP
+ *
+ * Return: 0, or -ERRNO
+ */
+int
+nfp_nffw_info_mip_first(struct nfp_nffw_info *state, uint32_t *cpp_id,
+                       uint64_t *off)
+{
+       struct nffw_fwinfo *fwinfo;
+
+       fwinfo = nfp_nffw_info_fwid_first(state);
+       if (!fwinfo)
+               return -EINVAL;
+
+       *cpp_id = nffw_fwinfo_mip_cppid_get(fwinfo);
+       *off = nffw_fwinfo_mip_offset_get(fwinfo);
+
+       if (nffw_fwinfo_mip_mu_da_get(fwinfo)) {
+               int locality_off;
+
+               if (NFP_CPP_ID_TARGET_of(*cpp_id) != NFP_CPP_TARGET_MU)
+                       return 0;
+
+               locality_off = nfp_mip_mu_locality_lsb(state->cpp);
+               if (locality_off < 0)
+                       return locality_off;
+
+               *off &= ~(NFP_MU_ADDR_ACCESS_TYPE_MASK << locality_off);
+               *off |= NFP_MU_ADDR_ACCESS_TYPE_DIRECT << locality_off;
+       }
+
+       return 0;
+}