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