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