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