• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 **/
49 struct nffw_meinfo {
50 	__le32 ctxmask__fwid__meid;
51 };
52 
53 struct 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 
59 struct nfp_nffw_info_v1 {
60 	struct nffw_meinfo meinfo[NFFW_MEINFO_CNT_V1];
61 	struct nffw_fwinfo fwinfo[NFFW_FWINFO_CNT_V1];
62 };
63 
64 struct 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 **/
70 struct 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 
78 struct 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  */
nffw_res_info_version_get(const struct nfp_nffw_info_data * res)92 static 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> */
nffw_res_flg_init_get(const struct nfp_nffw_info_data * res)98 static 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> */
nffw_fwinfo_loaded_get(const struct nffw_fwinfo * fi)104 static 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 */
nffw_fwinfo_mip_cppid_get(const struct nffw_fwinfo * fi)110 static 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> */
nffw_fwinfo_mip_mu_da_get(const struct nffw_fwinfo * fi)116 static 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 */
nffw_fwinfo_mip_offset_get(const struct nffw_fwinfo * fi)122 static 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 
129 static unsigned int
nffw_res_fwinfos(struct nfp_nffw_info_data * fwinf,struct nffw_fwinfo ** arr)130 nffw_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  */
nfp_nffw_info_open(struct nfp_cpp * cpp)159 struct 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 
195 err_release:
196 	nfp_resource_release(state->res);
197 err_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  */
nfp_nffw_info_close(struct nfp_nffw_info * state)206 void 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  */
nfp_nffw_info_fwid_first(struct nfp_nffw_info * state)218 static 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  */
nfp_nffw_info_mip_first(struct nfp_nffw_info * state,u32 * cpp_id,u64 * off)242 int 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