net/nfp: add NSP FW upload 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
47 #define NSP_BUFFER_CFG_SIZE_MASK        (0xff)
48
49 #define NSP_REG_ADDR(d, off, reg) ((uint8_t *)(d)->mem_base + (off) + (reg))
50 #define NSP_REG_VAL(p) (*(uint64_t *)(p))
51
52 /*
53  * An NFP expansion BAR is configured for allowing access to a specific NFP
54  * target:
55  *
56  *  IN:
57  *      desc: struct with basic NSP addresses to work with
58  *      expbar: NFP PF expansion BAR index to configure
59  *      tgt: NFP target to configure access
60  *      addr: NFP target address
61  *
62  *  OUT:
63  *      pcie_offset: NFP PCI BAR offset to work with
64  */
65 static void
66 nfp_nspu_mem_bar_cfg(nspu_desc_t *desc, int expbar, int tgt,
67                      uint64_t addr, uint64_t *pcie_offset)
68 {
69         uint64_t x, y, barsz;
70         uint32_t *expbar_ptr;
71
72         barsz = desc->barsz;
73
74         /*
75          * NFP CPP address to configure. This comes from NFP 6000
76          * datasheet document based on Bulk mapping.
77          */
78         x = (addr >> (barsz - 3)) << (21 - (40 - (barsz - 3)));
79         x |= CFG_EXP_BAR_MAP_TYPE << EXP_BAR_MAP_TYPE_SHIFT;
80         x |= CFG_EXP_BAR_ADDR_SZ << EXP_BAR_LENGTH_SHIFT;
81         x |= tgt << EXP_BAR_TARGET_SHIFT;
82
83         /* Getting expansion bar configuration register address */
84         expbar_ptr = (uint32_t *)desc->cfg_base;
85         /* Each physical PCI BAR has 8 NFP expansion BARs */
86         expbar_ptr += (desc->pcie_bar * 8) + expbar;
87
88         /* Writing to the expansion BAR register */
89         *expbar_ptr = (uint32_t)x;
90
91         /* Getting the pcie offset to work with from userspace */
92         y = addr & ((uint64_t)(1 << (barsz - 3)) - 1);
93         *pcie_offset = y;
94 }
95
96 /*
97  * Configuring an expansion bar for accessing NSP userspace interface. This
98  * function configures always the same expansion bar, which implies access to
99  * previously configured NFP target is lost.
100  */
101 static void
102 nspu_xlate(nspu_desc_t *desc, uint64_t addr, uint64_t *pcie_offset)
103 {
104         nfp_nspu_mem_bar_cfg(desc, desc->exp_bar, NFP_NSP_TARGET, addr,
105                              pcie_offset);
106 }
107
108 int
109 nfp_nsp_get_abi_version(nspu_desc_t *desc, int *major, int *minor)
110 {
111         uint64_t pcie_offset;
112         uint64_t nsp_reg;
113
114         nspu_xlate(desc, NSP_BASE, &pcie_offset);
115         nsp_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, pcie_offset, NSP_STATUS));
116
117         if (NSP_STATUS_MAGIC(nsp_reg) != NSP_MAGIC)
118                 return -1;
119
120         *major = NSP_STATUS_MAJOR(nsp_reg);
121         *minor = NSP_STATUS_MINOR(nsp_reg);
122
123         return 0;
124 }
125
126 int
127 nfp_nspu_init(nspu_desc_t *desc, int nfp, int pcie_bar, size_t pcie_barsz,
128               int exp_bar, void *exp_bar_cfg_base, void *exp_bar_mmap)
129 {
130         uint64_t offset, buffaddr;
131         uint64_t nsp_reg;
132
133         desc->nfp = nfp;
134         desc->pcie_bar = pcie_bar;
135         desc->exp_bar = exp_bar;
136         desc->barsz = pcie_barsz;
137         desc->windowsz = 1 << (desc->barsz - 3);
138         desc->cfg_base = exp_bar_cfg_base;
139         desc->mem_base = exp_bar_mmap;
140
141         nspu_xlate(desc, NSP_BASE, &offset);
142
143         /*
144          * Other NSPU clients can use other buffers. Let's tell NSPU we use the
145          * default buffer.
146          */
147         buffaddr = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_DEFAULT_BUF));
148         NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_BUFFER)) = buffaddr;
149
150         /* NFP internal addresses are 40 bits. Clean all other bits here */
151         buffaddr = buffaddr & (((uint64_t)1 << 40) - 1);
152         desc->bufaddr = buffaddr;
153
154         /* Lets get information about the buffer */
155         nsp_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_DEFAULT_BUF_CFG));
156
157         /* Buffer size comes in MBs. Coversion to bytes */
158         desc->buf_size = ((size_t)nsp_reg & NSP_BUFFER_CFG_SIZE_MASK) << 20;
159
160         return 0;
161 }
162
163 #define NSPU_NFP_BUF(addr, base, off) \
164         (*(uint64_t *)((uint8_t *)(addr)->mem_base + ((base) | (off))))
165
166 #define NSPU_HOST_BUF(base, off) (*(uint64_t *)((uint8_t *)(base) + (off)))
167
168 static int
169 nspu_buff_write(nspu_desc_t *desc, void *buffer, size_t size)
170 {
171         uint64_t pcie_offset, pcie_window_base, pcie_window_offset;
172         uint64_t windowsz = desc->windowsz;
173         uint64_t buffaddr, j, i = 0;
174         int ret = 0;
175
176         if (size > desc->buf_size)
177                 return -1;
178
179         buffaddr = desc->bufaddr;
180         windowsz = desc->windowsz;
181
182         while (i < size) {
183                 /* Expansion bar reconfiguration per window size */
184                 nspu_xlate(desc, buffaddr + i, &pcie_offset);
185                 pcie_window_base = pcie_offset & (~(windowsz - 1));
186                 pcie_window_offset = pcie_offset & (windowsz - 1);
187                 for (j = pcie_window_offset; ((j < windowsz) && (i < size));
188                      j += 8) {
189                         NSPU_NFP_BUF(desc, pcie_window_base, j) =
190                                 NSPU_HOST_BUF(buffer, i);
191                         i += 8;
192                 }
193         }
194
195         return ret;
196 }
197
198 static int
199 nspu_buff_read(nspu_desc_t *desc, void *buffer, size_t size)
200 {
201         uint64_t pcie_offset, pcie_window_base, pcie_window_offset;
202         uint64_t windowsz, i = 0, j;
203         uint64_t buffaddr;
204         int ret = 0;
205
206         if (size > desc->buf_size)
207                 return -1;
208
209         buffaddr = desc->bufaddr;
210         windowsz = desc->windowsz;
211
212         while (i < size) {
213                 /* Expansion bar reconfiguration per window size */
214                 nspu_xlate(desc, buffaddr + i, &pcie_offset);
215                 pcie_window_base = pcie_offset & (~(windowsz - 1));
216                 pcie_window_offset = pcie_offset & (windowsz - 1);
217                 for (j = pcie_window_offset; ((j < windowsz) && (i < size));
218                      j += 8) {
219                         NSPU_HOST_BUF(buffer, i) =
220                                 NSPU_NFP_BUF(desc, pcie_window_base, j);
221                         i += 8;
222                 }
223         }
224
225         return ret;
226 }
227
228 static int
229 nspu_command(nspu_desc_t *desc, uint16_t cmd, int read, int write,
230                  void *buffer, size_t rsize, size_t wsize)
231 {
232         uint64_t status, cmd_reg;
233         uint64_t offset;
234         int retry = 0;
235         int retries = 120;
236         int ret = 0;
237
238         /* Same expansion BAR is used for different things */
239         nspu_xlate(desc, NSP_BASE, &offset);
240
241         status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
242
243         while ((status & 0x1) && (retry < retries)) {
244                 status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
245                 retry++;
246                 sleep(1);
247         }
248
249         if (retry == retries)
250                 return -1;
251
252         if (write) {
253                 ret = nspu_buff_write(desc, buffer, wsize);
254                 if (ret)
255                         return ret;
256
257                 /* Expansion BAR changes when writing the buffer */
258                 nspu_xlate(desc, NSP_BASE, &offset);
259         }
260
261         NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_COMMAND)) =
262                 (uint64_t)wsize << 32 | (uint64_t)cmd << 16 | 1;
263
264         retry = 0;
265
266         cmd_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_COMMAND));
267         while ((cmd_reg & 0x1) && (retry < retries)) {
268                 cmd_reg = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_COMMAND));
269                 retry++;
270                 sleep(1);
271         }
272         if (retry == retries)
273                 return -1;
274
275         retry = 0;
276         status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
277         while ((status & 0x1) && (retry < retries)) {
278                 status = NSP_REG_VAL(NSP_REG_ADDR(desc, offset, NSP_STATUS));
279                 retry++;
280                 sleep(1);
281         }
282
283         if (retry == retries)
284                 return -1;
285
286         ret = status & (0xff << 8);
287         if (ret)
288                 return ret;
289
290         if (read) {
291                 ret = nspu_buff_read(desc, buffer, rsize);
292                 if (ret)
293                         return ret;
294         }
295
296         return ret;
297 }
298
299 int
300 nfp_fw_reset(nspu_desc_t *nspu_desc)
301 {
302         int res;
303
304         res = nspu_command(nspu_desc, NSP_CMD_RESET, 0, 0, 0, 0, 0);
305
306         if (res < 0)
307                 RTE_LOG(INFO, PMD, "fw reset failed: error %d", res);
308
309         return res;
310 }
311
312 #define DEFAULT_FW_PATH       "/lib/firmware/netronome"
313 #define DEFAULT_FW_FILENAME   "nic_dpdk_default.nffw"
314
315 int
316 nfp_fw_upload(nspu_desc_t *nspu_desc)
317 {
318         int fw_f;
319         char *fw_buf;
320         char filename[100];
321         struct stat file_stat;
322         off_t fsize, bytes;
323         ssize_t size;
324         int ret;
325
326         size = nspu_desc->buf_size;
327
328         sprintf(filename, "%s/%s", DEFAULT_FW_PATH, DEFAULT_FW_FILENAME);
329         fw_f = open(filename, O_RDONLY);
330         if (fw_f < 0) {
331                 RTE_LOG(INFO, PMD, "Firmware file %s/%s not found.",
332                         DEFAULT_FW_PATH, DEFAULT_FW_FILENAME);
333                 return -ENOENT;
334         }
335
336         fstat(fw_f, &file_stat);
337
338         fsize = file_stat.st_size;
339         RTE_LOG(DEBUG, PMD, "Firmware file with size: %" PRIu64 "\n",
340                             (uint64_t)fsize);
341
342         if (fsize > (off_t)size) {
343                 RTE_LOG(INFO, PMD, "fw file too big: %" PRIu64
344                                    " bytes (%" PRIu64 " max)",
345                                   (uint64_t)fsize, (uint64_t)size);
346                 return -EINVAL;
347         }
348
349         fw_buf = malloc((size_t)size);
350         if (!fw_buf) {
351                 RTE_LOG(INFO, PMD, "malloc failed for fw buffer");
352                 return -ENOMEM;
353         }
354         memset(fw_buf, 0, size);
355
356         bytes = read(fw_f, fw_buf, fsize);
357         if (bytes != fsize) {
358                 RTE_LOG(INFO, PMD, "Reading fw to buffer failed.\n"
359                                    "Just %" PRIu64 " of %" PRIu64 " bytes read.",
360                                    (uint64_t)bytes, (uint64_t)fsize);
361                 free(fw_buf);
362                 return -EIO;
363         }
364
365         ret = nspu_command(nspu_desc, NSP_CMD_FW_LOAD, 0, 1, fw_buf, 0, bytes);
366
367         free(fw_buf);
368
369         return ret;
370 }