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 */
41int 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, kernel_vaddr: tmp, length: sizeof(tmp));
48 if (n != sizeof(tmp))
49 return n < 0 ? n : -EIO;
50
51 *value = get_unaligned_le32(p: 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 */
64int 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(val: value, p: tmp);
71 n = nfp_cpp_write(cpp, cpp_id, address, kernel_vaddr: tmp, length: 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 */
85int 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, kernel_vaddr: tmp, length: sizeof(tmp));
92 if (n != sizeof(tmp))
93 return n < 0 ? n : -EIO;
94
95 *value = get_unaligned_le64(p: 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 */
108int 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(val: value, p: tmp);
115 n = nfp_cpp_write(cpp, cpp_id, address, kernel_vaddr: tmp, length: 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 */
123int 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 value: &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
144static 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
158int 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(id: 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: width_read, addr);
182
183 nfp_cpp_explicit_set_target(expl, cpp_id,
184 len: incr / width_read - 1, mask: byte_mask);
185 nfp_cpp_explicit_set_posted(expl, posted: 1, siga: 0, siga_mode: NFP_SIGNAL_PUSH,
186 sigb: 0, sigb_mode: 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 len: incr / width_read - 1,
193 mask: 0xff);
194 }
195
196 err = nfp_cpp_explicit_do(expl, address: addr);
197 if (err < 0)
198 goto exit_release;
199
200 err = nfp_cpp_explicit_get(expl, buff: tmp, len: incr);
201 if (err < 0)
202 goto exit_release;
203 }
204 err = len;
205exit_release:
206 nfp_cpp_explicit_release(expl);
207
208 return err;
209}
210
211int 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(id: 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: width_write, addr);
235
236 nfp_cpp_explicit_set_target(expl, cpp_id,
237 len: incr / width_write - 1, mask: byte_mask);
238 nfp_cpp_explicit_set_posted(expl, posted: 1, siga: 0, siga_mode: NFP_SIGNAL_PULL,
239 sigb: 0, sigb_mode: 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 len: incr / width_write - 1,
246 mask: 0xff);
247 }
248
249 err = nfp_cpp_explicit_put(expl, buff: tmp, len: incr);
250 if (err < 0)
251 goto exit_release;
252
253 err = nfp_cpp_explicit_do(expl, address: addr);
254 if (err < 0)
255 goto exit_release;
256 }
257 err = len;
258exit_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 */
278u8 __iomem *
279nfp_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, address: addr, size);
285 if (!*area)
286 goto err_eio;
287
288 res = nfp_cpp_area_iomem(area: *area);
289 if (!res)
290 goto err_release_free;
291
292 return res;
293
294err_release_free:
295 nfp_cpp_area_release_free(area: *area);
296err_eio:
297 return (u8 __iomem *)ERR_PTR(error: -EIO);
298}
299

source code of linux/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c