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 | */ |
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> */ |
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> */ |
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 */ |
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> */ |
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 */ |
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 |
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(res: 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 | */ |
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(size: sizeof(*state), GFP_KERNEL); |
167 | if (!state) |
168 | return ERR_PTR(error: -ENOMEM); |
169 | |
170 | state->res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_NFFW); |
171 | if (IS_ERR(ptr: state->res)) |
172 | goto err_free; |
173 | |
174 | fwinf = &state->fwinf; |
175 | |
176 | if (sizeof(*fwinf) > nfp_resource_size(res: state->res)) |
177 | goto err_release; |
178 | |
179 | err = nfp_cpp_read(cpp, cpp_id: nfp_resource_cpp_id(res: state->res), |
180 | address: nfp_resource_address(res: state->res), |
181 | kernel_vaddr: fwinf, length: sizeof(*fwinf)); |
182 | if (err < (int)sizeof(*fwinf)) |
183 | goto err_release; |
184 | |
185 | if (!nffw_res_flg_init_get(res: fwinf)) |
186 | goto err_release; |
187 | |
188 | info_ver = nffw_res_info_version_get(res: 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(res: state->res); |
197 | err_free: |
198 | kfree(objp: state); |
199 | return ERR_PTR(error: -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 | */ |
206 | void nfp_nffw_info_close(struct nfp_nffw_info *state) |
207 | { |
208 | nfp_resource_release(res: state->res); |
209 | kfree(objp: 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 | */ |
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(fwinf: &state->fwinf, arr: &fwinfo); |
224 | if (!cnt) |
225 | return NULL; |
226 | |
227 | for (i = 0; i < cnt; i++) |
228 | if (nffw_fwinfo_loaded_get(fi: &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 | */ |
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(fi: fwinfo); |
251 | *off = nffw_fwinfo_mip_offset_get(fi: fwinfo); |
252 | |
253 | if (nffw_fwinfo_mip_mu_da_get(fi: fwinfo)) { |
254 | int locality_off = nfp_cpp_mu_locality_lsb(cpp: 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 | |