Switchtec Userspace PROJECT_NUMBER = 4.4
Loading...
Searching...
No Matches
gasops.c
1/*
2 * Microsemi Switchtec(tm) PCIe Management Library
3 * Copyright (c) 2017, Microsemi Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25#include "gasops.h"
26#include "switchtec/gas.h"
27#include "../switchtec_priv.h"
28#include "switchtec/utils.h"
29
30#include <errno.h>
31#include <stddef.h>
32#include <string.h>
33#include <unistd.h>
34#include <sys/time.h>
35
36#define gas_reg_read8(dev, reg) __gas_read8(dev, &dev->gas_map->reg)
37#define gas_reg_read16(dev, reg) __gas_read16(dev, &dev->gas_map->reg)
38#define gas_reg_read32(dev, reg) __gas_read32(dev, &dev->gas_map->reg)
39#define gas_reg_read64(dev, reg) __gas_read64(dev, &dev->gas_map->reg)
40
41#define gas_reg_write8(dev, val, reg) __gas_write8(dev, val, \
42 &dev->gas_map->reg)
43#define gas_reg_write16(dev, val, reg) __gas_write16(dev, val, \
44 &dev->gas_map->reg)
45#define gas_reg_write32(dev, val, reg) __gas_write32(dev, val, \
46 &dev->gas_map->reg)
47#define gas_reg_write64(dev, val, reg) __gas_write64(dev, val, \
48 &dev->gas_map->reg)
49
51 int no_retry;
52 int num_subcmd;
53 int *subcmds;
54};
55static int fw_toggle_noretry_subcmds[] = {
56 MRPC_FW_TX_TOGGLE,
57};
58static const struct no_retry_struct gasop_noretry_cmds[] = {
59 [MRPC_SECURITY_CONFIG_SET] = {1, 0, NULL},
60 [MRPC_KMSK_ENTRY_SET] = {1, 0, NULL},
61 [MRPC_SECURE_STATE_SET] = {1, 0, NULL},
62 [MRPC_BOOTUP_RESUME] = {1, 0, NULL},
63 [MRPC_DBG_UNLOCK] = {1, 0, NULL},
64 [MRPC_FW_TX] = {1, 1, fw_toggle_noretry_subcmds},
65 [MRPC_SECURITY_CONFIG_SET_GEN5] = {1, 0, NULL},
66 [MRPC_KMSK_ENTRY_SET_GEN5] = {1, 0, NULL},
67 [MRPC_SECURE_STATE_SET_GEN5] = {1, 0, NULL},
68 [MRPC_BOOTUP_RESUME_GEN5] = {1, 0, NULL},
69 [MRPC_DBG_UNLOCK_GEN5] = {1, 0, NULL},
70 [MRPC_DBG_UNLOCK_GEN6] = {1, 0, NULL},
71 [MRPC_FW_TX_GEN5] = {1, 1, fw_toggle_noretry_subcmds},
72};
73static const int gasop_noretry_cmds_count = sizeof(gasop_noretry_cmds) /
74 sizeof(struct no_retry_struct);
75
76static inline bool gasop_is_no_retry_cmd(uint32_t cmd, int subcmd)
77{
78 int i;
79
80 cmd &= SWITCHTEC_CMD_MASK;
81
82 if (cmd >= gasop_noretry_cmds_count)
83 return 0;
84 if (gasop_noretry_cmds[cmd].no_retry == 0)
85 return 0;
86 if (gasop_noretry_cmds[cmd].num_subcmd == 0)
87 return 1;
88 for (i = 0; i < gasop_noretry_cmds[cmd].num_subcmd; i++) {
89 if (subcmd == gasop_noretry_cmds[cmd].subcmds[i])
90 return 1;
91 }
92
93 return 0;
94}
95
96int gasop_access_check(struct switchtec_dev *dev)
97{
98 uint32_t device_id;
99
100 device_id = gas_reg_read32(dev, sys_info.device_id);
101 if (device_id == -1)
102 return -1;
103 return 0;
104}
105
106void gasop_set_partition_info(struct switchtec_dev *dev)
107{
108 dev->partition = gas_reg_read8(dev, top.partition_id);
109 dev->partition_count = gas_reg_read8(dev, top.partition_count);
110}
111
112int gasop_cmd(struct switchtec_dev *dev, uint32_t cmd,
113 const void *payload, size_t payload_len, void *resp,
114 size_t resp_len)
115{
116 struct mrpc_regs __gas *mrpc = &dev->gas_map->mrpc;
117 int status;
118 int ret;
119 uint8_t subcmd = 0xff;
120
121 __memcpy_to_gas(dev, &mrpc->input_data, payload, payload_len);
122
123 /* Due to the possible unreliable nature of hardware
124 * communication, function __gas_write32() is implemented
125 * with automatic retry.
126 *
127 * This poses a potential issue when a command is critical
128 * and is expected to be sent only once (e.g., command that
129 * adds a KMSK entry to chip OTP memory). Retrying could
130 * cause the command be sent multiple times (and multiple
131 * KMSK entry being added, if unlucky).
132 *
133 * Here we filter out the specific commands and use 'no retry'
134 * version of gas_write32 for these commands.
135 */
136 if (payload)
137 subcmd = *(uint8_t*)payload;
138 if (gasop_is_no_retry_cmd(cmd, subcmd))
139 __gas_write32_no_retry(dev, cmd, &mrpc->cmd);
140 else
141 __gas_write32(dev, cmd, &mrpc->cmd);
142
143 while (1) {
144 usleep(5000);
145
146 status = __gas_read32(dev, &mrpc->status);
147 if (status != SWITCHTEC_MRPC_STATUS_INPROGRESS)
148 break;
149 }
150
151 if (status == SWITCHTEC_MRPC_STATUS_INTERRUPTED) {
152 errno = ENXIO;
153 return -errno;
154 }
155
156 if (status == SWITCHTEC_MRPC_STATUS_ERROR) {
157 errno = __gas_read32(dev, &mrpc->ret_value);
158 return errno;
159 }
160
161 if (status != SWITCHTEC_MRPC_STATUS_DONE) {
162 errno = ENXIO;
163 return -errno;
164 }
165
166 ret = __gas_read32(dev, &mrpc->ret_value);
167 if (ret)
168 errno = ret;
169
170 if (resp)
171 __memcpy_from_gas(dev, resp, &mrpc->output_data, resp_len);
172
173 return ret;
174}
175
176int gasop_get_device_id(struct switchtec_dev *dev)
177{
178 return gas_reg_read32(dev, sys_info.device_id);
179}
180
181int gasop_get_fw_version(struct switchtec_dev *dev, char *buf,
182 size_t buflen)
183{
184 long long ver;
185
186 ver = gas_reg_read32(dev, sys_info.firmware_version);
187 version_to_string(ver, buf, buflen);
188
189 return 0;
190}
191
192int gasop_get_device_version(struct switchtec_dev *dev, int *res)
193{
194 uint32_t dev_ver;
195 dev_ver = gas_reg_read32(dev, sys_info.device_version);
196
197 *res = dev_ver;
198 return 0;
199}
200
201int gasop_pff_to_port(struct switchtec_dev *dev, int pff,
202 int *partition, int *port)
203{
204 int i, part;
205 uint32_t reg;
206 struct part_cfg_regs __gas *pcfg;
207
208 *port = -1;
209
210 for (part = 0; part < dev->partition_count; part++) {
211 pcfg = &dev->gas_map->part_cfg[part];
212 *partition = part;
213
214 reg = __gas_read32(dev, &pcfg->usp_pff_inst_id);
215 if (reg == pff) {
216 *port = 0;
217 return 0;
218 }
219
220 reg = __gas_read32(dev, &pcfg->vep_pff_inst_id);
221 if (reg == pff) {
222 *port = SWITCHTEC_PFF_PORT_VEP;
223 return 0;
224 }
225
226 for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) {
227 reg = __gas_read32(dev, &pcfg->dsp_pff_inst_id[i]);
228 if (reg != pff)
229 continue;
230
231 *port = i + 1;
232 break;
233 }
234
235 if (*port != -1)
236 return 0;
237 }
238
239 errno = EINVAL;
240 return -EINVAL;
241}
242
243int gasop_port_to_pff(struct switchtec_dev *dev, int partition,
244 int port, int *pff)
245{
246 struct part_cfg_regs __gas *pcfg;
247
248 if (partition < 0) {
249 partition = dev->partition;
250 } else if (partition >= dev->partition_count) {
251 errno = EINVAL;
252 return -errno;
253 }
254
255 pcfg = &dev->gas_map->part_cfg[partition];
256
257 switch (port) {
258 case 0:
259 *pff = __gas_read32(dev, &pcfg->usp_pff_inst_id);
260 break;
261 case SWITCHTEC_PFF_PORT_VEP:
262 *pff = __gas_read32(dev, &pcfg->vep_pff_inst_id);
263 break;
264 default:
265 if (port > ARRAY_SIZE(pcfg->dsp_pff_inst_id)) {
266 errno = EINVAL;
267 return -errno;
268 }
269
270 *pff = __gas_read32(dev, &pcfg->dsp_pff_inst_id[port - 1]);
271 break;
272 }
273
274 return 0;
275}
276
277static void set_fw_info_part(struct switchtec_dev *dev,
278 struct switchtec_fw_image_info *info,
279 struct partition_info __gas *pi)
280{
281 info->part_addr = __gas_read32(dev, &pi->address);
282 info->part_len = __gas_read32(dev, &pi->length);
283}
284
285int gasop_flash_part(struct switchtec_dev *dev,
286 struct switchtec_fw_image_info *info,
287 enum switchtec_fw_image_part_id_gen3 part)
288{
289 struct flash_info_regs __gas *fi = &dev->gas_map->flash_info;
290 struct sys_info_regs __gas *si = &dev->gas_map->sys_info;
291 uint32_t active_addr = -1;
292 int val;
293
294 info->running = false;
295 info->active = false;
296
297 switch (part) {
298 case SWITCHTEC_FW_PART_ID_G3_IMG0:
299 active_addr = __gas_read32(dev, &fi->active_img.address);
300 set_fw_info_part(dev, info, &fi->img0);
301
302 val = __gas_read16(dev, &si->img_running);
303 if (val == SWITCHTEC_IMG0_RUNNING)
304 info->running = true;
305 break;
306
307 case SWITCHTEC_FW_PART_ID_G3_IMG1:
308 active_addr = __gas_read32(dev, &fi->active_img.address);
309 set_fw_info_part(dev, info, &fi->img1);
310
311 val = __gas_read16(dev, &si->img_running);
312 if (val == SWITCHTEC_IMG1_RUNNING)
313 info->running = true;
314 break;
315
316 case SWITCHTEC_FW_PART_ID_G3_DAT0:
317 active_addr = __gas_read32(dev, &fi->active_cfg.address);
318 set_fw_info_part(dev, info, &fi->cfg0);
319
320 val = __gas_read16(dev, &si->cfg_running);
321 if (val == SWITCHTEC_CFG0_RUNNING)
322 info->running = true;
323 break;
324
325 case SWITCHTEC_FW_PART_ID_G3_DAT1:
326 active_addr = __gas_read32(dev, &fi->active_cfg.address);
327 set_fw_info_part(dev, info, &fi->cfg1);
328
329 val = __gas_read16(dev, &si->cfg_running);
330 if (val == SWITCHTEC_CFG1_RUNNING)
331 info->running = true;
332 break;
333
334 case SWITCHTEC_FW_PART_ID_G3_NVLOG:
335 set_fw_info_part(dev, info, &fi->nvlog);
336 break;
337
338 default:
339 return -EINVAL;
340 }
341
342 if (info->part_addr == active_addr)
343 info->active = true;
344
345 return 0;
346}
347
348int gasop_event_summary(struct switchtec_dev *dev,
349 struct switchtec_event_summary *sum)
350{
351 int i;
352 uint32_t reg;
353
354 if (!sum)
355 return 0;
356
357 memset(sum, 0, sizeof(*sum));
358
359 sum->global = gas_reg_read32(dev, sw_event.global_summary);
360 sum->part_bitmap = gas_reg_read64(dev, sw_event.part_event_bitmap);
361
362 for (i = 0; i < dev->partition_count; i++) {
363 reg = gas_reg_read32(dev, part_cfg[i].part_event_summary);
364 sum->part[i] = reg;
365 if (i == dev->partition)
366 sum->local_part = reg;
367 }
368
369 for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
370 reg = gas_reg_read16(dev, pff_csr[i].vendor_id);
371 if (reg != MICROSEMI_VENDOR_ID)
372 break;
373
374 sum->pff[i] = gas_reg_read32(dev, pff_csr[i].pff_event_summary);
375 }
376
377 return 0;
378}
379
380static uint32_t __gas *global_ev_reg(struct switchtec_dev *dev,
381 size_t offset, int index)
382{
383 return (void __gas *)&dev->gas_map->sw_event + offset;
384}
385
386static uint32_t __gas *part_ev_reg(struct switchtec_dev *dev,
387 size_t offset, int index)
388{
389 return (void __gas *)&dev->gas_map->part_cfg[index] + offset;
390}
391
392static uint32_t __gas *pff_ev_reg(struct switchtec_dev *dev,
393 size_t offset, int index)
394{
395 return (void __gas *)&dev->gas_map->pff_csr[index] + offset;
396}
397
398#define EV_GLB(i, r)[SWITCHTEC_GLOBAL_EVT_ ## i] = \
399 {offsetof(struct sw_event_regs, r), global_ev_reg}
400#define EV_PAR(i, r)[SWITCHTEC_PART_EVT_ ## i] = \
401 {offsetof(struct part_cfg_regs, r), part_ev_reg}
402#define EV_PFF(i, r)[SWITCHTEC_PFF_EVT_ ## i] = \
403 {offsetof(struct pff_csr_regs, r), pff_ev_reg}
404
405static const struct event_reg {
406 size_t offset;
407 uint32_t __gas *(*map_reg)(struct switchtec_dev *stdev,
408 size_t offset, int index);
409} event_regs[] = {
410 EV_GLB(STACK_ERROR, stack_error_event_hdr),
411 EV_GLB(PPU_ERROR, ppu_error_event_hdr),
412 EV_GLB(ISP_ERROR, isp_error_event_hdr),
413 EV_GLB(SYS_RESET, sys_reset_event_hdr),
414 EV_GLB(FW_EXC, fw_exception_hdr),
415 EV_GLB(FW_NMI, fw_nmi_hdr),
416 EV_GLB(FW_NON_FATAL, fw_non_fatal_hdr),
417 EV_GLB(FW_FATAL, fw_fatal_hdr),
418 EV_GLB(TWI_MRPC_COMP, twi_mrpc_comp_hdr),
419 EV_GLB(TWI_MRPC_COMP_ASYNC, twi_mrpc_comp_async_hdr),
420 EV_GLB(CLI_MRPC_COMP, cli_mrpc_comp_hdr),
421 EV_GLB(CLI_MRPC_COMP_ASYNC, cli_mrpc_comp_async_hdr),
422 EV_GLB(GPIO_INT, gpio_interrupt_hdr),
423 EV_GLB(GFMS, gfms_event_hdr),
424 EV_PAR(PART_RESET, part_reset_hdr),
425 EV_PAR(MRPC_COMP, mrpc_comp_hdr),
426 EV_PAR(MRPC_COMP_ASYNC, mrpc_comp_async_hdr),
427 EV_PAR(DYN_PART_BIND_COMP, dyn_binding_hdr),
428 EV_PFF(AER_IN_P2P, aer_in_p2p_hdr),
429 EV_PFF(AER_IN_VEP, aer_in_vep_hdr),
430 EV_PFF(DPC, dpc_hdr),
431 EV_PFF(CTS, cts_hdr),
432 EV_PFF(UEC, uec_hdr),
433 EV_PFF(HOTPLUG, hotplug_hdr),
434 EV_PFF(IER, ier_hdr),
435 EV_PFF(THRESH, threshold_hdr),
436 EV_PFF(POWER_MGMT, power_mgmt_hdr),
437 EV_PFF(TLP_THROTTLING, tlp_throttling_hdr),
438 EV_PFF(FORCE_SPEED, force_speed_hdr),
439 EV_PFF(CREDIT_TIMEOUT, credit_timeout_hdr),
440 EV_PFF(LINK_STATE, link_state_hdr),
441 EV_GLB(ASSERT_ERR, sys_reset_event_hdr),
442};
443
444static uint32_t __gas *event_hdr_addr(struct switchtec_dev *dev,
445 enum switchtec_event_id e,
446 int index)
447{
448 size_t off;
449
450 if (e < 0 || e >= SWITCHTEC_MAX_EVENTS)
451 return NULL;
452
453 off = event_regs[e].offset;
454
455 if (event_regs[e].map_reg == part_ev_reg) {
456 if (index < 0)
457 index = dev->partition;
458 else if (index >= dev->partition_count)
459 return NULL;
460 } else if (event_regs[e].map_reg == pff_ev_reg) {
461 if (index < 0 || index >= SWITCHTEC_MAX_PFF_CSR)
462 return NULL;
463 }
464
465 return event_regs[e].map_reg(dev, off, index);
466}
467
468static int event_ctl(struct switchtec_dev *dev, enum switchtec_event_id e,
469 int index, int flags, uint32_t data[5])
470{
471 int i;
472 uint32_t __gas *reg;
473 uint32_t hdr;
474
475 reg = event_hdr_addr(dev, e, index);
476 if (!reg) {
477 errno = EINVAL;
478 return -errno;
479 }
480
481 hdr = __gas_read32(dev, reg);
482 if (data)
483 for (i = 0; i < 5; i++)
484 data[i] = __gas_read32(dev, &reg[i + 1]);
485
486 if (!(flags & SWITCHTEC_EVT_FLAG_CLEAR))
487 hdr &= ~SWITCHTEC_EVENT_CLEAR;
488 if (flags & SWITCHTEC_EVT_FLAG_EN_POLL)
489 hdr |= SWITCHTEC_EVENT_EN_IRQ;
490 if (flags & SWITCHTEC_EVT_FLAG_EN_LOG)
491 hdr |= SWITCHTEC_EVENT_EN_LOG;
492 if (flags & SWITCHTEC_EVT_FLAG_EN_CLI)
493 hdr |= SWITCHTEC_EVENT_EN_CLI;
494 if (flags & SWITCHTEC_EVT_FLAG_EN_FATAL)
495 hdr |= SWITCHTEC_EVENT_FATAL;
496 if (flags & SWITCHTEC_EVT_FLAG_DIS_POLL)
497 hdr &= ~SWITCHTEC_EVENT_EN_IRQ;
498 if (flags & SWITCHTEC_EVT_FLAG_DIS_LOG)
499 hdr &= ~SWITCHTEC_EVENT_EN_LOG;
500 if (flags & SWITCHTEC_EVT_FLAG_DIS_CLI)
501 hdr &= ~SWITCHTEC_EVENT_EN_CLI;
502 if (flags & SWITCHTEC_EVT_FLAG_DIS_FATAL)
503 hdr &= ~SWITCHTEC_EVENT_FATAL;
504
505 if (flags)
506 __gas_write32(dev, hdr, reg);
507
508 return (hdr >> 5) & 0xFF;
509}
510
511int gasop_event_ctl(struct switchtec_dev *dev, enum switchtec_event_id e,
512 int index, int flags, uint32_t data[5])
513{
514 int nr_idxs;
515 int ret = 0;
516
517 if (e >= SWITCHTEC_MAX_EVENTS)
518 goto einval;
519
520 if (index == SWITCHTEC_EVT_IDX_ALL) {
521 if (event_regs[e].map_reg == global_ev_reg)
522 nr_idxs = 1;
523 else if (event_regs[e].map_reg == part_ev_reg)
524 nr_idxs = dev->partition_count;
525 else if (event_regs[e].map_reg == pff_ev_reg)
526 nr_idxs = gas_reg_read8(dev, top.pff_count);
527 else
528 goto einval;
529
530 for (index = 0; index < nr_idxs; index++) {
531 ret = event_ctl(dev, e, index, flags, data);
532 if (ret < 0)
533 return ret;
534 }
535 } else {
536 ret = event_ctl(dev, e, index, flags, data);
537 }
538
539 return ret;
540
541einval:
542 errno = EINVAL;
543 return -errno;
544}
545
546int gasop_event_wait_for(struct switchtec_dev *dev,
547 enum switchtec_event_id e, int index,
548 struct switchtec_event_summary *res,
549 int timeout_ms)
550{
551 struct timeval tv;
552 long long start, now;
553 struct switchtec_event_summary wait_for = {0};
554 int ret;
555
556 ret = switchtec_event_summary_set(&wait_for, e, index);
557 if (ret)
558 return ret;
559
560 ret = switchtec_event_ctl(dev, e, index,
561 SWITCHTEC_EVT_FLAG_CLEAR |
562 SWITCHTEC_EVT_FLAG_EN_POLL,
563 NULL);
564 if (ret < 0)
565 return ret;
566
567 ret = gettimeofday(&tv, NULL);
568 if (ret)
569 return ret;
570
571 now = start = ((tv.tv_sec) * 1000 + tv.tv_usec / 1000);
572
573 while (1) {
574 ret = switchtec_event_check(dev, &wait_for, res);
575 if (ret < 0)
576 return ret;
577
578 if (ret)
579 return 1;
580
581 ret = gettimeofday(&tv, NULL);
582 if (ret)
583 return ret;
584
585 now = ((tv.tv_sec) * 1000 + tv.tv_usec / 1000);
586
587 if (timeout_ms > 0 && now - start >= timeout_ms)
588 return 0;
589
590 usleep(5000);
591 }
592}
GAS Accessor functions.
int switchtec_event_ctl(struct switchtec_dev *dev, enum switchtec_event_id e, int index, int flags, uint32_t data[5])
Enable, disable and clear events or retrieve event data.
Definition platform.c:328
int switchtec_event_summary_set(struct switchtec_event_summary *sum, enum switchtec_event_id e, int index)
Set a bit corresponding to an event in a summary structure.
Definition events.c:176
int switchtec_event_check(struct switchtec_dev *dev, struct switchtec_event_summary *chk, struct switchtec_event_summary *res)
Check if one or more events have occurred.
Definition events.c:302
Event summary bitmaps.
Definition switchtec.h:343
uint64_t part_bitmap
Bitmap of partitions with active events.
Definition switchtec.h:345
uint64_t global
Bitmap of global events.
Definition switchtec.h:344
unsigned part[SWITCHTEC_MAX_PARTS]
Bitmap of events in each partition.
Definition switchtec.h:349
unsigned local_part
Bitmap of events in the local partition.
Definition switchtec.h:346
unsigned pff[SWITCHTEC_MAX_PFF_CSR]
Bitmap of events in each port function.
Definition switchtec.h:352
Information about a firmware image or partition.
Definition switchtec.h:305
size_t part_addr
Address of the partition.
Definition switchtec.h:310
size_t part_len
Length of the partition.
Definition switchtec.h:311
switchtec_event_id
Enumeration of all possible events.
Definition switchtec.h:358