1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2015-2018 Netronome Systems, Inc. */ |
3 | |
4 | /* |
5 | * nfp_cpplib.c |
6 | * Library of functions to access the NFP's CPP bus |
7 | * Authors: Jakub Kicinski <jakub.kicinski@netronome.com> |
8 | * Jason McMullan <jason.mcmullan@netronome.com> |
9 | * Rolf Neugebauer <rolf.neugebauer@netronome.com> |
10 | */ |
11 | |
12 | #include <asm/unaligned.h> |
13 | #include <linux/bitfield.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/sched.h> |
19 | |
20 | #include "nfp_cpp.h" |
21 | #include "nfp6000/nfp6000.h" |
22 | #include "nfp6000/nfp_xpb.h" |
23 | |
24 | /* NFP6000 PL */ |
25 | #define NFP_PL_DEVICE_PART_NFP6000 0x6200 |
26 | #define NFP_PL_DEVICE_ID 0x00000004 |
27 | #define NFP_PL_DEVICE_ID_MASK GENMASK(7, 0) |
28 | #define NFP_PL_DEVICE_PART_MASK GENMASK(31, 16) |
29 | #define NFP_PL_DEVICE_MODEL_MASK (NFP_PL_DEVICE_PART_MASK | \ |
30 | NFP_PL_DEVICE_ID_MASK) |
31 | |
32 | /** |
33 | * nfp_cpp_readl() - Read a u32 word from a CPP location |
34 | * @cpp: CPP device handle |
35 | * @cpp_id: CPP ID for operation |
36 | * @address: Address for operation |
37 | * @value: Pointer to read buffer |
38 | * |
39 | * Return: 0 on success, or -ERRNO |
40 | */ |
41 | int nfp_cpp_readl(struct nfp_cpp *cpp, u32 cpp_id, |
42 | unsigned long long address, u32 *value) |
43 | { |
44 | u8 tmp[4]; |
45 | int n; |
46 | |
47 | n = nfp_cpp_read(cpp, cpp_id, address, kernel_vaddr: tmp, length: sizeof(tmp)); |
48 | if (n != sizeof(tmp)) |
49 | return n < 0 ? n : -EIO; |
50 | |
51 | *value = get_unaligned_le32(p: tmp); |
52 | return 0; |
53 | } |
54 | |
55 | /** |
56 | * nfp_cpp_writel() - Write a u32 word to a CPP location |
57 | * @cpp: CPP device handle |
58 | * @cpp_id: CPP ID for operation |
59 | * @address: Address for operation |
60 | * @value: Value to write |
61 | * |
62 | * Return: 0 on success, or -ERRNO |
63 | */ |
64 | int nfp_cpp_writel(struct nfp_cpp *cpp, u32 cpp_id, |
65 | unsigned long long address, u32 value) |
66 | { |
67 | u8 tmp[4]; |
68 | int n; |
69 | |
70 | put_unaligned_le32(val: value, p: tmp); |
71 | n = nfp_cpp_write(cpp, cpp_id, address, kernel_vaddr: tmp, length: sizeof(tmp)); |
72 | |
73 | return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO; |
74 | } |
75 | |
76 | /** |
77 | * nfp_cpp_readq() - Read a u64 word from a CPP location |
78 | * @cpp: CPP device handle |
79 | * @cpp_id: CPP ID for operation |
80 | * @address: Address for operation |
81 | * @value: Pointer to read buffer |
82 | * |
83 | * Return: 0 on success, or -ERRNO |
84 | */ |
85 | int nfp_cpp_readq(struct nfp_cpp *cpp, u32 cpp_id, |
86 | unsigned long long address, u64 *value) |
87 | { |
88 | u8 tmp[8]; |
89 | int n; |
90 | |
91 | n = nfp_cpp_read(cpp, cpp_id, address, kernel_vaddr: tmp, length: sizeof(tmp)); |
92 | if (n != sizeof(tmp)) |
93 | return n < 0 ? n : -EIO; |
94 | |
95 | *value = get_unaligned_le64(p: tmp); |
96 | return 0; |
97 | } |
98 | |
99 | /** |
100 | * nfp_cpp_writeq() - Write a u64 word to a CPP location |
101 | * @cpp: CPP device handle |
102 | * @cpp_id: CPP ID for operation |
103 | * @address: Address for operation |
104 | * @value: Value to write |
105 | * |
106 | * Return: 0 on success, or -ERRNO |
107 | */ |
108 | int nfp_cpp_writeq(struct nfp_cpp *cpp, u32 cpp_id, |
109 | unsigned long long address, u64 value) |
110 | { |
111 | u8 tmp[8]; |
112 | int n; |
113 | |
114 | put_unaligned_le64(val: value, p: tmp); |
115 | n = nfp_cpp_write(cpp, cpp_id, address, kernel_vaddr: tmp, length: sizeof(tmp)); |
116 | |
117 | return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO; |
118 | } |
119 | |
120 | /* NOTE: This code should not use nfp_xpb_* functions, |
121 | * as those are model-specific |
122 | */ |
123 | int nfp_cpp_model_autodetect(struct nfp_cpp *cpp, u32 *model) |
124 | { |
125 | u32 reg; |
126 | int err; |
127 | |
128 | err = nfp_xpb_readl(cpp, NFP_XPB_DEVICE(1, 1, 16) + NFP_PL_DEVICE_ID, |
129 | value: ®); |
130 | if (err < 0) |
131 | return err; |
132 | |
133 | *model = reg & NFP_PL_DEVICE_MODEL_MASK; |
134 | /* Disambiguate the NFP4000/NFP5000/NFP6000 chips */ |
135 | if (FIELD_GET(NFP_PL_DEVICE_PART_MASK, reg) == |
136 | NFP_PL_DEVICE_PART_NFP6000) { |
137 | if (*model & NFP_PL_DEVICE_ID_MASK) |
138 | *model -= 0x10; |
139 | } |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | static u8 nfp_bytemask(int width, u64 addr) |
145 | { |
146 | if (width == 8) |
147 | return 0xff; |
148 | else if (width == 4) |
149 | return 0x0f << (addr & 4); |
150 | else if (width == 2) |
151 | return 0x03 << (addr & 6); |
152 | else if (width == 1) |
153 | return 0x01 << (addr & 7); |
154 | else |
155 | return 0; |
156 | } |
157 | |
158 | int nfp_cpp_explicit_read(struct nfp_cpp *cpp, u32 cpp_id, |
159 | u64 addr, void *buff, size_t len, int width_read) |
160 | { |
161 | struct nfp_cpp_explicit *expl; |
162 | char *tmp = buff; |
163 | int err, i, incr; |
164 | u8 byte_mask; |
165 | |
166 | if (len & (width_read - 1)) |
167 | return -EINVAL; |
168 | |
169 | expl = nfp_cpp_explicit_acquire(cpp); |
170 | if (!expl) |
171 | return -EBUSY; |
172 | |
173 | incr = min_t(int, 16 * width_read, 128); |
174 | incr = min_t(int, incr, len); |
175 | |
176 | /* Translate a NFP_CPP_ACTION_RW to action 0 */ |
177 | if (NFP_CPP_ID_ACTION_of(id: cpp_id) == NFP_CPP_ACTION_RW) |
178 | cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 0, |
179 | NFP_CPP_ID_TOKEN_of(cpp_id)); |
180 | |
181 | byte_mask = nfp_bytemask(width: width_read, addr); |
182 | |
183 | nfp_cpp_explicit_set_target(expl, cpp_id, |
184 | len: incr / width_read - 1, mask: byte_mask); |
185 | nfp_cpp_explicit_set_posted(expl, posted: 1, siga: 0, siga_mode: NFP_SIGNAL_PUSH, |
186 | sigb: 0, sigb_mode: NFP_SIGNAL_NONE); |
187 | |
188 | for (i = 0; i < len; i += incr, addr += incr, tmp += incr) { |
189 | if (i + incr > len) { |
190 | incr = len - i; |
191 | nfp_cpp_explicit_set_target(expl, cpp_id, |
192 | len: incr / width_read - 1, |
193 | mask: 0xff); |
194 | } |
195 | |
196 | err = nfp_cpp_explicit_do(expl, address: addr); |
197 | if (err < 0) |
198 | goto exit_release; |
199 | |
200 | err = nfp_cpp_explicit_get(expl, buff: tmp, len: incr); |
201 | if (err < 0) |
202 | goto exit_release; |
203 | } |
204 | err = len; |
205 | exit_release: |
206 | nfp_cpp_explicit_release(expl); |
207 | |
208 | return err; |
209 | } |
210 | |
211 | int nfp_cpp_explicit_write(struct nfp_cpp *cpp, u32 cpp_id, u64 addr, |
212 | const void *buff, size_t len, int width_write) |
213 | { |
214 | struct nfp_cpp_explicit *expl; |
215 | const char *tmp = buff; |
216 | int err, i, incr; |
217 | u8 byte_mask; |
218 | |
219 | if (len & (width_write - 1)) |
220 | return -EINVAL; |
221 | |
222 | expl = nfp_cpp_explicit_acquire(cpp); |
223 | if (!expl) |
224 | return -EBUSY; |
225 | |
226 | incr = min_t(int, 16 * width_write, 128); |
227 | incr = min_t(int, incr, len); |
228 | |
229 | /* Translate a NFP_CPP_ACTION_RW to action 1 */ |
230 | if (NFP_CPP_ID_ACTION_of(id: cpp_id) == NFP_CPP_ACTION_RW) |
231 | cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 1, |
232 | NFP_CPP_ID_TOKEN_of(cpp_id)); |
233 | |
234 | byte_mask = nfp_bytemask(width: width_write, addr); |
235 | |
236 | nfp_cpp_explicit_set_target(expl, cpp_id, |
237 | len: incr / width_write - 1, mask: byte_mask); |
238 | nfp_cpp_explicit_set_posted(expl, posted: 1, siga: 0, siga_mode: NFP_SIGNAL_PULL, |
239 | sigb: 0, sigb_mode: NFP_SIGNAL_NONE); |
240 | |
241 | for (i = 0; i < len; i += incr, addr += incr, tmp += incr) { |
242 | if (i + incr > len) { |
243 | incr = len - i; |
244 | nfp_cpp_explicit_set_target(expl, cpp_id, |
245 | len: incr / width_write - 1, |
246 | mask: 0xff); |
247 | } |
248 | |
249 | err = nfp_cpp_explicit_put(expl, buff: tmp, len: incr); |
250 | if (err < 0) |
251 | goto exit_release; |
252 | |
253 | err = nfp_cpp_explicit_do(expl, address: addr); |
254 | if (err < 0) |
255 | goto exit_release; |
256 | } |
257 | err = len; |
258 | exit_release: |
259 | nfp_cpp_explicit_release(expl); |
260 | |
261 | return err; |
262 | } |
263 | |
264 | /** |
265 | * nfp_cpp_map_area() - Helper function to map an area |
266 | * @cpp: NFP CPP handler |
267 | * @name: Name for the area |
268 | * @cpp_id: CPP ID for operation |
269 | * @addr: CPP address |
270 | * @size: Size of the area |
271 | * @area: Area handle (output) |
272 | * |
273 | * Map an area of IOMEM access. To undo the effect of this function call |
274 | * @nfp_cpp_area_release_free(*area). |
275 | * |
276 | * Return: Pointer to memory mapped area or ERR_PTR |
277 | */ |
278 | u8 __iomem * |
279 | nfp_cpp_map_area(struct nfp_cpp *cpp, const char *name, u32 cpp_id, u64 addr, |
280 | unsigned long size, struct nfp_cpp_area **area) |
281 | { |
282 | u8 __iomem *res; |
283 | |
284 | *area = nfp_cpp_area_alloc_acquire(cpp, name, cpp_id, address: addr, size); |
285 | if (!*area) |
286 | goto err_eio; |
287 | |
288 | res = nfp_cpp_area_iomem(area: *area); |
289 | if (!res) |
290 | goto err_release_free; |
291 | |
292 | return res; |
293 | |
294 | err_release_free: |
295 | nfp_cpp_area_release_free(area: *area); |
296 | err_eio: |
297 | return (u8 __iomem *)ERR_PTR(error: -EIO); |
298 | } |
299 | |