linux/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nffw.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
   3
   4/*
   5 * nfp_nffw.c
   6 * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
   7 *          Jason McMullan <jason.mcmullan@netronome.com>
   8 *          Francois H. Theron <francois.theron@netronome.com>
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/slab.h>
  13
  14#include "nfp.h"
  15#include "nfp_cpp.h"
  16#include "nfp_nffw.h"
  17#include "nfp6000/nfp6000.h"
  18
  19/* Init-CSR owner IDs for firmware map to firmware IDs which start at 4.
  20 * Lower IDs are reserved for target and loader IDs.
  21 */
  22#define NFFW_FWID_EXT   3 /* For active MEs that we didn't load. */
  23#define NFFW_FWID_BASE  4
  24
  25#define NFFW_FWID_ALL   255
  26
  27/**
  28 * NFFW_INFO_VERSION history:
  29 * 0: This was never actually used (before versioning), but it refers to
  30 *    the previous struct which had FWINFO_CNT = MEINFO_CNT = 120 that later
  31 *    changed to 200.
  32 * 1: First versioned struct, with
  33 *     FWINFO_CNT = 120
  34 *     MEINFO_CNT = 120
  35 * 2:  FWINFO_CNT = 200
  36 *     MEINFO_CNT = 200
  37 */
  38#define NFFW_INFO_VERSION_CURRENT 2
  39
  40/* Enough for all current chip families */
  41#define NFFW_MEINFO_CNT_V1 120
  42#define NFFW_FWINFO_CNT_V1 120
  43#define NFFW_MEINFO_CNT_V2 200
  44#define NFFW_FWINFO_CNT_V2 200
  45
  46/* Work in 32-bit words to make cross-platform endianness easier to handle */
  47
  48/** nfp.nffw meinfo **/
  49struct nffw_meinfo {
  50        __le32 ctxmask__fwid__meid;
  51};
  52
  53struct nffw_fwinfo {
  54        __le32 loaded__mu_da__mip_off_hi;
  55        __le32 mip_cppid; /* 0 means no MIP */
  56        __le32 mip_offset_lo;
  57};
  58
  59struct nfp_nffw_info_v1 {
  60        struct nffw_meinfo meinfo[NFFW_MEINFO_CNT_V1];
  61        struct nffw_fwinfo fwinfo[NFFW_FWINFO_CNT_V1];
  62};
  63
  64struct nfp_nffw_info_v2 {
  65        struct nffw_meinfo meinfo[NFFW_MEINFO_CNT_V2];
  66        struct nffw_fwinfo fwinfo[NFFW_FWINFO_CNT_V2];
  67};
  68
  69/** Resource: nfp.nffw main **/
  70struct nfp_nffw_info_data {
  71        __le32 flags[2];
  72        union {
  73                struct nfp_nffw_info_v1 v1;
  74                struct nfp_nffw_info_v2 v2;
  75        } info;
  76};
  77
  78struct nfp_nffw_info {
  79        struct nfp_cpp *cpp;
  80        struct nfp_resource *res;
  81
  82        struct nfp_nffw_info_data fwinf;
  83};
  84
  85/* flg_info_version = flags[0]<27:16>
  86 * This is a small version counter intended only to detect if the current
  87 * implementation can read the current struct. Struct changes should be very
  88 * rare and as such a 12-bit counter should cover large spans of time. By the
  89 * time it wraps around, we don't expect to have 4096 versions of this struct
  90 * to be in use at the same time.
  91 */
  92static u32 nffw_res_info_version_get(const struct nfp_nffw_info_data *res)
  93{
  94        return (le32_to_cpu(res->flags[0]) >> 16) & 0xfff;
  95}
  96
  97/* flg_init = flags[0]<0> */
  98static u32 nffw_res_flg_init_get(const struct nfp_nffw_info_data *res)
  99{
 100        return (le32_to_cpu(res->flags[0]) >> 0) & 1;
 101}
 102
 103/* loaded = loaded__mu_da__mip_off_hi<31:31> */
 104static u32 nffw_fwinfo_loaded_get(const struct nffw_fwinfo *fi)
 105{
 106        return (le32_to_cpu(fi->loaded__mu_da__mip_off_hi) >> 31) & 1;
 107}
 108
 109/* mip_cppid = mip_cppid */
 110static u32 nffw_fwinfo_mip_cppid_get(const struct nffw_fwinfo *fi)
 111{
 112        return le32_to_cpu(fi->mip_cppid);
 113}
 114
 115/* loaded = loaded__mu_da__mip_off_hi<8:8> */
 116static u32 nffw_fwinfo_mip_mu_da_get(const struct nffw_fwinfo *fi)
 117{
 118        return (le32_to_cpu(fi->loaded__mu_da__mip_off_hi) >> 8) & 1;
 119}
 120
 121/* mip_offset = (loaded__mu_da__mip_off_hi<7:0> << 8) | mip_offset_lo */
 122static u64 nffw_fwinfo_mip_offset_get(const struct nffw_fwinfo *fi)
 123{
 124        u64 mip_off_hi = le32_to_cpu(fi->loaded__mu_da__mip_off_hi);
 125
 126        return (mip_off_hi & 0xFF) << 32 | le32_to_cpu(fi->mip_offset_lo);
 127}
 128
 129static unsigned int
 130nffw_res_fwinfos(struct nfp_nffw_info_data *fwinf, struct nffw_fwinfo **arr)
 131{
 132        /* For the this code, version 0 is most likely to be
 133         * version 1 in this case. Since the kernel driver
 134         * does not take responsibility for initialising the
 135         * nfp.nffw resource, any previous code (CA firmware or
 136         * userspace) that left the version 0 and did set
 137         * the init flag is going to be version 1.
 138         */
 139        switch (nffw_res_info_version_get(fwinf)) {
 140        case 0:
 141        case 1:
 142                *arr = &fwinf->info.v1.fwinfo[0];
 143                return NFFW_FWINFO_CNT_V1;
 144        case 2:
 145                *arr = &fwinf->info.v2.fwinfo[0];
 146                return NFFW_FWINFO_CNT_V2;
 147        default:
 148                *arr = NULL;
 149                return 0;
 150        }
 151}
 152
 153/**
 154 * nfp_nffw_info_open() - Acquire the lock on the NFFW table
 155 * @cpp:        NFP CPP handle
 156 *
 157 * Return: pointer to nfp_nffw_info object or ERR_PTR()
 158 */
 159struct nfp_nffw_info *nfp_nffw_info_open(struct nfp_cpp *cpp)
 160{
 161        struct nfp_nffw_info_data *fwinf;
 162        struct nfp_nffw_info *state;
 163        u32 info_ver;
 164        int err;
 165
 166        state = kzalloc(sizeof(*state), GFP_KERNEL);
 167        if (!state)
 168                return ERR_PTR(-ENOMEM);
 169
 170        state->res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_NFFW);
 171        if (IS_ERR(state->res))
 172                goto err_free;
 173
 174        fwinf = &state->fwinf;
 175
 176        if (sizeof(*fwinf) > nfp_resource_size(state->res))
 177                goto err_release;
 178
 179        err = nfp_cpp_read(cpp, nfp_resource_cpp_id(state->res),
 180                           nfp_resource_address(state->res),
 181                           fwinf, sizeof(*fwinf));
 182        if (err < (int)sizeof(*fwinf))
 183                goto err_release;
 184
 185        if (!nffw_res_flg_init_get(fwinf))
 186                goto err_release;
 187
 188        info_ver = nffw_res_info_version_get(fwinf);
 189        if (info_ver > NFFW_INFO_VERSION_CURRENT)
 190                goto err_release;
 191
 192        state->cpp = cpp;
 193        return state;
 194
 195err_release:
 196        nfp_resource_release(state->res);
 197err_free:
 198        kfree(state);
 199        return ERR_PTR(-EIO);
 200}
 201
 202/**
 203 * nfp_nffw_info_close() - Release the lock on the NFFW table and free state
 204 * @state:      NFP FW info state
 205 */
 206void nfp_nffw_info_close(struct nfp_nffw_info *state)
 207{
 208        nfp_resource_release(state->res);
 209        kfree(state);
 210}
 211
 212/**
 213 * nfp_nffw_info_fwid_first() - Return the first firmware ID in the NFFW
 214 * @state:      NFP FW info state
 215 *
 216 * Return: First NFFW firmware info, NULL on failure
 217 */
 218static struct nffw_fwinfo *nfp_nffw_info_fwid_first(struct nfp_nffw_info *state)
 219{
 220        struct nffw_fwinfo *fwinfo;
 221        unsigned int cnt, i;
 222
 223        cnt = nffw_res_fwinfos(&state->fwinf, &fwinfo);
 224        if (!cnt)
 225                return NULL;
 226
 227        for (i = 0; i < cnt; i++)
 228                if (nffw_fwinfo_loaded_get(&fwinfo[i]))
 229                        return &fwinfo[i];
 230
 231        return NULL;
 232}
 233
 234/**
 235 * nfp_nffw_info_mip_first() - Retrieve the location of the first FW's MIP
 236 * @state:      NFP FW info state
 237 * @cpp_id:     Pointer to the CPP ID of the MIP
 238 * @off:        Pointer to the CPP Address of the MIP
 239 *
 240 * Return: 0, or -ERRNO
 241 */
 242int nfp_nffw_info_mip_first(struct nfp_nffw_info *state, u32 *cpp_id, u64 *off)
 243{
 244        struct nffw_fwinfo *fwinfo;
 245
 246        fwinfo = nfp_nffw_info_fwid_first(state);
 247        if (!fwinfo)
 248                return -EINVAL;
 249
 250        *cpp_id = nffw_fwinfo_mip_cppid_get(fwinfo);
 251        *off = nffw_fwinfo_mip_offset_get(fwinfo);
 252
 253        if (nffw_fwinfo_mip_mu_da_get(fwinfo)) {
 254                int locality_off = nfp_cpp_mu_locality_lsb(state->cpp);
 255
 256                *off &= ~(NFP_MU_ADDR_ACCESS_TYPE_MASK << locality_off);
 257                *off |= NFP_MU_ADDR_ACCESS_TYPE_DIRECT << locality_off;
 258        }
 259
 260        return 0;
 261}
 262