1 /*
2  * Copyright (C) 2015-2017 Netronome Systems, Inc.
3  *
4  * This software is dual licensed under the GNU General License Version 2,
5  * June 1991 as shown in the file COPYING in the top-level directory of this
6  * source tree or the BSD 2-Clause License provided below.  You have the
7  * option to license this software under the complete terms of either license.
8  *
9  * The BSD 2-Clause License:
10  *
11  *     Redistribution and use in source and binary forms, with or
12  *     without modification, are permitted provided that the following
13  *     conditions are met:
14  *
15  *      1. Redistributions of source code must retain the above
16  *         copyright notice, this list of conditions and the following
17  *         disclaimer.
18  *
19  *      2. Redistributions in binary form must reproduce the above
20  *         copyright notice, this list of conditions and the following
21  *         disclaimer in the documentation and/or other materials
22  *         provided with the distribution.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31  * SOFTWARE.
32  */
33 
34 /*
35  * nfp_nffw.c
36  * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
37  *          Jason McMullan <jason.mcmullan@netronome.com>
38  *          Francois H. Theron <francois.theron@netronome.com>
39  */
40 
41 #include <linux/kernel.h>
42 #include <linux/slab.h>
43 
44 #include "nfp.h"
45 #include "nfp_cpp.h"
46 #include "nfp_nffw.h"
47 #include "nfp6000/nfp6000.h"
48 
49 /* Init-CSR owner IDs for firmware map to firmware IDs which start at 4.
50  * Lower IDs are reserved for target and loader IDs.
51  */
52 #define NFFW_FWID_EXT   3 /* For active MEs that we didn't load. */
53 #define NFFW_FWID_BASE  4
54 
55 #define NFFW_FWID_ALL   255
56 
57 /**
58  * NFFW_INFO_VERSION history:
59  * 0: This was never actually used (before versioning), but it refers to
60  *    the previous struct which had FWINFO_CNT = MEINFO_CNT = 120 that later
61  *    changed to 200.
62  * 1: First versioned struct, with
63  *     FWINFO_CNT = 120
64  *     MEINFO_CNT = 120
65  * 2:  FWINFO_CNT = 200
66  *     MEINFO_CNT = 200
67  */
68 #define NFFW_INFO_VERSION_CURRENT 2
69 
70 /* Enough for all current chip families */
71 #define NFFW_MEINFO_CNT_V1 120
72 #define NFFW_FWINFO_CNT_V1 120
73 #define NFFW_MEINFO_CNT_V2 200
74 #define NFFW_FWINFO_CNT_V2 200
75 
76 /* Work in 32-bit words to make cross-platform endianness easier to handle */
77 
78 /** nfp.nffw meinfo **/
79 struct nffw_meinfo {
80 	__le32 ctxmask__fwid__meid;
81 };
82 
83 struct nffw_fwinfo {
84 	__le32 loaded__mu_da__mip_off_hi;
85 	__le32 mip_cppid; /* 0 means no MIP */
86 	__le32 mip_offset_lo;
87 };
88 
89 struct nfp_nffw_info_v1 {
90 	struct nffw_meinfo meinfo[NFFW_MEINFO_CNT_V1];
91 	struct nffw_fwinfo fwinfo[NFFW_FWINFO_CNT_V1];
92 };
93 
94 struct nfp_nffw_info_v2 {
95 	struct nffw_meinfo meinfo[NFFW_MEINFO_CNT_V2];
96 	struct nffw_fwinfo fwinfo[NFFW_FWINFO_CNT_V2];
97 };
98 
99 /** Resource: nfp.nffw main **/
100 struct nfp_nffw_info_data {
101 	__le32 flags[2];
102 	union {
103 		struct nfp_nffw_info_v1 v1;
104 		struct nfp_nffw_info_v2 v2;
105 	} info;
106 };
107 
108 struct nfp_nffw_info {
109 	struct nfp_cpp *cpp;
110 	struct nfp_resource *res;
111 
112 	struct nfp_nffw_info_data fwinf;
113 };
114 
115 /* flg_info_version = flags[0]<27:16>
116  * This is a small version counter intended only to detect if the current
117  * implementation can read the current struct. Struct changes should be very
118  * rare and as such a 12-bit counter should cover large spans of time. By the
119  * time it wraps around, we don't expect to have 4096 versions of this struct
120  * to be in use at the same time.
121  */
nffw_res_info_version_get(const struct nfp_nffw_info_data * res)122 static u32 nffw_res_info_version_get(const struct nfp_nffw_info_data *res)
123 {
124 	return (le32_to_cpu(res->flags[0]) >> 16) & 0xfff;
125 }
126 
127 /* flg_init = flags[0]<0> */
nffw_res_flg_init_get(const struct nfp_nffw_info_data * res)128 static u32 nffw_res_flg_init_get(const struct nfp_nffw_info_data *res)
129 {
130 	return (le32_to_cpu(res->flags[0]) >> 0) & 1;
131 }
132 
133 /* loaded = loaded__mu_da__mip_off_hi<31:31> */
nffw_fwinfo_loaded_get(const struct nffw_fwinfo * fi)134 static u32 nffw_fwinfo_loaded_get(const struct nffw_fwinfo *fi)
135 {
136 	return (le32_to_cpu(fi->loaded__mu_da__mip_off_hi) >> 31) & 1;
137 }
138 
139 /* mip_cppid = mip_cppid */
nffw_fwinfo_mip_cppid_get(const struct nffw_fwinfo * fi)140 static u32 nffw_fwinfo_mip_cppid_get(const struct nffw_fwinfo *fi)
141 {
142 	return le32_to_cpu(fi->mip_cppid);
143 }
144 
145 /* loaded = loaded__mu_da__mip_off_hi<8:8> */
nffw_fwinfo_mip_mu_da_get(const struct nffw_fwinfo * fi)146 static u32 nffw_fwinfo_mip_mu_da_get(const struct nffw_fwinfo *fi)
147 {
148 	return (le32_to_cpu(fi->loaded__mu_da__mip_off_hi) >> 8) & 1;
149 }
150 
151 /* mip_offset = (loaded__mu_da__mip_off_hi<7:0> << 8) | mip_offset_lo */
nffw_fwinfo_mip_offset_get(const struct nffw_fwinfo * fi)152 static u64 nffw_fwinfo_mip_offset_get(const struct nffw_fwinfo *fi)
153 {
154 	u64 mip_off_hi = le32_to_cpu(fi->loaded__mu_da__mip_off_hi);
155 
156 	return (mip_off_hi & 0xFF) << 32 | le32_to_cpu(fi->mip_offset_lo);
157 }
158 
159 #define NFP_IMB_TGTADDRESSMODECFG_MODE_of(_x)		(((_x) >> 13) & 0x7)
160 #define NFP_IMB_TGTADDRESSMODECFG_ADDRMODE		BIT(12)
161 #define   NFP_IMB_TGTADDRESSMODECFG_ADDRMODE_32_BIT	0
162 #define   NFP_IMB_TGTADDRESSMODECFG_ADDRMODE_40_BIT	BIT(12)
163 
nfp_mip_mu_locality_lsb(struct nfp_cpp * cpp)164 static int nfp_mip_mu_locality_lsb(struct nfp_cpp *cpp)
165 {
166 	unsigned int mode, addr40;
167 	u32 xpbaddr, imbcppat;
168 	int err;
169 
170 	/* Hardcoded XPB IMB Base, island 0 */
171 	xpbaddr = 0x000a0000 + NFP_CPP_TARGET_MU * 4;
172 	err = nfp_xpb_readl(cpp, xpbaddr, &imbcppat);
173 	if (err < 0)
174 		return err;
175 
176 	mode = NFP_IMB_TGTADDRESSMODECFG_MODE_of(imbcppat);
177 	addr40 = !!(imbcppat & NFP_IMB_TGTADDRESSMODECFG_ADDRMODE);
178 
179 	return nfp_cppat_mu_locality_lsb(mode, addr40);
180 }
181 
182 static unsigned int
nffw_res_fwinfos(struct nfp_nffw_info_data * fwinf,struct nffw_fwinfo ** arr)183 nffw_res_fwinfos(struct nfp_nffw_info_data *fwinf, struct nffw_fwinfo **arr)
184 {
185 	/* For the this code, version 0 is most likely to be
186 	 * version 1 in this case. Since the kernel driver
187 	 * does not take responsibility for initialising the
188 	 * nfp.nffw resource, any previous code (CA firmware or
189 	 * userspace) that left the version 0 and did set
190 	 * the init flag is going to be version 1.
191 	 */
192 	switch (nffw_res_info_version_get(fwinf)) {
193 	case 0:
194 	case 1:
195 		*arr = &fwinf->info.v1.fwinfo[0];
196 		return NFFW_FWINFO_CNT_V1;
197 	case 2:
198 		*arr = &fwinf->info.v2.fwinfo[0];
199 		return NFFW_FWINFO_CNT_V2;
200 	default:
201 		*arr = NULL;
202 		return 0;
203 	}
204 }
205 
206 /**
207  * nfp_nffw_info_open() - Acquire the lock on the NFFW table
208  * @cpp:	NFP CPP handle
209  *
210  * Return: pointer to nfp_nffw_info object or ERR_PTR()
211  */
nfp_nffw_info_open(struct nfp_cpp * cpp)212 struct nfp_nffw_info *nfp_nffw_info_open(struct nfp_cpp *cpp)
213 {
214 	struct nfp_nffw_info_data *fwinf;
215 	struct nfp_nffw_info *state;
216 	u32 info_ver;
217 	int err;
218 
219 	state = kzalloc(sizeof(*state), GFP_KERNEL);
220 	if (!state)
221 		return ERR_PTR(-ENOMEM);
222 
223 	state->res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_NFFW);
224 	if (IS_ERR(state->res))
225 		goto err_free;
226 
227 	fwinf = &state->fwinf;
228 
229 	if (sizeof(*fwinf) > nfp_resource_size(state->res))
230 		goto err_release;
231 
232 	err = nfp_cpp_read(cpp, nfp_resource_cpp_id(state->res),
233 			   nfp_resource_address(state->res),
234 			   fwinf, sizeof(*fwinf));
235 	if (err < (int)sizeof(*fwinf))
236 		goto err_release;
237 
238 	if (!nffw_res_flg_init_get(fwinf))
239 		goto err_release;
240 
241 	info_ver = nffw_res_info_version_get(fwinf);
242 	if (info_ver > NFFW_INFO_VERSION_CURRENT)
243 		goto err_release;
244 
245 	state->cpp = cpp;
246 	return state;
247 
248 err_release:
249 	nfp_resource_release(state->res);
250 err_free:
251 	kfree(state);
252 	return ERR_PTR(-EIO);
253 }
254 
255 /**
256  * nfp_nffw_info_close() - Release the lock on the NFFW table and free state
257  * @state:	NFP FW info state
258  */
nfp_nffw_info_close(struct nfp_nffw_info * state)259 void nfp_nffw_info_close(struct nfp_nffw_info *state)
260 {
261 	nfp_resource_release(state->res);
262 	kfree(state);
263 }
264 
265 /**
266  * nfp_nffw_info_fwid_first() - Return the first firmware ID in the NFFW
267  * @state:	NFP FW info state
268  *
269  * Return: First NFFW firmware info, NULL on failure
270  */
nfp_nffw_info_fwid_first(struct nfp_nffw_info * state)271 static struct nffw_fwinfo *nfp_nffw_info_fwid_first(struct nfp_nffw_info *state)
272 {
273 	struct nffw_fwinfo *fwinfo;
274 	unsigned int cnt, i;
275 
276 	cnt = nffw_res_fwinfos(&state->fwinf, &fwinfo);
277 	if (!cnt)
278 		return NULL;
279 
280 	for (i = 0; i < cnt; i++)
281 		if (nffw_fwinfo_loaded_get(&fwinfo[i]))
282 			return &fwinfo[i];
283 
284 	return NULL;
285 }
286 
287 /**
288  * nfp_nffw_info_mip_first() - Retrieve the location of the first FW's MIP
289  * @state:	NFP FW info state
290  * @cpp_id:	Pointer to the CPP ID of the MIP
291  * @off:	Pointer to the CPP Address of the MIP
292  *
293  * Return: 0, or -ERRNO
294  */
nfp_nffw_info_mip_first(struct nfp_nffw_info * state,u32 * cpp_id,u64 * off)295 int nfp_nffw_info_mip_first(struct nfp_nffw_info *state, u32 *cpp_id, u64 *off)
296 {
297 	struct nffw_fwinfo *fwinfo;
298 
299 	fwinfo = nfp_nffw_info_fwid_first(state);
300 	if (!fwinfo)
301 		return -EINVAL;
302 
303 	*cpp_id = nffw_fwinfo_mip_cppid_get(fwinfo);
304 	*off = nffw_fwinfo_mip_offset_get(fwinfo);
305 
306 	if (nffw_fwinfo_mip_mu_da_get(fwinfo)) {
307 		int locality_off;
308 
309 		if (NFP_CPP_ID_TARGET_of(*cpp_id) != NFP_CPP_TARGET_MU)
310 			return 0;
311 
312 		locality_off = nfp_mip_mu_locality_lsb(state->cpp);
313 		if (locality_off < 0)
314 			return locality_off;
315 
316 		*off &= ~(NFP_MU_ADDR_ACCESS_TYPE_MASK << locality_off);
317 		*off |= NFP_MU_ADDR_ACCESS_TYPE_DIRECT << locality_off;
318 	}
319 
320 	return 0;
321 }
322