Commit | Line | Data |
---|---|---|
df69ba43 SN |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ | |
3 | ||
011c7289 AB |
4 | #include <linux/printk.h> |
5 | #include <linux/dynamic_debug.h> | |
df69ba43 | 6 | #include <linux/module.h> |
df69ba43 SN |
7 | #include <linux/netdevice.h> |
8 | #include <linux/utsname.h> | |
1c79031f | 9 | #include <generated/utsrelease.h> |
36b20b7f | 10 | #include <linux/ctype.h> |
df69ba43 SN |
11 | |
12 | #include "ionic.h" | |
13 | #include "ionic_bus.h" | |
1a58e196 | 14 | #include "ionic_lif.h" |
fbfb8031 | 15 | #include "ionic_debugfs.h" |
df69ba43 SN |
16 | |
17 | MODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION); | |
217397da | 18 | MODULE_AUTHOR("Shannon Nelson <shannon.nelson@amd.com>"); |
df69ba43 | 19 | MODULE_LICENSE("GPL"); |
df69ba43 | 20 | |
fbfb8031 SN |
21 | static const char *ionic_error_to_str(enum ionic_status_code code) |
22 | { | |
23 | switch (code) { | |
24 | case IONIC_RC_SUCCESS: | |
25 | return "IONIC_RC_SUCCESS"; | |
26 | case IONIC_RC_EVERSION: | |
27 | return "IONIC_RC_EVERSION"; | |
28 | case IONIC_RC_EOPCODE: | |
29 | return "IONIC_RC_EOPCODE"; | |
30 | case IONIC_RC_EIO: | |
31 | return "IONIC_RC_EIO"; | |
32 | case IONIC_RC_EPERM: | |
33 | return "IONIC_RC_EPERM"; | |
34 | case IONIC_RC_EQID: | |
35 | return "IONIC_RC_EQID"; | |
36 | case IONIC_RC_EQTYPE: | |
37 | return "IONIC_RC_EQTYPE"; | |
38 | case IONIC_RC_ENOENT: | |
39 | return "IONIC_RC_ENOENT"; | |
40 | case IONIC_RC_EINTR: | |
41 | return "IONIC_RC_EINTR"; | |
42 | case IONIC_RC_EAGAIN: | |
43 | return "IONIC_RC_EAGAIN"; | |
44 | case IONIC_RC_ENOMEM: | |
45 | return "IONIC_RC_ENOMEM"; | |
46 | case IONIC_RC_EFAULT: | |
47 | return "IONIC_RC_EFAULT"; | |
48 | case IONIC_RC_EBUSY: | |
49 | return "IONIC_RC_EBUSY"; | |
50 | case IONIC_RC_EEXIST: | |
51 | return "IONIC_RC_EEXIST"; | |
52 | case IONIC_RC_EINVAL: | |
53 | return "IONIC_RC_EINVAL"; | |
54 | case IONIC_RC_ENOSPC: | |
55 | return "IONIC_RC_ENOSPC"; | |
56 | case IONIC_RC_ERANGE: | |
57 | return "IONIC_RC_ERANGE"; | |
58 | case IONIC_RC_BAD_ADDR: | |
59 | return "IONIC_RC_BAD_ADDR"; | |
60 | case IONIC_RC_DEV_CMD: | |
61 | return "IONIC_RC_DEV_CMD"; | |
b2133d8d SN |
62 | case IONIC_RC_ENOSUPP: |
63 | return "IONIC_RC_ENOSUPP"; | |
fbfb8031 SN |
64 | case IONIC_RC_ERROR: |
65 | return "IONIC_RC_ERROR"; | |
66 | case IONIC_RC_ERDMA: | |
67 | return "IONIC_RC_ERDMA"; | |
9e15410d SN |
68 | case IONIC_RC_EBAD_FW: |
69 | return "IONIC_RC_EBAD_FW"; | |
fbfb8031 SN |
70 | default: |
71 | return "IONIC_RC_UNKNOWN"; | |
72 | } | |
73 | } | |
74 | ||
75 | static int ionic_error_to_errno(enum ionic_status_code code) | |
76 | { | |
77 | switch (code) { | |
78 | case IONIC_RC_SUCCESS: | |
79 | return 0; | |
80 | case IONIC_RC_EVERSION: | |
81 | case IONIC_RC_EQTYPE: | |
82 | case IONIC_RC_EQID: | |
83 | case IONIC_RC_EINVAL: | |
84 | return -EINVAL; | |
7c372bac BC |
85 | case IONIC_RC_ENOSUPP: |
86 | return -EOPNOTSUPP; | |
fbfb8031 SN |
87 | case IONIC_RC_EPERM: |
88 | return -EPERM; | |
89 | case IONIC_RC_ENOENT: | |
90 | return -ENOENT; | |
91 | case IONIC_RC_EAGAIN: | |
92 | return -EAGAIN; | |
93 | case IONIC_RC_ENOMEM: | |
94 | return -ENOMEM; | |
95 | case IONIC_RC_EFAULT: | |
96 | return -EFAULT; | |
97 | case IONIC_RC_EBUSY: | |
98 | return -EBUSY; | |
99 | case IONIC_RC_EEXIST: | |
100 | return -EEXIST; | |
101 | case IONIC_RC_ENOSPC: | |
102 | return -ENOSPC; | |
103 | case IONIC_RC_ERANGE: | |
104 | return -ERANGE; | |
105 | case IONIC_RC_BAD_ADDR: | |
106 | return -EFAULT; | |
107 | case IONIC_RC_EOPCODE: | |
108 | case IONIC_RC_EINTR: | |
109 | case IONIC_RC_DEV_CMD: | |
110 | case IONIC_RC_ERROR: | |
111 | case IONIC_RC_ERDMA: | |
112 | case IONIC_RC_EIO: | |
113 | default: | |
114 | return -EIO; | |
115 | } | |
116 | } | |
117 | ||
118 | static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) | |
119 | { | |
120 | switch (opcode) { | |
121 | case IONIC_CMD_NOP: | |
122 | return "IONIC_CMD_NOP"; | |
123 | case IONIC_CMD_INIT: | |
124 | return "IONIC_CMD_INIT"; | |
125 | case IONIC_CMD_RESET: | |
126 | return "IONIC_CMD_RESET"; | |
127 | case IONIC_CMD_IDENTIFY: | |
128 | return "IONIC_CMD_IDENTIFY"; | |
129 | case IONIC_CMD_GETATTR: | |
130 | return "IONIC_CMD_GETATTR"; | |
131 | case IONIC_CMD_SETATTR: | |
132 | return "IONIC_CMD_SETATTR"; | |
133 | case IONIC_CMD_PORT_IDENTIFY: | |
134 | return "IONIC_CMD_PORT_IDENTIFY"; | |
135 | case IONIC_CMD_PORT_INIT: | |
136 | return "IONIC_CMD_PORT_INIT"; | |
137 | case IONIC_CMD_PORT_RESET: | |
138 | return "IONIC_CMD_PORT_RESET"; | |
139 | case IONIC_CMD_PORT_GETATTR: | |
140 | return "IONIC_CMD_PORT_GETATTR"; | |
141 | case IONIC_CMD_PORT_SETATTR: | |
142 | return "IONIC_CMD_PORT_SETATTR"; | |
143 | case IONIC_CMD_LIF_INIT: | |
144 | return "IONIC_CMD_LIF_INIT"; | |
145 | case IONIC_CMD_LIF_RESET: | |
146 | return "IONIC_CMD_LIF_RESET"; | |
147 | case IONIC_CMD_LIF_IDENTIFY: | |
148 | return "IONIC_CMD_LIF_IDENTIFY"; | |
149 | case IONIC_CMD_LIF_SETATTR: | |
150 | return "IONIC_CMD_LIF_SETATTR"; | |
151 | case IONIC_CMD_LIF_GETATTR: | |
152 | return "IONIC_CMD_LIF_GETATTR"; | |
61db421d SN |
153 | case IONIC_CMD_LIF_SETPHC: |
154 | return "IONIC_CMD_LIF_SETPHC"; | |
fbfb8031 SN |
155 | case IONIC_CMD_RX_MODE_SET: |
156 | return "IONIC_CMD_RX_MODE_SET"; | |
157 | case IONIC_CMD_RX_FILTER_ADD: | |
158 | return "IONIC_CMD_RX_FILTER_ADD"; | |
159 | case IONIC_CMD_RX_FILTER_DEL: | |
160 | return "IONIC_CMD_RX_FILTER_DEL"; | |
5b3f3f2a SN |
161 | case IONIC_CMD_Q_IDENTIFY: |
162 | return "IONIC_CMD_Q_IDENTIFY"; | |
fbfb8031 SN |
163 | case IONIC_CMD_Q_INIT: |
164 | return "IONIC_CMD_Q_INIT"; | |
165 | case IONIC_CMD_Q_CONTROL: | |
166 | return "IONIC_CMD_Q_CONTROL"; | |
167 | case IONIC_CMD_RDMA_RESET_LIF: | |
168 | return "IONIC_CMD_RDMA_RESET_LIF"; | |
169 | case IONIC_CMD_RDMA_CREATE_EQ: | |
170 | return "IONIC_CMD_RDMA_CREATE_EQ"; | |
171 | case IONIC_CMD_RDMA_CREATE_CQ: | |
172 | return "IONIC_CMD_RDMA_CREATE_CQ"; | |
173 | case IONIC_CMD_RDMA_CREATE_ADMINQ: | |
174 | return "IONIC_CMD_RDMA_CREATE_ADMINQ"; | |
175 | case IONIC_CMD_FW_DOWNLOAD: | |
176 | return "IONIC_CMD_FW_DOWNLOAD"; | |
177 | case IONIC_CMD_FW_CONTROL: | |
178 | return "IONIC_CMD_FW_CONTROL"; | |
87c905d8 SN |
179 | case IONIC_CMD_FW_DOWNLOAD_V1: |
180 | return "IONIC_CMD_FW_DOWNLOAD_V1"; | |
181 | case IONIC_CMD_FW_CONTROL_V1: | |
182 | return "IONIC_CMD_FW_CONTROL_V1"; | |
fbb39807 SN |
183 | case IONIC_CMD_VF_GETATTR: |
184 | return "IONIC_CMD_VF_GETATTR"; | |
185 | case IONIC_CMD_VF_SETATTR: | |
186 | return "IONIC_CMD_VF_SETATTR"; | |
fbfb8031 SN |
187 | default: |
188 | return "DEVCMD_UNKNOWN"; | |
189 | } | |
190 | } | |
191 | ||
938962d5 SN |
192 | static void ionic_adminq_flush(struct ionic_lif *lif) |
193 | { | |
4dcd4575 | 194 | struct ionic_admin_desc_info *desc_info; |
d60984d3 | 195 | struct ionic_admin_cmd *desc; |
e768929d SN |
196 | unsigned long irqflags; |
197 | struct ionic_queue *q; | |
198 | ||
199 | spin_lock_irqsave(&lif->adminq_lock, irqflags); | |
200 | if (!lif->adminqcq) { | |
201 | spin_unlock_irqrestore(&lif->adminq_lock, irqflags); | |
202 | return; | |
203 | } | |
938962d5 | 204 | |
e768929d | 205 | q = &lif->adminqcq->q; |
938962d5 | 206 | |
f1d2e894 | 207 | while (q->tail_idx != q->head_idx) { |
d60984d3 | 208 | desc = &q->adminq[q->tail_idx]; |
4dcd4575 | 209 | desc_info = &q->admin_info[q->tail_idx]; |
d60984d3 | 210 | memset(desc, 0, sizeof(union ionic_adminq_cmd)); |
4dcd4575 | 211 | desc_info->ctx = NULL; |
f1d2e894 | 212 | q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); |
938962d5 | 213 | } |
e768929d | 214 | spin_unlock_irqrestore(&lif->adminq_lock, irqflags); |
938962d5 SN |
215 | } |
216 | ||
8c9d956a SN |
217 | void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode, |
218 | u8 status, int err) | |
219 | { | |
4cc787bd SN |
220 | const char *stat_str; |
221 | ||
222 | stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" : | |
223 | ionic_error_to_str(status); | |
224 | ||
8c9d956a | 225 | netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n", |
4cc787bd | 226 | ionic_opcode_to_str(opcode), opcode, stat_str, err); |
8c9d956a SN |
227 | } |
228 | ||
938962d5 SN |
229 | static int ionic_adminq_check_err(struct ionic_lif *lif, |
230 | struct ionic_admin_ctx *ctx, | |
8c9d956a SN |
231 | const bool timeout, |
232 | const bool do_msg) | |
938962d5 | 233 | { |
938962d5 SN |
234 | int err = 0; |
235 | ||
236 | if (ctx->comp.comp.status || timeout) { | |
938962d5 SN |
237 | err = timeout ? -ETIMEDOUT : |
238 | ionic_error_to_errno(ctx->comp.comp.status); | |
239 | ||
8c9d956a SN |
240 | if (do_msg) |
241 | ionic_adminq_netdev_err_print(lif, ctx->cmd.cmd.opcode, | |
242 | ctx->comp.comp.status, err); | |
938962d5 SN |
243 | |
244 | if (timeout) | |
245 | ionic_adminq_flush(lif); | |
246 | } | |
247 | ||
248 | return err; | |
249 | } | |
250 | ||
65e548f6 | 251 | bool ionic_notifyq_service(struct ionic_cq *cq) |
05c94473 | 252 | { |
05c94473 | 253 | struct ionic_deferred_work *work; |
65e548f6 | 254 | union ionic_notifyq_comp *comp; |
05c94473 SN |
255 | struct net_device *netdev; |
256 | struct ionic_queue *q; | |
257 | struct ionic_lif *lif; | |
258 | u64 eid; | |
259 | ||
65e548f6 SN |
260 | comp = &((union ionic_notifyq_comp *)cq->base)[cq->tail_idx]; |
261 | ||
05c94473 | 262 | q = cq->bound_q; |
4dcd4575 | 263 | lif = q->admin_info[0].ctx; |
05c94473 SN |
264 | netdev = lif->netdev; |
265 | eid = le64_to_cpu(comp->event.eid); | |
266 | ||
267 | /* Have we run out of new completions to process? */ | |
268 | if ((s64)(eid - lif->last_eid) <= 0) | |
269 | return false; | |
270 | ||
271 | lif->last_eid = eid; | |
272 | ||
273 | dev_dbg(lif->ionic->dev, "notifyq event:\n"); | |
274 | dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1, | |
275 | comp, sizeof(*comp), true); | |
276 | ||
277 | switch (le16_to_cpu(comp->event.ecode)) { | |
278 | case IONIC_EVENT_LINK_CHANGE: | |
279 | ionic_link_status_check_request(lif, CAN_NOT_SLEEP); | |
280 | break; | |
281 | case IONIC_EVENT_RESET: | |
282 | if (lif->ionic->idev.fw_status_ready && | |
283 | !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && | |
284 | !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { | |
285 | work = kzalloc(sizeof(*work), GFP_ATOMIC); | |
286 | if (!work) { | |
287 | netdev_err(lif->netdev, "Reset event dropped\n"); | |
288 | clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); | |
289 | } else { | |
290 | work->type = IONIC_DW_TYPE_LIF_RESET; | |
9e25450d | 291 | ionic_lif_deferred_enqueue(lif, work); |
05c94473 SN |
292 | } |
293 | } | |
294 | break; | |
295 | default: | |
296 | netdev_warn(netdev, "Notifyq event ecode=%d eid=%lld\n", | |
297 | comp->event.ecode, eid); | |
298 | break; | |
299 | } | |
300 | ||
301 | return true; | |
302 | } | |
303 | ||
65e548f6 | 304 | bool ionic_adminq_service(struct ionic_cq *cq) |
05c94473 | 305 | { |
4dcd4575 | 306 | struct ionic_admin_desc_info *desc_info; |
ae24a8f8 | 307 | struct ionic_queue *q = cq->bound_q; |
ae24a8f8 SN |
308 | struct ionic_admin_comp *comp; |
309 | u16 index; | |
310 | ||
65e548f6 | 311 | comp = &((struct ionic_admin_comp *)cq->base)[cq->tail_idx]; |
05c94473 SN |
312 | |
313 | if (!color_match(comp->color, cq->done_color)) | |
314 | return false; | |
315 | ||
ae24a8f8 SN |
316 | /* check for empty queue */ |
317 | if (q->tail_idx == q->head_idx) | |
318 | return false; | |
319 | ||
320 | do { | |
4dcd4575 | 321 | desc_info = &q->admin_info[q->tail_idx]; |
ae24a8f8 SN |
322 | index = q->tail_idx; |
323 | q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); | |
8599bd4c SN |
324 | if (likely(desc_info->ctx)) { |
325 | struct ionic_admin_ctx *ctx = desc_info->ctx; | |
326 | ||
327 | memcpy(&ctx->comp, comp, sizeof(*comp)); | |
328 | ||
329 | dev_dbg(q->dev, "comp admin queue command:\n"); | |
330 | dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1, | |
331 | &ctx->comp, sizeof(ctx->comp), true); | |
332 | complete_all(&ctx->work); | |
333 | desc_info->ctx = NULL; | |
334 | } | |
ae24a8f8 | 335 | } while (index != le16_to_cpu(comp->comp_index)); |
05c94473 SN |
336 | |
337 | return true; | |
338 | } | |
339 | ||
b69585bf AH |
340 | bool ionic_adminq_poke_doorbell(struct ionic_queue *q) |
341 | { | |
342 | struct ionic_lif *lif = q->lif; | |
343 | unsigned long now, then, dif; | |
344 | unsigned long irqflags; | |
345 | ||
346 | spin_lock_irqsave(&lif->adminq_lock, irqflags); | |
347 | ||
348 | if (q->tail_idx == q->head_idx) { | |
349 | spin_unlock_irqrestore(&lif->adminq_lock, irqflags); | |
350 | return false; | |
351 | } | |
352 | ||
353 | now = READ_ONCE(jiffies); | |
354 | then = q->dbell_jiffies; | |
355 | dif = now - then; | |
356 | ||
357 | if (dif > q->dbell_deadline) { | |
358 | ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, | |
359 | q->dbval | q->head_idx); | |
360 | ||
361 | q->dbell_jiffies = now; | |
362 | } | |
363 | ||
364 | spin_unlock_irqrestore(&lif->adminq_lock, irqflags); | |
365 | ||
366 | return true; | |
367 | } | |
368 | ||
4f1704fa | 369 | int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) |
938962d5 | 370 | { |
4dcd4575 | 371 | struct ionic_admin_desc_info *desc_info; |
d60984d3 | 372 | struct ionic_admin_cmd *desc; |
e768929d | 373 | unsigned long irqflags; |
f1d2e894 | 374 | struct ionic_queue *q; |
938962d5 SN |
375 | int err = 0; |
376 | ||
e768929d SN |
377 | spin_lock_irqsave(&lif->adminq_lock, irqflags); |
378 | if (!lif->adminqcq) { | |
379 | spin_unlock_irqrestore(&lif->adminq_lock, irqflags); | |
a4674f34 | 380 | return -EIO; |
e768929d | 381 | } |
a4674f34 | 382 | |
f1d2e894 | 383 | q = &lif->adminqcq->q; |
a4674f34 | 384 | |
f1d2e894 | 385 | if (!ionic_q_has_space(q, 1)) { |
938962d5 SN |
386 | err = -ENOSPC; |
387 | goto err_out; | |
388 | } | |
389 | ||
97ca4865 SN |
390 | err = ionic_heartbeat_check(lif->ionic); |
391 | if (err) | |
392 | goto err_out; | |
393 | ||
4dcd4575 SN |
394 | desc_info = &q->admin_info[q->head_idx]; |
395 | desc_info->ctx = ctx; | |
396 | ||
d60984d3 SN |
397 | desc = &q->adminq[q->head_idx]; |
398 | memcpy(desc, &ctx->cmd, sizeof(ctx->cmd)); | |
938962d5 SN |
399 | |
400 | dev_dbg(&lif->netdev->dev, "post admin queue command:\n"); | |
401 | dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1, | |
402 | &ctx->cmd, sizeof(ctx->cmd), true); | |
403 | ||
4dcd4575 | 404 | ionic_q_post(q, true); |
938962d5 SN |
405 | |
406 | err_out: | |
e768929d | 407 | spin_unlock_irqrestore(&lif->adminq_lock, irqflags); |
938962d5 SN |
408 | |
409 | return err; | |
410 | } | |
411 | ||
8c9d956a SN |
412 | int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, |
413 | const int err, const bool do_msg) | |
938962d5 SN |
414 | { |
415 | struct net_device *netdev = lif->netdev; | |
a095e477 SN |
416 | unsigned long time_limit; |
417 | unsigned long time_start; | |
418 | unsigned long time_done; | |
938962d5 SN |
419 | unsigned long remaining; |
420 | const char *name; | |
938962d5 | 421 | |
a095e477 SN |
422 | name = ionic_opcode_to_str(ctx->cmd.cmd.opcode); |
423 | ||
938962d5 | 424 | if (err) { |
8c9d956a | 425 | if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) |
c672412f SN |
426 | netdev_err(netdev, "Posting of %s (%d) failed: %d\n", |
427 | name, ctx->cmd.cmd.opcode, err); | |
bc43ed4f | 428 | ctx->comp.comp.status = IONIC_RC_ERROR; |
938962d5 SN |
429 | return err; |
430 | } | |
431 | ||
a095e477 SN |
432 | time_start = jiffies; |
433 | time_limit = time_start + HZ * (ulong)DEVCMD_TIMEOUT; | |
434 | do { | |
435 | remaining = wait_for_completion_timeout(&ctx->work, | |
436 | IONIC_ADMINQ_TIME_SLICE); | |
437 | ||
438 | /* check for done */ | |
439 | if (remaining) | |
440 | break; | |
441 | ||
ec8ee714 | 442 | /* force a check of FW status and break out if FW reset */ |
896de449 | 443 | ionic_heartbeat_check(lif->ionic); |
398d1e37 SN |
444 | if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) && |
445 | !lif->ionic->idev.fw_status_ready) || | |
446 | test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { | |
8c9d956a | 447 | if (do_msg) |
ec8ee714 SN |
448 | netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n", |
449 | name, ctx->cmd.cmd.opcode); | |
bc43ed4f | 450 | ctx->comp.comp.status = IONIC_RC_ERROR; |
a095e477 SN |
451 | return -ENXIO; |
452 | } | |
453 | ||
454 | } while (time_before(jiffies, time_limit)); | |
455 | time_done = jiffies; | |
456 | ||
457 | dev_dbg(lif->ionic->dev, "%s: elapsed %d msecs\n", | |
458 | __func__, jiffies_to_msecs(time_done - time_start)); | |
459 | ||
8c9d956a SN |
460 | return ionic_adminq_check_err(lif, ctx, |
461 | time_after_eq(time_done, time_limit), | |
462 | do_msg); | |
938962d5 SN |
463 | } |
464 | ||
ca5fdf9a SN |
465 | static int __ionic_adminq_post_wait(struct ionic_lif *lif, |
466 | struct ionic_admin_ctx *ctx, | |
467 | const bool do_msg) | |
4f1704fa SN |
468 | { |
469 | int err; | |
470 | ||
ca5fdf9a SN |
471 | if (!ionic_is_fw_running(&lif->ionic->idev)) |
472 | return 0; | |
473 | ||
4f1704fa SN |
474 | err = ionic_adminq_post(lif, ctx); |
475 | ||
ca5fdf9a | 476 | return ionic_adminq_wait(lif, ctx, err, do_msg); |
8c9d956a SN |
477 | } |
478 | ||
ca5fdf9a | 479 | int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) |
8c9d956a | 480 | { |
ca5fdf9a SN |
481 | return __ionic_adminq_post_wait(lif, ctx, true); |
482 | } | |
8c9d956a | 483 | |
ca5fdf9a SN |
484 | int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) |
485 | { | |
486 | return __ionic_adminq_post_wait(lif, ctx, false); | |
4f1704fa SN |
487 | } |
488 | ||
97ca4865 SN |
489 | static void ionic_dev_cmd_clean(struct ionic *ionic) |
490 | { | |
bc0bf9de | 491 | struct ionic_dev *idev = &ionic->idev; |
97ca4865 | 492 | |
7662fad3 SN |
493 | if (!idev->dev_cmd_regs) |
494 | return; | |
495 | ||
bc0bf9de SN |
496 | iowrite32(0, &idev->dev_cmd_regs->doorbell); |
497 | memset_io(&idev->dev_cmd_regs->cmd, 0, sizeof(idev->dev_cmd_regs->cmd)); | |
97ca4865 SN |
498 | } |
499 | ||
b640b552 BC |
500 | void ionic_dev_cmd_dev_err_print(struct ionic *ionic, u8 opcode, u8 status, |
501 | int err) | |
502 | { | |
503 | const char *stat_str; | |
504 | ||
505 | stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" : | |
506 | ionic_error_to_str(status); | |
507 | ||
508 | dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n", | |
509 | ionic_opcode_to_str(opcode), opcode, stat_str, err); | |
510 | } | |
511 | ||
512 | static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds, | |
513 | const bool do_msg) | |
fbfb8031 SN |
514 | { |
515 | struct ionic_dev *idev = &ionic->idev; | |
516 | unsigned long start_time; | |
517 | unsigned long max_wait; | |
518 | unsigned long duration; | |
ec8ee714 | 519 | bool fw_up; |
fbfb8031 | 520 | int opcode; |
5466491c | 521 | bool done; |
fbfb8031 SN |
522 | int err; |
523 | ||
fbfb8031 SN |
524 | /* Wait for dev cmd to complete, retrying if we get EAGAIN, |
525 | * but don't wait any longer than max_seconds. | |
526 | */ | |
527 | max_wait = jiffies + (max_seconds * HZ); | |
528 | try_again: | |
5466491c | 529 | done = false; |
24f11024 | 530 | opcode = idev->opcode; |
fbfb8031 | 531 | start_time = jiffies; |
ec8ee714 SN |
532 | for (fw_up = ionic_is_fw_running(idev); |
533 | !done && fw_up && time_before(jiffies, max_wait); | |
534 | fw_up = ionic_is_fw_running(idev)) { | |
fbfb8031 SN |
535 | done = ionic_dev_cmd_done(idev); |
536 | if (done) | |
537 | break; | |
30b5191a | 538 | usleep_range(100, 200); |
ec8ee714 | 539 | } |
fbfb8031 SN |
540 | duration = jiffies - start_time; |
541 | ||
fbfb8031 SN |
542 | dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n", |
543 | ionic_opcode_to_str(opcode), opcode, | |
544 | done, duration / HZ, duration); | |
545 | ||
ec8ee714 SN |
546 | if (!done && !fw_up) { |
547 | ionic_dev_cmd_clean(ionic); | |
548 | dev_warn(ionic->dev, "DEVCMD %s (%d) interrupted - FW is down\n", | |
549 | ionic_opcode_to_str(opcode), opcode); | |
97ca4865 SN |
550 | return -ENXIO; |
551 | } | |
552 | ||
fbfb8031 | 553 | if (!done && !time_before(jiffies, max_wait)) { |
97ca4865 | 554 | ionic_dev_cmd_clean(ionic); |
fbfb8031 SN |
555 | dev_warn(ionic->dev, "DEVCMD %s (%d) timeout after %ld secs\n", |
556 | ionic_opcode_to_str(opcode), opcode, max_seconds); | |
557 | return -ETIMEDOUT; | |
558 | } | |
559 | ||
560 | err = ionic_dev_cmd_status(&ionic->idev); | |
561 | if (err) { | |
30b5191a SN |
562 | if (err == IONIC_RC_EAGAIN && |
563 | time_before(jiffies, (max_wait - HZ))) { | |
564 | dev_dbg(ionic->dev, "DEV_CMD %s (%d), %s (%d) retrying...\n", | |
fbfb8031 SN |
565 | ionic_opcode_to_str(opcode), opcode, |
566 | ionic_error_to_str(err), err); | |
567 | ||
fbfb8031 | 568 | iowrite32(0, &idev->dev_cmd_regs->done); |
0fc4dd45 | 569 | msleep(1000); |
fbfb8031 SN |
570 | iowrite32(1, &idev->dev_cmd_regs->doorbell); |
571 | goto try_again; | |
572 | } | |
573 | ||
30b5191a | 574 | if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN)) |
b640b552 BC |
575 | if (do_msg) |
576 | ionic_dev_cmd_dev_err_print(ionic, opcode, err, | |
577 | ionic_error_to_errno(err)); | |
fbfb8031 SN |
578 | |
579 | return ionic_error_to_errno(err); | |
580 | } | |
581 | ||
0fc4dd45 SN |
582 | ionic_dev_cmd_clean(ionic); |
583 | ||
fbfb8031 SN |
584 | return 0; |
585 | } | |
586 | ||
b640b552 BC |
587 | int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) |
588 | { | |
589 | return __ionic_dev_cmd_wait(ionic, max_seconds, true); | |
590 | } | |
591 | ||
592 | int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_seconds) | |
593 | { | |
594 | return __ionic_dev_cmd_wait(ionic, max_seconds, false); | |
595 | } | |
596 | ||
fbfb8031 SN |
597 | int ionic_setup(struct ionic *ionic) |
598 | { | |
599 | int err; | |
600 | ||
601 | err = ionic_dev_setup(ionic); | |
602 | if (err) | |
603 | return err; | |
5c784311 | 604 | ionic_reset(ionic); |
fbfb8031 SN |
605 | |
606 | return 0; | |
607 | } | |
608 | ||
609 | int ionic_identify(struct ionic *ionic) | |
610 | { | |
611 | struct ionic_identity *ident = &ionic->ident; | |
612 | struct ionic_dev *idev = &ionic->idev; | |
613 | size_t sz; | |
614 | int err; | |
615 | ||
616 | memset(ident, 0, sizeof(*ident)); | |
617 | ||
618 | ident->drv.os_type = cpu_to_le32(IONIC_OS_TYPE_LINUX); | |
ad0ebd8b JS |
619 | strscpy(ident->drv.driver_ver_str, UTS_RELEASE, |
620 | sizeof(ident->drv.driver_ver_str)); | |
fbfb8031 SN |
621 | |
622 | mutex_lock(&ionic->dev_cmd_lock); | |
623 | ||
624 | sz = min(sizeof(ident->drv), sizeof(idev->dev_cmd_regs->data)); | |
625 | memcpy_toio(&idev->dev_cmd_regs->data, &ident->drv, sz); | |
626 | ||
f43a96d9 | 627 | ionic_dev_cmd_identify(idev, IONIC_DEV_IDENTITY_VERSION_2); |
fbfb8031 SN |
628 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); |
629 | if (!err) { | |
630 | sz = min(sizeof(ident->dev), sizeof(idev->dev_cmd_regs->data)); | |
631 | memcpy_fromio(&ident->dev, &idev->dev_cmd_regs->data, sz); | |
632 | } | |
fbfb8031 SN |
633 | mutex_unlock(&ionic->dev_cmd_lock); |
634 | ||
a21b5d49 | 635 | if (err) { |
36b20b7f | 636 | dev_err(ionic->dev, "Cannot identify ionic: %d\n", err); |
a21b5d49 SN |
637 | goto err_out; |
638 | } | |
fbfb8031 | 639 | |
36b20b7f SN |
640 | if (isprint(idev->dev_info.fw_version[0]) && |
641 | isascii(idev->dev_info.fw_version[0])) | |
642 | dev_info(ionic->dev, "FW: %.*s\n", | |
643 | (int)(sizeof(idev->dev_info.fw_version) - 1), | |
644 | idev->dev_info.fw_version); | |
645 | else | |
646 | dev_info(ionic->dev, "FW: (invalid string) 0x%02x 0x%02x 0x%02x 0x%02x ...\n", | |
647 | (u8)idev->dev_info.fw_version[0], | |
648 | (u8)idev->dev_info.fw_version[1], | |
649 | (u8)idev->dev_info.fw_version[2], | |
650 | (u8)idev->dev_info.fw_version[3]); | |
651 | ||
a21b5d49 SN |
652 | err = ionic_lif_identify(ionic, IONIC_LIF_TYPE_CLASSIC, |
653 | &ionic->ident.lif); | |
654 | if (err) { | |
655 | dev_err(ionic->dev, "Cannot identify LIFs: %d\n", err); | |
656 | goto err_out; | |
657 | } | |
fbfb8031 SN |
658 | |
659 | return 0; | |
660 | ||
a21b5d49 | 661 | err_out: |
fbfb8031 SN |
662 | return err; |
663 | } | |
664 | ||
665 | int ionic_init(struct ionic *ionic) | |
666 | { | |
667 | struct ionic_dev *idev = &ionic->idev; | |
668 | int err; | |
669 | ||
670 | mutex_lock(&ionic->dev_cmd_lock); | |
671 | ionic_dev_cmd_init(idev); | |
672 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); | |
673 | mutex_unlock(&ionic->dev_cmd_lock); | |
674 | ||
675 | return err; | |
676 | } | |
677 | ||
678 | int ionic_reset(struct ionic *ionic) | |
679 | { | |
680 | struct ionic_dev *idev = &ionic->idev; | |
681 | int err; | |
682 | ||
b8fd0271 BC |
683 | if (!ionic_is_fw_running(idev)) |
684 | return 0; | |
685 | ||
fbfb8031 SN |
686 | mutex_lock(&ionic->dev_cmd_lock); |
687 | ionic_dev_cmd_reset(idev); | |
688 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); | |
689 | mutex_unlock(&ionic->dev_cmd_lock); | |
690 | ||
691 | return err; | |
692 | } | |
693 | ||
04436595 SN |
694 | int ionic_port_identify(struct ionic *ionic) |
695 | { | |
696 | struct ionic_identity *ident = &ionic->ident; | |
697 | struct ionic_dev *idev = &ionic->idev; | |
698 | size_t sz; | |
699 | int err; | |
700 | ||
701 | mutex_lock(&ionic->dev_cmd_lock); | |
702 | ||
703 | ionic_dev_cmd_port_identify(idev); | |
704 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); | |
705 | if (!err) { | |
706 | sz = min(sizeof(ident->port), sizeof(idev->dev_cmd_regs->data)); | |
707 | memcpy_fromio(&ident->port, &idev->dev_cmd_regs->data, sz); | |
708 | } | |
709 | ||
710 | mutex_unlock(&ionic->dev_cmd_lock); | |
711 | ||
712 | return err; | |
713 | } | |
714 | ||
715 | int ionic_port_init(struct ionic *ionic) | |
716 | { | |
717 | struct ionic_identity *ident = &ionic->ident; | |
718 | struct ionic_dev *idev = &ionic->idev; | |
719 | size_t sz; | |
720 | int err; | |
721 | ||
04436595 | 722 | if (!idev->port_info) { |
ddc5911b SN |
723 | idev->port_info_sz = ALIGN(sizeof(*idev->port_info), PAGE_SIZE); |
724 | idev->port_info = dma_alloc_coherent(ionic->dev, | |
725 | idev->port_info_sz, | |
726 | &idev->port_info_pa, | |
727 | GFP_KERNEL); | |
c0c682ee | 728 | if (!idev->port_info) |
ddc5911b | 729 | return -ENOMEM; |
04436595 SN |
730 | } |
731 | ||
732 | sz = min(sizeof(ident->port.config), sizeof(idev->dev_cmd_regs->data)); | |
733 | ||
734 | mutex_lock(&ionic->dev_cmd_lock); | |
735 | ||
736 | memcpy_toio(&idev->dev_cmd_regs->data, &ident->port.config, sz); | |
737 | ionic_dev_cmd_port_init(idev); | |
738 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); | |
739 | ||
740 | ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_UP); | |
896de449 | 741 | ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); |
04436595 SN |
742 | |
743 | mutex_unlock(&ionic->dev_cmd_lock); | |
744 | if (err) { | |
745 | dev_err(ionic->dev, "Failed to init port\n"); | |
746 | dma_free_coherent(ionic->dev, idev->port_info_sz, | |
747 | idev->port_info, idev->port_info_pa); | |
748 | idev->port_info = NULL; | |
749 | idev->port_info_pa = 0; | |
750 | } | |
751 | ||
752 | return err; | |
753 | } | |
754 | ||
755 | int ionic_port_reset(struct ionic *ionic) | |
756 | { | |
757 | struct ionic_dev *idev = &ionic->idev; | |
b8fd0271 | 758 | int err = 0; |
04436595 SN |
759 | |
760 | if (!idev->port_info) | |
761 | return 0; | |
762 | ||
b8fd0271 BC |
763 | if (ionic_is_fw_running(idev)) { |
764 | mutex_lock(&ionic->dev_cmd_lock); | |
765 | ionic_dev_cmd_port_reset(idev); | |
766 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); | |
767 | mutex_unlock(&ionic->dev_cmd_lock); | |
768 | } | |
04436595 SN |
769 | |
770 | dma_free_coherent(ionic->dev, idev->port_info_sz, | |
771 | idev->port_info, idev->port_info_pa); | |
772 | ||
773 | idev->port_info = NULL; | |
774 | idev->port_info_pa = 0; | |
775 | ||
04436595 SN |
776 | return err; |
777 | } | |
778 | ||
df69ba43 SN |
779 | static int __init ionic_init_module(void) |
780 | { | |
280c0f7c YC |
781 | int ret; |
782 | ||
fbfb8031 | 783 | ionic_debugfs_create(); |
280c0f7c YC |
784 | ret = ionic_bus_register_driver(); |
785 | if (ret) | |
786 | ionic_debugfs_destroy(); | |
787 | ||
788 | return ret; | |
df69ba43 SN |
789 | } |
790 | ||
791 | static void __exit ionic_cleanup_module(void) | |
792 | { | |
793 | ionic_bus_unregister_driver(); | |
fbfb8031 | 794 | ionic_debugfs_destroy(); |
df69ba43 SN |
795 | |
796 | pr_info("%s removed\n", IONIC_DRV_NAME); | |
797 | } | |
798 | ||
799 | module_init(ionic_init_module); | |
800 | module_exit(ionic_cleanup_module); |