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 
50 static 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 };
58 static const int gasop_noretry_cmds_count = sizeof(gasop_noretry_cmds) /
59  sizeof(char);
60 
61 static 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 
70 int 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 
80 void 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 
86 int 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 
147 int gasop_get_device_id(struct switchtec_dev *dev)
148 {
149  return gas_reg_read32(dev, sys_info.device_id);
150 }
151 
152 int 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 
163 int 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 
205 int 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 
239 static 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 
247 int 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 
310 int 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 
342 static 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 
348 static 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 
354 static 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 
367 static 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 
405 static 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 
429 static 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 
472 int 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 
502 einval:
503  errno = EINVAL;
504  return -errno;
505 }
506 
507 int 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