nfp_nffw.c (6697B)
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}