net/nfp: add NSP symbol resolution command
[dpdk.git] / drivers / net / nfp / nfp_nspu.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/file.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9
10 #include <rte_log.h>
11
12 #include "nfp_nfpu.h"
13
14 #define CFG_EXP_BAR_ADDR_SZ     1
15 #define CFG_EXP_BAR_MAP_TYPE    1
16
17 #define EXP_BAR_TARGET_SHIFT     23
18 #define EXP_BAR_LENGTH_SHIFT     27 /* 0=32, 1=64 bit increment */
19 #define EXP_BAR_MAP_TYPE_SHIFT   29 /* Bulk BAR map */
20
21 /* NFP target for NSP access */
22 #define NFP_NSP_TARGET   7
23
24 /*
25  * This is an NFP internal address used for configuring properly an NFP
26  * expansion BAR.
27  */
28 #define MEM_CMD_BASE_ADDR       0x8100000000
29
30 /* NSP interface registers */
31 #define NSP_BASE                (MEM_CMD_BASE_ADDR + 0x22100)
32 #define NSP_STATUS              0x00
33 #define NSP_COMMAND             0x08
34 #define NSP_BUFFER              0x10
35 #define NSP_DEFAULT_BUF         0x18
36 #define NSP_DEFAULT_BUF_CFG  0x20
37
38 #define NSP_MAGIC                0xab10
39 #define NSP_STATUS_MAGIC(x)      (((x) >> 48) & 0xffff)
40 #define NSP_STATUS_MAJOR(x)      (int)(((x) >> 44) & 0xf)
41 #define NSP_STATUS_MINOR(x)      (int)(((x) >> 32) & 0xfff)
42
43 /* NSP commands */
44 #define NSP_CMD_RESET          1
45 #define NSP_CMD_FW_LOAD        6
46 #define NSP_CMD_GET_SYMBOL     14
47
48 #define NSP_BUFFER_CFG_SIZE_MASK        (0xff)
49
50 #define NSP_REG_ADDR(d, off, reg) ((uint8_t *)(d)->mem_base + (off) + (reg))
51 #define NSP_REG_VAL(p) (*(uint64_t *)(p))
52
53 /*
54  * An NFP expansion BAR is configured for allowing access to a specific NFP
55  * target:
56  *
57  *  IN:
58  *      desc: struct with basic NSP addresses to work with
59  *      expbar: NFP PF expansion BAR index to configure
60  *      tgt: NFP target to configure access
61  *      addr: NFP target address
62  *
63  *  OUT:
64  *      pcie_offset: NFP PCI BAR offset to work with
65  */
66 static void
67 nfp_nspu_mem_bar_cfg(nspu_desc_t *desc, int expbar, int tgt,
68                      uint64_t addr, uint64_t *pcie_offset)
69 {
70         uint64_t x, y, barsz;
71         uint32_t *expbar_ptr;
72
73         barsz = desc->barsz;
74
75         /*
76          * NFP CPP address to configure. This comes from NFP 6000
77          * datasheet document based on Bulk mapping.
78          */
79         x = (addr >> (barsz - 3)) << (21 - (40 - (barsz - 3)));
80         x |= CFG_EXP_BAR_MAP_TYPE << EXP_BAR_MAP_TYPE_SHIFT;
81         x |= CFG_EXP_BAR_ADDR_SZ << EXP_BAR_LENGTH_SHIFT;
82         x |= tgt << EXP_BAR_TARGET_SHIFT;
83
84         /* Getting expansion bar configuration register address */
85         expbar_ptr = (uint32_t *)desc->cfg_base;
86         /* Each physical PCI BAR has 8 NFP expansion BARs */
87         expbar_ptr += (desc->pcie_bar * 8) + expbar;
88
89         /* Writing to the expansion BAR register */
90         *expbar_ptr = (uint32_t)x;
91
92         /* Getting the pcie offset to work with from userspace */
93         y = addr & ((uint64_t)(1 << (barsz - 3)) - 1);
94         *pcie_offset = y;
95 }
96
97 /*
98  * Configuring an expansion bar for accessing NSP userspace interface. This
99  * function configures always the same expansion bar, which implies access to
100  * previously configured NFP target is lost.
101  */
102 static void
103 nspu_xlate(nspu_desc_t *desc, uint64_t addr, uint64_t *pcie_offset)
104 {
105         nfp_nspu_mem_bar_cfg(desc, desc->exp_bar, NFP_NSP_TARGET, addr,
106                              pcie_offset);
107 }
108
109 int
110 nfp_nsp_get_abi_version(nspu_desc_t *desc, int *major, int *minor)
111 {
112         uint64_t pcie_offset;
113         uint64_t nsp_reg;
114
115         nspu_xlate(desc, NSP_BASE, &pcie_offset);
116         nsp_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, pcie_offset, NSP_STATUS));
117
118         if (NSP_STATUS_MAGIC(nsp_reg) != NSP_MAGIC)
119                 return -1;
120
121         *major = NSP_STATUS_MAJOR(nsp_reg);
122         *minor = NSP_STATUS_MINOR(nsp_reg);
123
124         return 0;
125 }
126
127 int
128 nfp_nspu_init(nspu_desc_t *desc, int nfp, int pcie_bar, size_t pcie_barsz,
129               int exp_bar, void *exp_bar_cfg_base, void *exp_bar_mmap)
130 {
131         uint64_t offset, buffaddr;
132         uint64_t nsp_reg;
133
134         desc->nfp = nfp;
135         desc->pcie_bar = pcie_bar;
136         desc->exp_bar = exp_bar;
137         desc->barsz = pcie_barsz;
138         desc->windowsz = 1 << (desc->barsz - 3);
139         desc->cfg_base = exp_bar_cfg_base;
140         desc->mem_base = exp_bar_mmap;
141
142         nspu_xlate(desc, NSP_BASE, &offset);
143
144         /*
145          * Other NSPU clients can use other buffers. Let's tell NSPU we use the
146          * default buffer.
147          */
148         buffaddr = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_DEFAULT_BUF));
149         NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_BUFFER)) = buffaddr;
150
151         /* NFP internal addresses are 40 bits. Clean all other bits here */
152         buffaddr = buffaddr & (((uint64_t)1 << 40) - 1);
153         desc->bufaddr = buffaddr;
154
155         /* Lets get information about the buffer */
156         nsp_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_DEFAULT_BUF_CFG));
157
158         /* Buffer size comes in MBs. Coversion to bytes */
159         desc->buf_size = ((size_t)nsp_reg & NSP_BUFFER_CFG_SIZE_MASK) << 20;
160
161         return 0;
162 }
163
164 #define NSPU_NFP_BUF(addr, base, off) \
165         (*(uint64_t *)((uint8_t *)(addr)->mem_base + ((base) | (off))))
166
167 #define NSPU_HOST_BUF(base, off) (*(uint64_t *)((uint8_t *)(base) + (off)))
168
169 static int
170 nspu_buff_write(nspu_desc_t *desc, void *buffer, size_t size)
171 {
172         uint64_t pcie_offset, pcie_window_base, pcie_window_offset;
173         uint64_t windowsz = desc->windowsz;
174         uint64_t buffaddr, j, i = 0;
175         int ret = 0;
176
177         if (size > desc->buf_size)
178                 return -1;
179
180         buffaddr = desc->bufaddr;
181         windowsz = desc->windowsz;
182
183         while (i < size) {
184                 /* Expansion bar reconfiguration per window size */
185                 nspu_xlate(desc, buffaddr + i, &pcie_offset);
186                 pcie_window_base = pcie_offset & (~(windowsz - 1));
187                 pcie_window_offset = pcie_offset & (windowsz - 1);
188                 for (j = pcie_window_offset; ((j < windowsz) && (i < size));
189                      j += 8) {
190                         NSPU_NFP_BUF(desc, pcie_window_base, j) =
191                                 NSPU_HOST_BUF(buffer, i);
192                         i += 8;
193                 }
194         }
195
196         return ret;
197 }
198
199 static int
200 nspu_buff_read(nspu_desc_t *desc, void *buffer, size_t size)
201 {
202         uint64_t pcie_offset, pcie_window_base, pcie_window_offset;
203         uint64_t windowsz, i = 0, j;
204         uint64_t buffaddr;
205         int ret = 0;
206
207         if (size > desc->buf_size)
208                 return -1;
209
210         buffaddr = desc->bufaddr;
211         windowsz = desc->windowsz;
212
213         while (i < size) {
214                 /* Expansion bar reconfiguration per window size */
215                 nspu_xlate(desc, buffaddr + i, &pcie_offset);
216                 pcie_window_base = pcie_offset & (~(windowsz - 1));
217                 pcie_window_offset = pcie_offset & (windowsz - 1);
218                 for (j = pcie_window_offset; ((j < windowsz) && (i < size));
219                      j += 8) {
220                         NSPU_HOST_BUF(buffer, i) =
221                                 NSPU_NFP_BUF(desc, pcie_window_base, j);
222                         i += 8;
223                 }
224         }
225
226         return ret;
227 }
228
229 static int
230 nspu_command(nspu_desc_t *desc, uint16_t cmd, int read, int write,
231                  void *buffer, size_t rsize, size_t wsize)
232 {
233         uint64_t status, cmd_reg;
234         uint64_t offset;
235         int retry = 0;
236         int retries = 120;
237         int ret = 0;
238
239         /* Same expansion BAR is used for different things */
240         nspu_xlate(desc, NSP_BASE, &offset);
241
242         status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
243
244         while ((status & 0x1) && (retry < retries)) {
245                 status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
246                 retry++;
247                 sleep(1);
248         }
249
250         if (retry == retries)
251                 return -1;
252
253         if (write) {
254                 ret = nspu_buff_write(desc, buffer, wsize);
255                 if (ret)
256                         return ret;
257
258                 /* Expansion BAR changes when writing the buffer */
259                 nspu_xlate(desc, NSP_BASE, &offset);
260         }
261
262         NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_COMMAND)) =
263                 (uint64_t)wsize << 32 | (uint64_t)cmd << 16 | 1;
264
265         retry = 0;
266
267         cmd_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_COMMAND));
268         while ((cmd_reg & 0x1) && (retry < retries)) {
269                 cmd_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_COMMAND));
270                 retry++;
271                 sleep(1);
272         }
273         if (retry == retries)
274                 return -1;
275
276         retry = 0;
277         status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
278         while ((status & 0x1) && (retry < retries)) {
279                 status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
280                 retry++;
281                 sleep(1);
282         }
283
284         if (retry == retries)
285                 return -1;
286
287         ret = status & (0xff << 8);
288         if (ret)
289                 return ret;
290
291         if (read) {
292                 ret = nspu_buff_read(desc, buffer, rsize);
293                 if (ret)
294                         return ret;
295         }
296
297         return ret;
298 }
299
300 int
301 nfp_fw_reset(nspu_desc_t *nspu_desc)
302 {
303         int res;
304
305         res = nspu_command(nspu_desc, NSP_CMD_RESET, 0, 0, 0, 0, 0);
306
307         if (res < 0)
308                 RTE_LOG(INFO, PMD, "fw reset failed: error %d", res);
309
310         return res;
311 }
312
313 #define DEFAULT_FW_PATH       "/lib/firmware/netronome"
314 #define DEFAULT_FW_FILENAME   "nic_dpdk_default.nffw"
315
316 int
317 nfp_fw_upload(nspu_desc_t *nspu_desc)
318 {
319         int fw_f;
320         char *fw_buf;
321         char filename[100];
322         struct stat file_stat;
323         off_t fsize, bytes;
324         ssize_t size;
325         int ret;
326
327         size = nspu_desc->buf_size;
328
329         sprintf(filename, "%s/%s", DEFAULT_FW_PATH, DEFAULT_FW_FILENAME);
330         fw_f = open(filename, O_RDONLY);
331         if (fw_f < 0) {
332                 RTE_LOG(INFO, PMD, "Firmware file %s/%s not found.",
333                         DEFAULT_FW_PATH, DEFAULT_FW_FILENAME);
334                 return -ENOENT;
335         }
336
337         fstat(fw_f, &file_stat);
338
339         fsize = file_stat.st_size;
340         RTE_LOG(DEBUG, PMD, "Firmware file with size: %" PRIu64 "\n",
341                             (uint64_t)fsize);
342
343         if (fsize > (off_t)size) {
344                 RTE_LOG(INFO, PMD, "fw file too big: %" PRIu64
345                                    " bytes (%" PRIu64 " max)",
346                                   (uint64_t)fsize, (uint64_t)size);
347                 return -EINVAL;
348         }
349
350         fw_buf = malloc((size_t)size);
351         if (!fw_buf) {
352                 RTE_LOG(INFO, PMD, "malloc failed for fw buffer");
353                 return -ENOMEM;
354         }
355         memset(fw_buf, 0, size);
356
357         bytes = read(fw_f, fw_buf, fsize);
358         if (bytes != fsize) {
359                 RTE_LOG(INFO, PMD, "Reading fw to buffer failed.\n"
360                                    "Just %" PRIu64 " of %" PRIu64 " bytes read.",
361                                    (uint64_t)bytes, (uint64_t)fsize);
362                 free(fw_buf);
363                 return -EIO;
364         }
365
366         ret = nspu_command(nspu_desc, NSP_CMD_FW_LOAD, 0, 1, fw_buf, 0, bytes);
367
368         free(fw_buf);
369
370         return ret;
371 }
372
373 /* Firmware symbol descriptor size */
374 #define NFP_SYM_DESC_LEN 40
375
376 #define SYMBOL_DATA(b, off)     (*(int64_t *)((b) + (off)))
377 #define SYMBOL_UDATA(b, off)     (*(uint64_t *)((b) + (off)))
378
379 /* Firmware symbols contain information about how to access what they
380  * represent. It can be as simple as an numeric variable declared at a
381  * specific NFP memory, but it can also be more complex structures and
382  * related to specific hardware functionalities or components. Target,
383  * domain and address allow to create the BAR window for accessing such
384  * hw object and size defines the length to map.
385  *
386  * A vNIC is a network interface implemented inside the NFP and using a
387  * subset of device PCI BARs. Specific firmware symbols allow to map those
388  * vNIC bars by host drivers like the NFP PMD.
389  *
390  * Accessing what the symbol represents implies to map the access through
391  * a PCI BAR window. NFP expansion BARs are used in this regard through
392  * the NSPU interface.
393  */
394 int
395 nfp_nspu_set_bar_from_symbl(nspu_desc_t *desc, const char *symbl,
396                             uint32_t expbar, uint64_t *pcie_offset,
397                             ssize_t *size)
398 {
399         int64_t type;
400         int64_t target;
401         int64_t domain;
402         uint64_t addr;
403         char *sym_buf;
404         int ret = 0;
405
406         sym_buf = malloc(desc->buf_size);
407         strncpy(sym_buf, symbl, strlen(symbl));
408         ret = nspu_command(desc, NSP_CMD_GET_SYMBOL, 1, 1, sym_buf,
409                            NFP_SYM_DESC_LEN, strlen(symbl));
410         if (ret) {
411                 RTE_LOG(DEBUG, PMD, "symbol resolution (%s) failed\n", symbl);
412                 goto clean;
413         }
414
415         /* Reading symbol information */
416         type = SYMBOL_DATA(sym_buf, 0);
417         target = SYMBOL_DATA(sym_buf, 8);
418         domain =  SYMBOL_DATA(sym_buf, 16);
419         addr = SYMBOL_UDATA(sym_buf, 24);
420         *size = (ssize_t)SYMBOL_UDATA(sym_buf, 32);
421
422         if (type != 1) {
423                 RTE_LOG(INFO, PMD, "wrong symbol type\n");
424                 ret = -EINVAL;
425                 goto clean;
426         }
427         if (!(target == 7 || target == -7)) {
428                 RTE_LOG(INFO, PMD, "wrong symbol target\n");
429                 ret = -EINVAL;
430                 goto clean;
431         }
432         if (domain == 8 || domain == 9) {
433                 RTE_LOG(INFO, PMD, "wrong symbol domain\n");
434                 ret = -EINVAL;
435                 goto clean;
436         }
437
438         /* Adjusting address based on symbol location */
439         if ((domain >= 24) && (domain < 28) && (target == 7)) {
440                 addr = 1ULL << 37 | addr | ((uint64_t)domain & 0x3) << 35;
441         } else {
442                 addr = 1ULL << 39 | addr | ((uint64_t)domain & 0x3f) << 32;
443                 if (target == -7)
444                         target = 7;
445         }
446
447         /* Configuring NFP expansion bar for mapping specific PCI BAR window */
448         nfp_nspu_mem_bar_cfg(desc, expbar, target, addr, pcie_offset);
449
450         /* This is the PCI BAR offset to use by the host */
451         *pcie_offset |= ((expbar & 0x7) << (desc->barsz - 3));
452
453 clean:
454         free(sym_buf);
455         return ret;
456 }