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  */
nfp_cpp_readl(struct nfp_cpp * cpp,u32 cpp_id,unsigned long long address,u32 * value)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, tmp, sizeof(tmp));
48 	if (n != sizeof(tmp))
49 		return n < 0 ? n : -EIO;
50 
51 	*value = get_unaligned_le32(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  */
nfp_cpp_writel(struct nfp_cpp * cpp,u32 cpp_id,unsigned long long address,u32 value)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(value, tmp);
71 	n = nfp_cpp_write(cpp, cpp_id, address, tmp, 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  */
nfp_cpp_readq(struct nfp_cpp * cpp,u32 cpp_id,unsigned long long address,u64 * value)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, tmp, sizeof(tmp));
92 	if (n != sizeof(tmp))
93 		return n < 0 ? n : -EIO;
94 
95 	*value = get_unaligned_le64(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  */
nfp_cpp_writeq(struct nfp_cpp * cpp,u32 cpp_id,unsigned long long address,u64 value)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(value, tmp);
115 	n = nfp_cpp_write(cpp, cpp_id, address, tmp, 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  */
nfp_cpp_model_autodetect(struct nfp_cpp * cpp,u32 * model)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 			    &reg);
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 
nfp_bytemask(int width,u64 addr)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 
nfp_cpp_explicit_read(struct nfp_cpp * cpp,u32 cpp_id,u64 addr,void * buff,size_t len,int width_read)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(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_read, addr);
182 
183 	nfp_cpp_explicit_set_target(expl, cpp_id,
184 				    incr / width_read - 1, byte_mask);
185 	nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PUSH,
186 				    0, 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 						    incr / width_read - 1,
193 						    0xff);
194 		}
195 
196 		err = nfp_cpp_explicit_do(expl, addr);
197 		if (err < 0)
198 			goto exit_release;
199 
200 		err = nfp_cpp_explicit_get(expl, tmp, 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 
nfp_cpp_explicit_write(struct nfp_cpp * cpp,u32 cpp_id,u64 addr,const void * buff,size_t len,int width_write)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(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_write, addr);
235 
236 	nfp_cpp_explicit_set_target(expl, cpp_id,
237 				    incr / width_write - 1, byte_mask);
238 	nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PULL,
239 				    0, 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 						    incr / width_write - 1,
246 						    0xff);
247 		}
248 
249 		err = nfp_cpp_explicit_put(expl, tmp, incr);
250 		if (err < 0)
251 			goto exit_release;
252 
253 		err = nfp_cpp_explicit_do(expl, 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 *
nfp_cpp_map_area(struct nfp_cpp * cpp,const char * name,u32 cpp_id,u64 addr,unsigned long size,struct nfp_cpp_area ** area)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, addr, size);
285 	if (!*area)
286 		goto err_eio;
287 
288 	res = nfp_cpp_area_iomem(*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);
296 err_eio:
297 	return (u8 __iomem *)ERR_PTR(-EIO);
298 }
299