diff options
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/cmd.c | 699 |
1 files changed, 672 insertions, 27 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index b27654e5d544..9c0bdcabaea0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c | |||
@@ -39,12 +39,18 @@ | |||
39 | #include <linux/errno.h> | 39 | #include <linux/errno.h> |
40 | 40 | ||
41 | #include <linux/mlx4/cmd.h> | 41 | #include <linux/mlx4/cmd.h> |
42 | #include <linux/semaphore.h> | ||
42 | 43 | ||
43 | #include <asm/io.h> | 44 | #include <asm/io.h> |
44 | 45 | ||
45 | #include "mlx4.h" | 46 | #include "mlx4.h" |
47 | #include "fw.h" | ||
46 | 48 | ||
47 | #define CMD_POLL_TOKEN 0xffff | 49 | #define CMD_POLL_TOKEN 0xffff |
50 | #define INBOX_MASK 0xffffffffffffff00ULL | ||
51 | |||
52 | #define CMD_CHAN_VER 1 | ||
53 | #define CMD_CHAN_IF_REV 1 | ||
48 | 54 | ||
49 | enum { | 55 | enum { |
50 | /* command completed successfully: */ | 56 | /* command completed successfully: */ |
@@ -110,8 +116,12 @@ struct mlx4_cmd_context { | |||
110 | int next; | 116 | int next; |
111 | u64 out_param; | 117 | u64 out_param; |
112 | u16 token; | 118 | u16 token; |
119 | u8 fw_status; | ||
113 | }; | 120 | }; |
114 | 121 | ||
122 | static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, | ||
123 | struct mlx4_vhcr_cmd *in_vhcr); | ||
124 | |||
115 | static int mlx4_status_to_errno(u8 status) | 125 | static int mlx4_status_to_errno(u8 status) |
116 | { | 126 | { |
117 | static const int trans_table[] = { | 127 | static const int trans_table[] = { |
@@ -142,6 +152,125 @@ static int mlx4_status_to_errno(u8 status) | |||
142 | return trans_table[status]; | 152 | return trans_table[status]; |
143 | } | 153 | } |
144 | 154 | ||
155 | static int comm_pending(struct mlx4_dev *dev) | ||
156 | { | ||
157 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
158 | u32 status = readl(&priv->mfunc.comm->slave_read); | ||
159 | |||
160 | return (swab32(status) >> 31) != priv->cmd.comm_toggle; | ||
161 | } | ||
162 | |||
163 | static void mlx4_comm_cmd_post(struct mlx4_dev *dev, u8 cmd, u16 param) | ||
164 | { | ||
165 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
166 | u32 val; | ||
167 | |||
168 | priv->cmd.comm_toggle ^= 1; | ||
169 | val = param | (cmd << 16) | (priv->cmd.comm_toggle << 31); | ||
170 | __raw_writel((__force u32) cpu_to_be32(val), | ||
171 | &priv->mfunc.comm->slave_write); | ||
172 | mmiowb(); | ||
173 | } | ||
174 | |||
175 | /* dummy procedure for this patch */ | ||
176 | int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe) | ||
177 | { | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static int mlx4_comm_cmd_poll(struct mlx4_dev *dev, u8 cmd, u16 param, | ||
182 | unsigned long timeout) | ||
183 | { | ||
184 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
185 | unsigned long end; | ||
186 | int err = 0; | ||
187 | int ret_from_pending = 0; | ||
188 | |||
189 | /* First, verify that the master reports correct status */ | ||
190 | if (comm_pending(dev)) { | ||
191 | mlx4_warn(dev, "Communication channel is not idle." | ||
192 | "my toggle is %d (cmd:0x%x)\n", | ||
193 | priv->cmd.comm_toggle, cmd); | ||
194 | return -EAGAIN; | ||
195 | } | ||
196 | |||
197 | /* Write command */ | ||
198 | down(&priv->cmd.poll_sem); | ||
199 | mlx4_comm_cmd_post(dev, cmd, param); | ||
200 | |||
201 | end = msecs_to_jiffies(timeout) + jiffies; | ||
202 | while (comm_pending(dev) && time_before(jiffies, end)) | ||
203 | cond_resched(); | ||
204 | ret_from_pending = comm_pending(dev); | ||
205 | if (ret_from_pending) { | ||
206 | /* check if the slave is trying to boot in the middle of | ||
207 | * FLR process. The only non-zero result in the RESET command | ||
208 | * is MLX4_DELAY_RESET_SLAVE*/ | ||
209 | if ((MLX4_COMM_CMD_RESET == cmd)) { | ||
210 | mlx4_warn(dev, "Got slave FLRed from Communication" | ||
211 | " channel (ret:0x%x)\n", ret_from_pending); | ||
212 | err = MLX4_DELAY_RESET_SLAVE; | ||
213 | } else { | ||
214 | mlx4_warn(dev, "Communication channel timed out\n"); | ||
215 | err = -ETIMEDOUT; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | up(&priv->cmd.poll_sem); | ||
220 | return err; | ||
221 | } | ||
222 | |||
223 | static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 op, | ||
224 | u16 param, unsigned long timeout) | ||
225 | { | ||
226 | struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; | ||
227 | struct mlx4_cmd_context *context; | ||
228 | int err = 0; | ||
229 | |||
230 | down(&cmd->event_sem); | ||
231 | |||
232 | spin_lock(&cmd->context_lock); | ||
233 | BUG_ON(cmd->free_head < 0); | ||
234 | context = &cmd->context[cmd->free_head]; | ||
235 | context->token += cmd->token_mask + 1; | ||
236 | cmd->free_head = context->next; | ||
237 | spin_unlock(&cmd->context_lock); | ||
238 | |||
239 | init_completion(&context->done); | ||
240 | |||
241 | mlx4_comm_cmd_post(dev, op, param); | ||
242 | |||
243 | if (!wait_for_completion_timeout(&context->done, | ||
244 | msecs_to_jiffies(timeout))) { | ||
245 | err = -EBUSY; | ||
246 | goto out; | ||
247 | } | ||
248 | |||
249 | err = context->result; | ||
250 | if (err && context->fw_status != CMD_STAT_MULTI_FUNC_REQ) { | ||
251 | mlx4_err(dev, "command 0x%x failed: fw status = 0x%x\n", | ||
252 | op, context->fw_status); | ||
253 | goto out; | ||
254 | } | ||
255 | |||
256 | out: | ||
257 | spin_lock(&cmd->context_lock); | ||
258 | context->next = cmd->free_head; | ||
259 | cmd->free_head = context - cmd->context; | ||
260 | spin_unlock(&cmd->context_lock); | ||
261 | |||
262 | up(&cmd->event_sem); | ||
263 | return err; | ||
264 | } | ||
265 | |||
266 | static int mlx4_comm_cmd(struct mlx4_dev *dev, u8 cmd, u16 param, | ||
267 | unsigned long timeout) | ||
268 | { | ||
269 | if (mlx4_priv(dev)->cmd.use_events) | ||
270 | return mlx4_comm_cmd_wait(dev, cmd, param, timeout); | ||
271 | return mlx4_comm_cmd_poll(dev, cmd, param, timeout); | ||
272 | } | ||
273 | |||
145 | static int cmd_pending(struct mlx4_dev *dev) | 274 | static int cmd_pending(struct mlx4_dev *dev) |
146 | { | 275 | { |
147 | u32 status = readl(mlx4_priv(dev)->cmd.hcr + HCR_STATUS_OFFSET); | 276 | u32 status = readl(mlx4_priv(dev)->cmd.hcr + HCR_STATUS_OFFSET); |
@@ -167,8 +296,10 @@ static int mlx4_cmd_post(struct mlx4_dev *dev, u64 in_param, u64 out_param, | |||
167 | end += msecs_to_jiffies(GO_BIT_TIMEOUT_MSECS); | 296 | end += msecs_to_jiffies(GO_BIT_TIMEOUT_MSECS); |
168 | 297 | ||
169 | while (cmd_pending(dev)) { | 298 | while (cmd_pending(dev)) { |
170 | if (time_after_eq(jiffies, end)) | 299 | if (time_after_eq(jiffies, end)) { |
300 | mlx4_err(dev, "%s:cmd_pending failed\n", __func__); | ||
171 | goto out; | 301 | goto out; |
302 | } | ||
172 | cond_resched(); | 303 | cond_resched(); |
173 | } | 304 | } |
174 | 305 | ||
@@ -192,7 +323,7 @@ static int mlx4_cmd_post(struct mlx4_dev *dev, u64 in_param, u64 out_param, | |||
192 | (cmd->toggle << HCR_T_BIT) | | 323 | (cmd->toggle << HCR_T_BIT) | |
193 | (event ? (1 << HCR_E_BIT) : 0) | | 324 | (event ? (1 << HCR_E_BIT) : 0) | |
194 | (op_modifier << HCR_OPMOD_SHIFT) | | 325 | (op_modifier << HCR_OPMOD_SHIFT) | |
195 | op), hcr + 6); | 326 | op), hcr + 6); |
196 | 327 | ||
197 | /* | 328 | /* |
198 | * Make sure that our HCR writes don't get mixed in with | 329 | * Make sure that our HCR writes don't get mixed in with |
@@ -209,6 +340,62 @@ out: | |||
209 | return ret; | 340 | return ret; |
210 | } | 341 | } |
211 | 342 | ||
343 | static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | ||
344 | int out_is_imm, u32 in_modifier, u8 op_modifier, | ||
345 | u16 op, unsigned long timeout) | ||
346 | { | ||
347 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
348 | struct mlx4_vhcr_cmd *vhcr = priv->mfunc.vhcr; | ||
349 | int ret; | ||
350 | |||
351 | down(&priv->cmd.slave_sem); | ||
352 | vhcr->in_param = cpu_to_be64(in_param); | ||
353 | vhcr->out_param = out_param ? cpu_to_be64(*out_param) : 0; | ||
354 | vhcr->in_modifier = cpu_to_be32(in_modifier); | ||
355 | vhcr->opcode = cpu_to_be16((((u16) op_modifier) << 12) | (op & 0xfff)); | ||
356 | vhcr->token = cpu_to_be16(CMD_POLL_TOKEN); | ||
357 | vhcr->status = 0; | ||
358 | vhcr->flags = !!(priv->cmd.use_events) << 6; | ||
359 | if (mlx4_is_master(dev)) { | ||
360 | ret = mlx4_master_process_vhcr(dev, dev->caps.function, vhcr); | ||
361 | if (!ret) { | ||
362 | if (out_is_imm) { | ||
363 | if (out_param) | ||
364 | *out_param = | ||
365 | be64_to_cpu(vhcr->out_param); | ||
366 | else { | ||
367 | mlx4_err(dev, "response expected while" | ||
368 | "output mailbox is NULL for " | ||
369 | "command 0x%x\n", op); | ||
370 | vhcr->status = -EINVAL; | ||
371 | } | ||
372 | } | ||
373 | ret = vhcr->status; | ||
374 | } | ||
375 | } else { | ||
376 | ret = mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR_POST, 0, | ||
377 | MLX4_COMM_TIME + timeout); | ||
378 | if (!ret) { | ||
379 | if (out_is_imm) { | ||
380 | if (out_param) | ||
381 | *out_param = | ||
382 | be64_to_cpu(vhcr->out_param); | ||
383 | else { | ||
384 | mlx4_err(dev, "response expected while" | ||
385 | "output mailbox is NULL for " | ||
386 | "command 0x%x\n", op); | ||
387 | vhcr->status = -EINVAL; | ||
388 | } | ||
389 | } | ||
390 | ret = vhcr->status; | ||
391 | } else | ||
392 | mlx4_err(dev, "failed execution of VHCR_POST command" | ||
393 | "opcode 0x%x\n", op); | ||
394 | } | ||
395 | up(&priv->cmd.slave_sem); | ||
396 | return ret; | ||
397 | } | ||
398 | |||
212 | static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | 399 | static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, |
213 | int out_is_imm, u32 in_modifier, u8 op_modifier, | 400 | int out_is_imm, u32 in_modifier, u8 op_modifier, |
214 | u16 op, unsigned long timeout) | 401 | u16 op, unsigned long timeout) |
@@ -217,6 +404,7 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | |||
217 | void __iomem *hcr = priv->cmd.hcr; | 404 | void __iomem *hcr = priv->cmd.hcr; |
218 | int err = 0; | 405 | int err = 0; |
219 | unsigned long end; | 406 | unsigned long end; |
407 | u32 stat; | ||
220 | 408 | ||
221 | down(&priv->cmd.poll_sem); | 409 | down(&priv->cmd.poll_sem); |
222 | 410 | ||
@@ -240,9 +428,12 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | |||
240 | __raw_readl(hcr + HCR_OUT_PARAM_OFFSET)) << 32 | | 428 | __raw_readl(hcr + HCR_OUT_PARAM_OFFSET)) << 32 | |
241 | (u64) be32_to_cpu((__force __be32) | 429 | (u64) be32_to_cpu((__force __be32) |
242 | __raw_readl(hcr + HCR_OUT_PARAM_OFFSET + 4)); | 430 | __raw_readl(hcr + HCR_OUT_PARAM_OFFSET + 4)); |
243 | 431 | stat = be32_to_cpu((__force __be32) | |
244 | err = mlx4_status_to_errno(be32_to_cpu((__force __be32) | 432 | __raw_readl(hcr + HCR_STATUS_OFFSET)) >> 24; |
245 | __raw_readl(hcr + HCR_STATUS_OFFSET)) >> 24); | 433 | err = mlx4_status_to_errno(stat); |
434 | if (err) | ||
435 | mlx4_err(dev, "command 0x%x failed: fw status = 0x%x\n", | ||
436 | op, stat); | ||
246 | 437 | ||
247 | out: | 438 | out: |
248 | up(&priv->cmd.poll_sem); | 439 | up(&priv->cmd.poll_sem); |
@@ -259,6 +450,7 @@ void mlx4_cmd_event(struct mlx4_dev *dev, u16 token, u8 status, u64 out_param) | |||
259 | if (token != context->token) | 450 | if (token != context->token) |
260 | return; | 451 | return; |
261 | 452 | ||
453 | context->fw_status = status; | ||
262 | context->result = mlx4_status_to_errno(status); | 454 | context->result = mlx4_status_to_errno(status); |
263 | context->out_param = out_param; | 455 | context->out_param = out_param; |
264 | 456 | ||
@@ -287,14 +479,18 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | |||
287 | mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, | 479 | mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, |
288 | in_modifier, op_modifier, op, context->token, 1); | 480 | in_modifier, op_modifier, op, context->token, 1); |
289 | 481 | ||
290 | if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { | 482 | if (!wait_for_completion_timeout(&context->done, |
483 | msecs_to_jiffies(timeout))) { | ||
291 | err = -EBUSY; | 484 | err = -EBUSY; |
292 | goto out; | 485 | goto out; |
293 | } | 486 | } |
294 | 487 | ||
295 | err = context->result; | 488 | err = context->result; |
296 | if (err) | 489 | if (err) { |
490 | mlx4_err(dev, "command 0x%x failed: fw status = 0x%x\n", | ||
491 | op, context->fw_status); | ||
297 | goto out; | 492 | goto out; |
493 | } | ||
298 | 494 | ||
299 | if (out_is_imm) | 495 | if (out_is_imm) |
300 | *out_param = context->out_param; | 496 | *out_param = context->out_param; |
@@ -313,15 +509,448 @@ int __mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | |||
313 | int out_is_imm, u32 in_modifier, u8 op_modifier, | 509 | int out_is_imm, u32 in_modifier, u8 op_modifier, |
314 | u16 op, unsigned long timeout, int native) | 510 | u16 op, unsigned long timeout, int native) |
315 | { | 511 | { |
316 | if (mlx4_priv(dev)->cmd.use_events) | 512 | if (!mlx4_is_mfunc(dev) || (native && mlx4_is_master(dev))) { |
317 | return mlx4_cmd_wait(dev, in_param, out_param, out_is_imm, | 513 | if (mlx4_priv(dev)->cmd.use_events) |
318 | in_modifier, op_modifier, op, timeout); | 514 | return mlx4_cmd_wait(dev, in_param, out_param, |
319 | else | 515 | out_is_imm, in_modifier, |
320 | return mlx4_cmd_poll(dev, in_param, out_param, out_is_imm, | 516 | op_modifier, op, timeout); |
321 | in_modifier, op_modifier, op, timeout); | 517 | else |
518 | return mlx4_cmd_poll(dev, in_param, out_param, | ||
519 | out_is_imm, in_modifier, | ||
520 | op_modifier, op, timeout); | ||
521 | } | ||
522 | return mlx4_slave_cmd(dev, in_param, out_param, out_is_imm, | ||
523 | in_modifier, op_modifier, op, timeout); | ||
322 | } | 524 | } |
323 | EXPORT_SYMBOL_GPL(__mlx4_cmd); | 525 | EXPORT_SYMBOL_GPL(__mlx4_cmd); |
324 | 526 | ||
527 | |||
528 | static int mlx4_ARM_COMM_CHANNEL(struct mlx4_dev *dev) | ||
529 | { | ||
530 | return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_ARM_COMM_CHANNEL, | ||
531 | MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); | ||
532 | } | ||
533 | |||
534 | static int mlx4_ACCESS_MEM(struct mlx4_dev *dev, u64 master_addr, | ||
535 | int slave, u64 slave_addr, | ||
536 | int size, int is_read) | ||
537 | { | ||
538 | u64 in_param; | ||
539 | u64 out_param; | ||
540 | |||
541 | if ((slave_addr & 0xfff) | (master_addr & 0xfff) | | ||
542 | (slave & ~0x7f) | (size & 0xff)) { | ||
543 | mlx4_err(dev, "Bad access mem params - slave_addr:0x%llx " | ||
544 | "master_addr:0x%llx slave_id:%d size:%d\n", | ||
545 | slave_addr, master_addr, slave, size); | ||
546 | return -EINVAL; | ||
547 | } | ||
548 | |||
549 | if (is_read) { | ||
550 | in_param = (u64) slave | slave_addr; | ||
551 | out_param = (u64) dev->caps.function | master_addr; | ||
552 | } else { | ||
553 | in_param = (u64) dev->caps.function | master_addr; | ||
554 | out_param = (u64) slave | slave_addr; | ||
555 | } | ||
556 | |||
557 | return mlx4_cmd_imm(dev, in_param, &out_param, size, 0, | ||
558 | MLX4_CMD_ACCESS_MEM, | ||
559 | MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); | ||
560 | } | ||
561 | |||
562 | int mlx4_DMA_wrapper(struct mlx4_dev *dev, int slave, | ||
563 | struct mlx4_vhcr *vhcr, | ||
564 | struct mlx4_cmd_mailbox *inbox, | ||
565 | struct mlx4_cmd_mailbox *outbox, | ||
566 | struct mlx4_cmd_info *cmd) | ||
567 | { | ||
568 | u64 in_param; | ||
569 | u64 out_param; | ||
570 | int err; | ||
571 | |||
572 | in_param = cmd->has_inbox ? (u64) inbox->dma : vhcr->in_param; | ||
573 | out_param = cmd->has_outbox ? (u64) outbox->dma : vhcr->out_param; | ||
574 | if (cmd->encode_slave_id) { | ||
575 | in_param &= 0xffffffffffffff00ll; | ||
576 | in_param |= slave; | ||
577 | } | ||
578 | |||
579 | err = __mlx4_cmd(dev, in_param, &out_param, cmd->out_is_imm, | ||
580 | vhcr->in_modifier, vhcr->op_modifier, vhcr->op, | ||
581 | MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); | ||
582 | |||
583 | if (cmd->out_is_imm) | ||
584 | vhcr->out_param = out_param; | ||
585 | |||
586 | return err; | ||
587 | } | ||
588 | |||
589 | static struct mlx4_cmd_info cmd_info[] = { | ||
590 | { | ||
591 | .opcode = MLX4_CMD_QUERY_FW, | ||
592 | .has_inbox = false, | ||
593 | .has_outbox = true, | ||
594 | .out_is_imm = false, | ||
595 | .encode_slave_id = false, | ||
596 | .verify = NULL, | ||
597 | .wrapper = NULL | ||
598 | }, | ||
599 | { | ||
600 | .opcode = MLX4_CMD_QUERY_HCA, | ||
601 | .has_inbox = false, | ||
602 | .has_outbox = true, | ||
603 | .out_is_imm = false, | ||
604 | .encode_slave_id = false, | ||
605 | .verify = NULL, | ||
606 | .wrapper = NULL | ||
607 | }, | ||
608 | { | ||
609 | .opcode = MLX4_CMD_QUERY_DEV_CAP, | ||
610 | .has_inbox = false, | ||
611 | .has_outbox = true, | ||
612 | .out_is_imm = false, | ||
613 | .encode_slave_id = false, | ||
614 | .verify = NULL, | ||
615 | .wrapper = NULL | ||
616 | }, | ||
617 | }; | ||
618 | |||
619 | static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, | ||
620 | struct mlx4_vhcr_cmd *in_vhcr) | ||
621 | { | ||
622 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
623 | struct mlx4_cmd_info *cmd = NULL; | ||
624 | struct mlx4_vhcr_cmd *vhcr_cmd = in_vhcr ? in_vhcr : priv->mfunc.vhcr; | ||
625 | struct mlx4_vhcr *vhcr; | ||
626 | struct mlx4_cmd_mailbox *inbox = NULL; | ||
627 | struct mlx4_cmd_mailbox *outbox = NULL; | ||
628 | u64 in_param; | ||
629 | u64 out_param; | ||
630 | int ret = 0; | ||
631 | int i; | ||
632 | |||
633 | /* Create sw representation of Virtual HCR */ | ||
634 | vhcr = kzalloc(sizeof(struct mlx4_vhcr), GFP_KERNEL); | ||
635 | if (!vhcr) | ||
636 | return -ENOMEM; | ||
637 | |||
638 | /* DMA in the vHCR */ | ||
639 | if (!in_vhcr) { | ||
640 | ret = mlx4_ACCESS_MEM(dev, priv->mfunc.vhcr_dma, slave, | ||
641 | priv->mfunc.master.slave_state[slave].vhcr_dma, | ||
642 | ALIGN(sizeof(struct mlx4_vhcr_cmd), | ||
643 | MLX4_ACCESS_MEM_ALIGN), 1); | ||
644 | if (ret) { | ||
645 | mlx4_err(dev, "%s:Failed reading vhcr" | ||
646 | "ret: 0x%x\n", __func__, ret); | ||
647 | kfree(vhcr); | ||
648 | return ret; | ||
649 | } | ||
650 | } | ||
651 | |||
652 | /* Fill SW VHCR fields */ | ||
653 | vhcr->in_param = be64_to_cpu(vhcr_cmd->in_param); | ||
654 | vhcr->out_param = be64_to_cpu(vhcr_cmd->out_param); | ||
655 | vhcr->in_modifier = be32_to_cpu(vhcr_cmd->in_modifier); | ||
656 | vhcr->token = be16_to_cpu(vhcr_cmd->token); | ||
657 | vhcr->op = be16_to_cpu(vhcr_cmd->opcode) & 0xfff; | ||
658 | vhcr->op_modifier = (u8) (be16_to_cpu(vhcr_cmd->opcode) >> 12); | ||
659 | vhcr->e_bit = vhcr_cmd->flags & (1 << 6); | ||
660 | |||
661 | /* Lookup command */ | ||
662 | for (i = 0; i < ARRAY_SIZE(cmd_info); ++i) { | ||
663 | if (vhcr->op == cmd_info[i].opcode) { | ||
664 | cmd = &cmd_info[i]; | ||
665 | break; | ||
666 | } | ||
667 | } | ||
668 | if (!cmd) { | ||
669 | mlx4_err(dev, "Unknown command:0x%x accepted from slave:%d\n", | ||
670 | vhcr->op, slave); | ||
671 | vhcr_cmd->status = -EINVAL; | ||
672 | goto out_status; | ||
673 | } | ||
674 | |||
675 | /* Read inbox */ | ||
676 | if (cmd->has_inbox) { | ||
677 | vhcr->in_param &= INBOX_MASK; | ||
678 | inbox = mlx4_alloc_cmd_mailbox(dev); | ||
679 | if (IS_ERR(inbox)) { | ||
680 | ret = PTR_ERR(inbox); | ||
681 | inbox = NULL; | ||
682 | goto out; | ||
683 | } | ||
684 | |||
685 | ret = mlx4_ACCESS_MEM(dev, inbox->dma, slave, | ||
686 | vhcr->in_param, | ||
687 | MLX4_MAILBOX_SIZE, 1); | ||
688 | if (ret) { | ||
689 | mlx4_err(dev, "%s: Failed reading inbox (cmd:0x%x)\n", | ||
690 | __func__, cmd->opcode); | ||
691 | goto out; | ||
692 | } | ||
693 | } | ||
694 | |||
695 | /* Apply permission and bound checks if applicable */ | ||
696 | if (cmd->verify && cmd->verify(dev, slave, vhcr, inbox)) { | ||
697 | mlx4_warn(dev, "Command:0x%x from slave: %d failed protection " | ||
698 | "checks for resource_id:%d\n", vhcr->op, slave, | ||
699 | vhcr->in_modifier); | ||
700 | vhcr_cmd->status = -EPERM; | ||
701 | goto out_status; | ||
702 | } | ||
703 | |||
704 | /* Allocate outbox */ | ||
705 | if (cmd->has_outbox) { | ||
706 | outbox = mlx4_alloc_cmd_mailbox(dev); | ||
707 | if (IS_ERR(outbox)) { | ||
708 | ret = PTR_ERR(outbox); | ||
709 | outbox = NULL; | ||
710 | goto out; | ||
711 | } | ||
712 | } | ||
713 | |||
714 | /* Execute the command! */ | ||
715 | if (cmd->wrapper) { | ||
716 | vhcr_cmd->status = cmd->wrapper(dev, slave, vhcr, inbox, outbox, | ||
717 | cmd); | ||
718 | if (cmd->out_is_imm) | ||
719 | vhcr_cmd->out_param = cpu_to_be64(vhcr->out_param); | ||
720 | } else { | ||
721 | in_param = cmd->has_inbox ? (u64) inbox->dma : | ||
722 | vhcr->in_param; | ||
723 | out_param = cmd->has_outbox ? (u64) outbox->dma : | ||
724 | vhcr->out_param; | ||
725 | vhcr_cmd->status = __mlx4_cmd(dev, in_param, &out_param, | ||
726 | cmd->out_is_imm, vhcr->in_modifier, | ||
727 | vhcr->op_modifier, vhcr->op, | ||
728 | MLX4_CMD_TIME_CLASS_A, | ||
729 | MLX4_CMD_NATIVE); | ||
730 | |||
731 | if (vhcr_cmd->status) { | ||
732 | mlx4_warn(dev, "vhcr command:0x%x slave:%d failed with" | ||
733 | " error:%d, status %d\n", | ||
734 | vhcr->op, slave, vhcr->errno, | ||
735 | vhcr_cmd->status); | ||
736 | ret = vhcr_cmd->status; | ||
737 | goto out; | ||
738 | } | ||
739 | |||
740 | if (cmd->out_is_imm) { | ||
741 | vhcr->out_param = out_param; | ||
742 | vhcr_cmd->out_param = cpu_to_be64(vhcr->out_param); | ||
743 | } | ||
744 | } | ||
745 | |||
746 | /* Write outbox if command completed successfully */ | ||
747 | if (cmd->has_outbox && !vhcr->errno) { | ||
748 | ret = mlx4_ACCESS_MEM(dev, outbox->dma, slave, | ||
749 | vhcr->out_param, | ||
750 | MLX4_MAILBOX_SIZE, MLX4_CMD_WRAPPED); | ||
751 | if (ret) { | ||
752 | mlx4_err(dev, "%s:Failed writing outbox\n", __func__); | ||
753 | goto out; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | out_status: | ||
758 | /* DMA back vhcr result */ | ||
759 | if (!in_vhcr) { | ||
760 | ret = mlx4_ACCESS_MEM(dev, priv->mfunc.vhcr_dma, slave, | ||
761 | priv->mfunc.master.slave_state[slave].vhcr_dma, | ||
762 | ALIGN(sizeof(struct mlx4_vhcr), | ||
763 | MLX4_ACCESS_MEM_ALIGN), | ||
764 | MLX4_CMD_WRAPPED); | ||
765 | if (ret) | ||
766 | mlx4_err(dev, "%s:Failed writing vhcr result\n", | ||
767 | __func__); | ||
768 | else if (vhcr->e_bit && | ||
769 | mlx4_GEN_EQE(dev, slave, &priv->mfunc.master.cmd_eqe)) | ||
770 | mlx4_warn(dev, "Failed to generate command completion " | ||
771 | "eqe for slave %d\n", slave); | ||
772 | } | ||
773 | |||
774 | out: | ||
775 | kfree(vhcr); | ||
776 | mlx4_free_cmd_mailbox(dev, inbox); | ||
777 | mlx4_free_cmd_mailbox(dev, outbox); | ||
778 | return ret; | ||
779 | } | ||
780 | |||
781 | static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, | ||
782 | u16 param, u8 toggle) | ||
783 | { | ||
784 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
785 | struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state; | ||
786 | u32 reply; | ||
787 | u32 slave_status = 0; | ||
788 | u8 is_going_down = 0; | ||
789 | |||
790 | slave_state[slave].comm_toggle ^= 1; | ||
791 | reply = (u32) slave_state[slave].comm_toggle << 31; | ||
792 | if (toggle != slave_state[slave].comm_toggle) { | ||
793 | mlx4_warn(dev, "Incorrect toggle %d from slave %d. *** MASTER" | ||
794 | "STATE COMPROMISIED ***\n", toggle, slave); | ||
795 | goto reset_slave; | ||
796 | } | ||
797 | if (cmd == MLX4_COMM_CMD_RESET) { | ||
798 | mlx4_warn(dev, "Received reset from slave:%d\n", slave); | ||
799 | slave_state[slave].active = false; | ||
800 | /*check if we are in the middle of FLR process, | ||
801 | if so return "retry" status to the slave*/ | ||
802 | if (MLX4_COMM_CMD_FLR == slave_state[slave].last_cmd) { | ||
803 | slave_status = MLX4_DELAY_RESET_SLAVE; | ||
804 | goto inform_slave_state; | ||
805 | } | ||
806 | |||
807 | /* write the version in the event field */ | ||
808 | reply |= mlx4_comm_get_version(); | ||
809 | |||
810 | goto reset_slave; | ||
811 | } | ||
812 | /*command from slave in the middle of FLR*/ | ||
813 | if (cmd != MLX4_COMM_CMD_RESET && | ||
814 | MLX4_COMM_CMD_FLR == slave_state[slave].last_cmd) { | ||
815 | mlx4_warn(dev, "slave:%d is Trying to run cmd(0x%x) " | ||
816 | "in the middle of FLR\n", slave, cmd); | ||
817 | return; | ||
818 | } | ||
819 | |||
820 | switch (cmd) { | ||
821 | case MLX4_COMM_CMD_VHCR0: | ||
822 | if (slave_state[slave].last_cmd != MLX4_COMM_CMD_RESET) | ||
823 | goto reset_slave; | ||
824 | slave_state[slave].vhcr_dma = ((u64) param) << 48; | ||
825 | priv->mfunc.master.slave_state[slave].cookie = 0; | ||
826 | mutex_init(&priv->mfunc.master.gen_eqe_mutex[slave]); | ||
827 | break; | ||
828 | case MLX4_COMM_CMD_VHCR1: | ||
829 | if (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR0) | ||
830 | goto reset_slave; | ||
831 | slave_state[slave].vhcr_dma |= ((u64) param) << 32; | ||
832 | break; | ||
833 | case MLX4_COMM_CMD_VHCR2: | ||
834 | if (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR1) | ||
835 | goto reset_slave; | ||
836 | slave_state[slave].vhcr_dma |= ((u64) param) << 16; | ||
837 | break; | ||
838 | case MLX4_COMM_CMD_VHCR_EN: | ||
839 | if (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR2) | ||
840 | goto reset_slave; | ||
841 | slave_state[slave].vhcr_dma |= param; | ||
842 | slave_state[slave].active = true; | ||
843 | break; | ||
844 | case MLX4_COMM_CMD_VHCR_POST: | ||
845 | if ((slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR_EN) && | ||
846 | (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR_POST)) | ||
847 | goto reset_slave; | ||
848 | down(&priv->cmd.slave_sem); | ||
849 | if (mlx4_master_process_vhcr(dev, slave, NULL)) { | ||
850 | mlx4_err(dev, "Failed processing vhcr for slave:%d," | ||
851 | " reseting slave.\n", slave); | ||
852 | up(&priv->cmd.slave_sem); | ||
853 | goto reset_slave; | ||
854 | } | ||
855 | up(&priv->cmd.slave_sem); | ||
856 | break; | ||
857 | default: | ||
858 | mlx4_warn(dev, "Bad comm cmd:%d from slave:%d\n", cmd, slave); | ||
859 | goto reset_slave; | ||
860 | } | ||
861 | spin_lock(&priv->mfunc.master.slave_state_lock); | ||
862 | if (!slave_state[slave].is_slave_going_down) | ||
863 | slave_state[slave].last_cmd = cmd; | ||
864 | else | ||
865 | is_going_down = 1; | ||
866 | spin_unlock(&priv->mfunc.master.slave_state_lock); | ||
867 | if (is_going_down) { | ||
868 | mlx4_warn(dev, "Slave is going down aborting command(%d)" | ||
869 | " executing from slave:%d\n", | ||
870 | cmd, slave); | ||
871 | return; | ||
872 | } | ||
873 | __raw_writel((__force u32) cpu_to_be32(reply), | ||
874 | &priv->mfunc.comm[slave].slave_read); | ||
875 | mmiowb(); | ||
876 | |||
877 | return; | ||
878 | |||
879 | reset_slave: | ||
880 | spin_lock(&priv->mfunc.master.slave_state_lock); | ||
881 | if (!slave_state[slave].is_slave_going_down) | ||
882 | slave_state[slave].last_cmd = MLX4_COMM_CMD_RESET; | ||
883 | spin_unlock(&priv->mfunc.master.slave_state_lock); | ||
884 | /*with slave in the middle of flr, no need to clean resources again.*/ | ||
885 | inform_slave_state: | ||
886 | memset(&slave_state[slave].event_eq, 0, | ||
887 | sizeof(struct mlx4_slave_event_eq_info)); | ||
888 | __raw_writel((__force u32) cpu_to_be32(reply), | ||
889 | &priv->mfunc.comm[slave].slave_read); | ||
890 | wmb(); | ||
891 | } | ||
892 | |||
893 | /* master command processing */ | ||
894 | void mlx4_master_comm_channel(struct work_struct *work) | ||
895 | { | ||
896 | struct mlx4_mfunc_master_ctx *master = | ||
897 | container_of(work, | ||
898 | struct mlx4_mfunc_master_ctx, | ||
899 | comm_work); | ||
900 | struct mlx4_mfunc *mfunc = | ||
901 | container_of(master, struct mlx4_mfunc, master); | ||
902 | struct mlx4_priv *priv = | ||
903 | container_of(mfunc, struct mlx4_priv, mfunc); | ||
904 | struct mlx4_dev *dev = &priv->dev; | ||
905 | __be32 *bit_vec; | ||
906 | u32 comm_cmd; | ||
907 | u32 vec; | ||
908 | int i, j, slave; | ||
909 | int toggle; | ||
910 | int served = 0; | ||
911 | int reported = 0; | ||
912 | u32 slt; | ||
913 | |||
914 | bit_vec = master->comm_arm_bit_vector; | ||
915 | for (i = 0; i < COMM_CHANNEL_BIT_ARRAY_SIZE; i++) { | ||
916 | vec = be32_to_cpu(bit_vec[i]); | ||
917 | for (j = 0; j < 32; j++) { | ||
918 | if (!(vec & (1 << j))) | ||
919 | continue; | ||
920 | ++reported; | ||
921 | slave = (i * 32) + j; | ||
922 | comm_cmd = swab32(readl( | ||
923 | &mfunc->comm[slave].slave_write)); | ||
924 | slt = swab32(readl(&mfunc->comm[slave].slave_read)) | ||
925 | >> 31; | ||
926 | toggle = comm_cmd >> 31; | ||
927 | if (toggle != slt) { | ||
928 | if (master->slave_state[slave].comm_toggle | ||
929 | != slt) { | ||
930 | printk(KERN_INFO "slave %d out of sync." | ||
931 | " read toggle %d, state toggle %d. " | ||
932 | "Resynching.\n", slave, slt, | ||
933 | master->slave_state[slave].comm_toggle); | ||
934 | master->slave_state[slave].comm_toggle = | ||
935 | slt; | ||
936 | } | ||
937 | mlx4_master_do_cmd(dev, slave, | ||
938 | comm_cmd >> 16 & 0xff, | ||
939 | comm_cmd & 0xffff, toggle); | ||
940 | ++served; | ||
941 | } | ||
942 | } | ||
943 | } | ||
944 | |||
945 | if (reported && reported != served) | ||
946 | mlx4_warn(dev, "Got command event with bitmask from %d slaves" | ||
947 | " but %d were served\n", | ||
948 | reported, served); | ||
949 | |||
950 | if (mlx4_ARM_COMM_CHANNEL(dev)) | ||
951 | mlx4_warn(dev, "Failed to arm comm channel events\n"); | ||
952 | } | ||
953 | |||
325 | int mlx4_cmd_init(struct mlx4_dev *dev) | 954 | int mlx4_cmd_init(struct mlx4_dev *dev) |
326 | { | 955 | { |
327 | struct mlx4_priv *priv = mlx4_priv(dev); | 956 | struct mlx4_priv *priv = mlx4_priv(dev); |
@@ -331,22 +960,30 @@ int mlx4_cmd_init(struct mlx4_dev *dev) | |||
331 | priv->cmd.use_events = 0; | 960 | priv->cmd.use_events = 0; |
332 | priv->cmd.toggle = 1; | 961 | priv->cmd.toggle = 1; |
333 | 962 | ||
334 | priv->cmd.hcr = ioremap(pci_resource_start(dev->pdev, 0) + MLX4_HCR_BASE, | 963 | priv->cmd.hcr = NULL; |
335 | MLX4_HCR_SIZE); | 964 | priv->mfunc.vhcr = NULL; |
336 | if (!priv->cmd.hcr) { | 965 | |
337 | mlx4_err(dev, "Couldn't map command register."); | 966 | if (!mlx4_is_slave(dev)) { |
338 | return -ENOMEM; | 967 | priv->cmd.hcr = ioremap(pci_resource_start(dev->pdev, 0) + |
968 | MLX4_HCR_BASE, MLX4_HCR_SIZE); | ||
969 | if (!priv->cmd.hcr) { | ||
970 | mlx4_err(dev, "Couldn't map command register.\n"); | ||
971 | return -ENOMEM; | ||
972 | } | ||
339 | } | 973 | } |
340 | 974 | ||
341 | priv->cmd.pool = pci_pool_create("mlx4_cmd", dev->pdev, | 975 | priv->cmd.pool = pci_pool_create("mlx4_cmd", dev->pdev, |
342 | MLX4_MAILBOX_SIZE, | 976 | MLX4_MAILBOX_SIZE, |
343 | MLX4_MAILBOX_SIZE, 0); | 977 | MLX4_MAILBOX_SIZE, 0); |
344 | if (!priv->cmd.pool) { | 978 | if (!priv->cmd.pool) |
345 | iounmap(priv->cmd.hcr); | 979 | goto err_hcr; |
346 | return -ENOMEM; | ||
347 | } | ||
348 | 980 | ||
349 | return 0; | 981 | return 0; |
982 | |||
983 | err_hcr: | ||
984 | if (!mlx4_is_slave(dev)) | ||
985 | iounmap(priv->cmd.hcr); | ||
986 | return -ENOMEM; | ||
350 | } | 987 | } |
351 | 988 | ||
352 | void mlx4_cmd_cleanup(struct mlx4_dev *dev) | 989 | void mlx4_cmd_cleanup(struct mlx4_dev *dev) |
@@ -354,7 +991,9 @@ void mlx4_cmd_cleanup(struct mlx4_dev *dev) | |||
354 | struct mlx4_priv *priv = mlx4_priv(dev); | 991 | struct mlx4_priv *priv = mlx4_priv(dev); |
355 | 992 | ||
356 | pci_pool_destroy(priv->cmd.pool); | 993 | pci_pool_destroy(priv->cmd.pool); |
357 | iounmap(priv->cmd.hcr); | 994 | |
995 | if (!mlx4_is_slave(dev)) | ||
996 | iounmap(priv->cmd.hcr); | ||
358 | } | 997 | } |
359 | 998 | ||
360 | /* | 999 | /* |
@@ -365,6 +1004,7 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) | |||
365 | { | 1004 | { |
366 | struct mlx4_priv *priv = mlx4_priv(dev); | 1005 | struct mlx4_priv *priv = mlx4_priv(dev); |
367 | int i; | 1006 | int i; |
1007 | int err = 0; | ||
368 | 1008 | ||
369 | priv->cmd.context = kmalloc(priv->cmd.max_cmds * | 1009 | priv->cmd.context = kmalloc(priv->cmd.max_cmds * |
370 | sizeof (struct mlx4_cmd_context), | 1010 | sizeof (struct mlx4_cmd_context), |
@@ -389,11 +1029,10 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) | |||
389 | ; /* nothing */ | 1029 | ; /* nothing */ |
390 | --priv->cmd.token_mask; | 1030 | --priv->cmd.token_mask; |
391 | 1031 | ||
392 | priv->cmd.use_events = 1; | ||
393 | |||
394 | down(&priv->cmd.poll_sem); | 1032 | down(&priv->cmd.poll_sem); |
1033 | priv->cmd.use_events = 1; | ||
395 | 1034 | ||
396 | return 0; | 1035 | return err; |
397 | } | 1036 | } |
398 | 1037 | ||
399 | /* | 1038 | /* |
@@ -433,7 +1072,8 @@ struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) | |||
433 | } | 1072 | } |
434 | EXPORT_SYMBOL_GPL(mlx4_alloc_cmd_mailbox); | 1073 | EXPORT_SYMBOL_GPL(mlx4_alloc_cmd_mailbox); |
435 | 1074 | ||
436 | void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox) | 1075 | void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, |
1076 | struct mlx4_cmd_mailbox *mailbox) | ||
437 | { | 1077 | { |
438 | if (!mailbox) | 1078 | if (!mailbox) |
439 | return; | 1079 | return; |
@@ -442,3 +1082,8 @@ void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbo | |||
442 | kfree(mailbox); | 1082 | kfree(mailbox); |
443 | } | 1083 | } |
444 | EXPORT_SYMBOL_GPL(mlx4_free_cmd_mailbox); | 1084 | EXPORT_SYMBOL_GPL(mlx4_free_cmd_mailbox); |
1085 | |||
1086 | u32 mlx4_comm_get_version(void) | ||
1087 | { | ||
1088 | return ((u32) CMD_CHAN_IF_REV << 8) | (u32) CMD_CHAN_VER; | ||
1089 | } | ||