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