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