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