Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / netronome / nfp / nfpcore / nfp_cpplib.c
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 }