Switchtec Userspace PROJECT_NUMBER = 3.1
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
50static const char gasop_noretry_cmds[] = {
51 [MRPC_SECURITY_CONFIG_SET] = 1,
52 [MRPC_KMSK_ENTRY_SET] = 1,
53 [MRPC_SECURE_STATE_SET] = 1,
54 [MRPC_BOOTUP_RESUME] = 1,
55 [MRPC_DBG_UNLOCK] = 1,
56 [MRPC_SECURITY_CONFIG_SET_GEN5] = 1,
57};
58static const int gasop_noretry_cmds_count = sizeof(gasop_noretry_cmds) /
59 sizeof(char);
60
61static inline bool gasop_is_no_retry_cmd(uint32_t cmd)
62{
63 cmd &= SWITCHTEC_CMD_MASK;
64
65 if (cmd >= gasop_noretry_cmds_count)
66 return 0;
67 return gasop_noretry_cmds[cmd];
68}
69
70int gasop_access_check(struct switchtec_dev *dev)
71{
72 uint32_t device_id;
73
74 device_id = gas_reg_read32(dev, sys_info.device_id);
75 if (device_id == -1)
76 return -1;
77 return 0;
78}
79
80void gasop_set_partition_info(struct switchtec_dev *dev)
81{
82 dev->partition = gas_reg_read8(dev, top.partition_id);
83 dev->partition_count = gas_reg_read8(dev, top.partition_count);
84}
85
86int gasop_cmd(struct switchtec_dev *dev, uint32_t cmd,
87 const void *payload, size_t payload_len, void *resp,
88 size_t resp_len)
89{
90 struct mrpc_regs __gas *mrpc = &dev->gas_map->mrpc;
91 int status;
92 int ret;
93
94 __memcpy_to_gas(dev, &mrpc->input_data, payload, payload_len);
95
96 /* Due to the possible unreliable nature of hardware
97 * communication, function __gas_write32() is implemented
98 * with automatic retry.
99 *
100 * This poses a potential issue when a command is critical
101 * and is expected to be sent only once (e.g., command that
102 * adds a KMSK entry to chip OTP memory). Retrying could
103 * cause the command be sent multiple times (and multiple
104 * KMSK entry being added, if unlucky).
105 *
106 * Here we filter out the specific commands and use 'no retry'
107 * version of gas_write32 for these commands.
108 */
109 if (gasop_is_no_retry_cmd(cmd))
110 __gas_write32_no_retry(dev, cmd, &mrpc->cmd);
111 else
112 __gas_write32(dev, cmd, &mrpc->cmd);
113
114 while (1) {
115 usleep(5000);
116
117 status = __gas_read32(dev, &mrpc->status);
118 if (status != SWITCHTEC_MRPC_STATUS_INPROGRESS)
119 break;
120 }
121
122 if (status == SWITCHTEC_MRPC_STATUS_INTERRUPTED) {
123 errno = ENXIO;
124 return -errno;
125 }
126
127 if(status == SWITCHTEC_MRPC_STATUS_ERROR) {
128 errno = __gas_read32(dev, &mrpc->ret_value);
129 return errno;
130 }
131
132 if (status != SWITCHTEC_MRPC_STATUS_DONE) {
133 errno = ENXIO;
134 return -errno;
135 }
136
137 ret = __gas_read32(dev, &mrpc->ret_value);
138 if (ret)
139 errno = ret;
140
141 if(resp)
142 __memcpy_from_gas(dev, resp, &mrpc->output_data, resp_len);
143
144 return ret;
145}
146
147int gasop_get_device_id(struct switchtec_dev *dev)
148{
149 return gas_reg_read32(dev, sys_info.device_id);
150}
151
152int gasop_get_fw_version(struct switchtec_dev *dev, char *buf,
153 size_t buflen)
154{
155 long long ver;
156
157 ver = gas_reg_read32(dev, sys_info.firmware_version);
158 version_to_string(ver, buf, buflen);
159
160 return 0;
161}
162
163int gasop_pff_to_port(struct switchtec_dev *dev, int pff,
164 int *partition, int *port)
165{
166 int i, part;
167 uint32_t reg;
168 struct part_cfg_regs __gas *pcfg;
169
170 *port = -1;
171
172 for (part = 0; part < dev->partition_count; part++) {
173 pcfg = &dev->gas_map->part_cfg[part];
174 *partition = part;
175
176 reg = __gas_read32(dev, &pcfg->usp_pff_inst_id);
177 if (reg == pff) {
178 *port = 0;
179 return 0;
180 }
181
182 reg = __gas_read32(dev, &pcfg->vep_pff_inst_id);
183 if (reg == pff) {
184 *port = SWITCHTEC_PFF_PORT_VEP;
185 return 0;
186 }
187
188 for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) {
189 reg = __gas_read32(dev, &pcfg->dsp_pff_inst_id[i]);
190 if (reg != pff)
191 continue;
192
193 *port = i + 1;
194 break;
195 }
196
197 if (*port != -1)
198 return 0;
199 }
200
201 errno = EINVAL;
202 return -EINVAL;
203}
204
205int gasop_port_to_pff(struct switchtec_dev *dev, int partition,
206 int port, int *pff)
207{
208 struct part_cfg_regs __gas *pcfg;
209
210 if (partition < 0) {
211 partition = dev->partition;
212 } else if (partition >= dev->partition_count) {
213 errno = EINVAL;
214 return -errno;
215 }
216
217 pcfg = &dev->gas_map->part_cfg[partition];
218
219 switch (port) {
220 case 0:
221 *pff = __gas_read32(dev, &pcfg->usp_pff_inst_id);
222 break;
223 case SWITCHTEC_PFF_PORT_VEP:
224 *pff = __gas_read32(dev, &pcfg->vep_pff_inst_id);
225 break;
226 default:
227 if (port > ARRAY_SIZE(pcfg->dsp_pff_inst_id)) {
228 errno = EINVAL;
229 return -errno;
230 }
231
232 *pff = __gas_read32(dev, &pcfg->dsp_pff_inst_id[port - 1]);
233 break;
234 }
235
236 return 0;
237}
238
239static void set_fw_info_part(struct switchtec_dev *dev,
240 struct switchtec_fw_image_info *info,
241 struct partition_info __gas *pi)
242{
243 info->part_addr = __gas_read32(dev, &pi->address);
244 info->part_len = __gas_read32(dev, &pi->length);
245}
246
247int gasop_flash_part(struct switchtec_dev *dev,
248 struct switchtec_fw_image_info *info,
249 enum switchtec_fw_image_part_id_gen3 part)
250{
251 struct flash_info_regs __gas *fi = &dev->gas_map->flash_info;
252 struct sys_info_regs __gas *si = &dev->gas_map->sys_info;
253 uint32_t active_addr = -1;
254 int val;
255
256 info->running = false;
257 info->active = false;
258
259 switch (part) {
260 case SWITCHTEC_FW_PART_ID_G3_IMG0:
261 active_addr = __gas_read32(dev, &fi->active_img.address);
262 set_fw_info_part(dev, info, &fi->img0);
263
264 val = __gas_read16(dev, &si->img_running);
265 if (val == SWITCHTEC_IMG0_RUNNING)
266 info->running = true;
267 break;
268
269 case SWITCHTEC_FW_PART_ID_G3_IMG1:
270 active_addr = __gas_read32(dev, &fi->active_img.address);
271 set_fw_info_part(dev, info, &fi->img1);
272
273 val = __gas_read16(dev, &si->img_running);
274 if (val == SWITCHTEC_IMG1_RUNNING)
275 info->running = true;
276 break;
277
278 case SWITCHTEC_FW_PART_ID_G3_DAT0:
279 active_addr = __gas_read32(dev, &fi->active_cfg.address);
280 set_fw_info_part(dev, info, &fi->cfg0);
281
282 val = __gas_read16(dev, &si->cfg_running);
283 if (val == SWITCHTEC_CFG0_RUNNING)
284 info->running = true;
285 break;
286
287 case SWITCHTEC_FW_PART_ID_G3_DAT1:
288 active_addr = __gas_read32(dev, &fi->active_cfg.address);
289 set_fw_info_part(dev, info, &fi->cfg1);
290
291 val = __gas_read16(dev, &si->cfg_running);
292 if (val == SWITCHTEC_CFG1_RUNNING)
293 info->running = true;
294 break;
295
296 case SWITCHTEC_FW_PART_ID_G3_NVLOG:
297 set_fw_info_part(dev, info, &fi->nvlog);
298 break;
299
300 default:
301 return -EINVAL;
302 }
303
304 if (info->part_addr == active_addr)
305 info->active = true;
306
307 return 0;
308}
309
310int gasop_event_summary(struct switchtec_dev *dev,
311 struct switchtec_event_summary *sum)
312{
313 int i;
314 uint32_t reg;
315
316 if (!sum)
317 return 0;
318
319 memset(sum, 0, sizeof(*sum));
320
321 sum->global = gas_reg_read32(dev, sw_event.global_summary);
322 sum->part_bitmap = gas_reg_read64(dev, sw_event.part_event_bitmap);
323
324 for (i = 0; i < dev->partition_count; i++) {
325 reg = gas_reg_read32(dev, part_cfg[i].part_event_summary);
326 sum->part[i] = reg;
327 if (i == dev->partition)
328 sum->local_part = reg;
329 }
330
331 for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
332 reg = gas_reg_read16(dev, pff_csr[i].vendor_id);
333 if (reg != MICROSEMI_VENDOR_ID)
334 break;
335
336 sum->pff[i] = gas_reg_read32(dev, pff_csr[i].pff_event_summary);
337 }
338
339 return 0;
340}
341
342static uint32_t __gas *global_ev_reg(struct switchtec_dev *dev,
343 size_t offset, int index)
344{
345 return (void __gas *)&dev->gas_map->sw_event + offset;
346}
347
348static uint32_t __gas *part_ev_reg(struct switchtec_dev *dev,
349 size_t offset, int index)
350{
351 return (void __gas *)&dev->gas_map->part_cfg[index] + offset;
352}
353
354static uint32_t __gas *pff_ev_reg(struct switchtec_dev *dev,
355 size_t offset, int index)
356{
357 return (void __gas *)&dev->gas_map->pff_csr[index] + offset;
358}
359
360#define EV_GLB(i, r)[SWITCHTEC_GLOBAL_EVT_ ## i] = \
361 {offsetof(struct sw_event_regs, r), global_ev_reg}
362#define EV_PAR(i, r)[SWITCHTEC_PART_EVT_ ## i] = \
363 {offsetof(struct part_cfg_regs, r), part_ev_reg}
364#define EV_PFF(i, r)[SWITCHTEC_PFF_EVT_ ## i] = \
365 {offsetof(struct pff_csr_regs, r), pff_ev_reg}
366
367static const struct event_reg {
368 size_t offset;
369 uint32_t __gas *(*map_reg)(struct switchtec_dev *stdev,
370 size_t offset, int index);
371} event_regs[] = {
372 EV_GLB(STACK_ERROR, stack_error_event_hdr),
373 EV_GLB(PPU_ERROR, ppu_error_event_hdr),
374 EV_GLB(ISP_ERROR, isp_error_event_hdr),
375 EV_GLB(SYS_RESET, sys_reset_event_hdr),
376 EV_GLB(FW_EXC, fw_exception_hdr),
377 EV_GLB(FW_NMI, fw_nmi_hdr),
378 EV_GLB(FW_NON_FATAL, fw_non_fatal_hdr),
379 EV_GLB(FW_FATAL, fw_fatal_hdr),
380 EV_GLB(TWI_MRPC_COMP, twi_mrpc_comp_hdr),
381 EV_GLB(TWI_MRPC_COMP_ASYNC, twi_mrpc_comp_async_hdr),
382 EV_GLB(CLI_MRPC_COMP, cli_mrpc_comp_hdr),
383 EV_GLB(CLI_MRPC_COMP_ASYNC, cli_mrpc_comp_async_hdr),
384 EV_GLB(GPIO_INT, gpio_interrupt_hdr),
385 EV_GLB(GFMS, gfms_event_hdr),
386 EV_PAR(PART_RESET, part_reset_hdr),
387 EV_PAR(MRPC_COMP, mrpc_comp_hdr),
388 EV_PAR(MRPC_COMP_ASYNC, mrpc_comp_async_hdr),
389 EV_PAR(DYN_PART_BIND_COMP, dyn_binding_hdr),
390 EV_PFF(AER_IN_P2P, aer_in_p2p_hdr),
391 EV_PFF(AER_IN_VEP, aer_in_vep_hdr),
392 EV_PFF(DPC, dpc_hdr),
393 EV_PFF(CTS, cts_hdr),
394 EV_PFF(UEC, uec_hdr),
395 EV_PFF(HOTPLUG, hotplug_hdr),
396 EV_PFF(IER, ier_hdr),
397 EV_PFF(THRESH, threshold_hdr),
398 EV_PFF(POWER_MGMT, power_mgmt_hdr),
399 EV_PFF(TLP_THROTTLING, tlp_throttling_hdr),
400 EV_PFF(FORCE_SPEED, force_speed_hdr),
401 EV_PFF(CREDIT_TIMEOUT, credit_timeout_hdr),
402 EV_PFF(LINK_STATE, link_state_hdr),
403};
404
405static uint32_t __gas *event_hdr_addr(struct switchtec_dev *dev,
406 enum switchtec_event_id e,
407 int index)
408{
409 size_t off;
410
411 if (e < 0 || e >= SWITCHTEC_MAX_EVENTS)
412 return NULL;
413
414 off = event_regs[e].offset;
415
416 if (event_regs[e].map_reg == part_ev_reg) {
417 if (index < 0)
418 index = dev->partition;
419 else if (index >= dev->partition_count)
420 return NULL;
421 } else if (event_regs[e].map_reg == pff_ev_reg) {
422 if (index < 0 || index >= SWITCHTEC_MAX_PFF_CSR)
423 return NULL;
424 }
425
426 return event_regs[e].map_reg(dev, off, index);
427}
428
429static int event_ctl(struct switchtec_dev *dev, enum switchtec_event_id e,
430 int index, int flags, uint32_t data[5])
431{
432 int i;
433 uint32_t __gas *reg;
434 uint32_t hdr;
435
436 reg = event_hdr_addr(dev, e, index);
437 if (!reg) {
438 errno = EINVAL;
439 return -errno;
440 }
441
442 hdr = __gas_read32(dev, reg);
443 if (data)
444 for (i = 0; i < 5; i++)
445 data[i] = __gas_read32(dev, &reg[i + 1]);
446
447 if (!(flags & SWITCHTEC_EVT_FLAG_CLEAR))
448 hdr &= ~SWITCHTEC_EVENT_CLEAR;
449 if (flags & SWITCHTEC_EVT_FLAG_EN_POLL)
450 hdr |= SWITCHTEC_EVENT_EN_IRQ;
451 if (flags & SWITCHTEC_EVT_FLAG_EN_LOG)
452 hdr |= SWITCHTEC_EVENT_EN_LOG;
453 if (flags & SWITCHTEC_EVT_FLAG_EN_CLI)
454 hdr |= SWITCHTEC_EVENT_EN_CLI;
455 if (flags & SWITCHTEC_EVT_FLAG_EN_FATAL)
456 hdr |= SWITCHTEC_EVENT_FATAL;
457 if (flags & SWITCHTEC_EVT_FLAG_DIS_POLL)
458 hdr &= ~SWITCHTEC_EVENT_EN_IRQ;
459 if (flags & SWITCHTEC_EVT_FLAG_DIS_LOG)
460 hdr &= ~SWITCHTEC_EVENT_EN_LOG;
461 if (flags & SWITCHTEC_EVT_FLAG_DIS_CLI)
462 hdr &= ~SWITCHTEC_EVENT_EN_CLI;
463 if (flags & SWITCHTEC_EVT_FLAG_DIS_FATAL)
464 hdr &= ~SWITCHTEC_EVENT_FATAL;
465
466 if (flags)
467 __gas_write32(dev, hdr, reg);
468
469 return (hdr >> 5) & 0xFF;
470}
471
472int gasop_event_ctl(struct switchtec_dev *dev, enum switchtec_event_id e,
473 int index, int flags, uint32_t data[5])
474{
475 int nr_idxs;
476 int ret = 0;
477
478 if (e >= SWITCHTEC_MAX_EVENTS)
479 goto einval;
480
481 if (index == SWITCHTEC_EVT_IDX_ALL) {
482 if (event_regs[e].map_reg == global_ev_reg)
483 nr_idxs = 1;
484 else if (event_regs[e].map_reg == part_ev_reg)
485 nr_idxs = dev->partition_count;
486 else if (event_regs[e].map_reg == pff_ev_reg)
487 nr_idxs = gas_reg_read8(dev, top.pff_count);
488 else
489 goto einval;
490
491 for (index = 0; index < nr_idxs; index++) {
492 ret = event_ctl(dev, e, index, flags, data);
493 if (ret < 0)
494 return ret;
495 }
496 } else {
497 ret = event_ctl(dev, e, index, flags, data);
498 }
499
500 return ret;
501
502einval:
503 errno = EINVAL;
504 return -errno;
505}
506
507int gasop_event_wait_for(struct switchtec_dev *dev,
508 enum switchtec_event_id e, int index,
509 struct switchtec_event_summary *res,
510 int timeout_ms)
511{
512 struct timeval tv;
513 long long start, now;
514 struct switchtec_event_summary wait_for = {0};
515 int ret;
516
517 ret = switchtec_event_summary_set(&wait_for, e, index);
518 if (ret)
519 return ret;
520
521 ret = switchtec_event_ctl(dev, e, index,
522 SWITCHTEC_EVT_FLAG_CLEAR |
523 SWITCHTEC_EVT_FLAG_EN_POLL,
524 NULL);
525 if (ret < 0)
526 return ret;
527
528 ret = gettimeofday(&tv, NULL);
529 if (ret)
530 return ret;
531
532 now = start = ((tv.tv_sec) * 1000 + tv.tv_usec / 1000);
533
534 while (1) {
535 ret = switchtec_event_check(dev, &wait_for, res);
536 if (ret < 0)
537 return ret;
538
539 if (ret)
540 return 1;
541
542 ret = gettimeofday(&tv, NULL);
543 if (ret)
544 return ret;
545
546 now = ((tv.tv_sec) * 1000 + tv.tv_usec / 1000);
547
548 if (timeout_ms > 0 && now - start >= timeout_ms)
549 return 0;
550
551 usleep(5000);
552 }
553}
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:313
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:175
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:297
Event summary bitmaps.
Definition: switchtec.h:289
uint64_t part_bitmap
Bitmap of partitions with active events.
Definition: switchtec.h:291
uint64_t global
Bitmap of global events.
Definition: switchtec.h:290
unsigned part[SWITCHTEC_MAX_PARTS]
Bitmap of events in each partition.
Definition: switchtec.h:295
unsigned local_part
Bitmap of events in the local partition.
Definition: switchtec.h:292
unsigned pff[SWITCHTEC_MAX_PFF_CSR]
Bitmap of events in each port function.
Definition: switchtec.h:298
Information about a firmware image or partition.
Definition: switchtec.h:251
size_t part_addr
Address of the partition.
Definition: switchtec.h:256
size_t part_len
Length of the partition.
Definition: switchtec.h:257
switchtec_event_id
Enumeration of all possible events.
Definition: switchtec.h:304