diff options
author | Ron Mercer <ron.mercer@qlogic.com> | 2009-03-02 03:07:32 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-03-03 01:45:24 -0500 |
commit | bcc2cb3b97e37317c301309d7052bb61e6cce2c4 (patch) | |
tree | e1eed42ab44980713cc6b8d38e917e0d27ff9bcd /drivers | |
parent | cdca8d02ea4229c2ccf3c27fb537b150843f67c9 (diff) |
qlge: Add support for getting/setting port config.
This patch adds functionality to get and set port parameters.
Currently it is used to set maximum TX/RX frame sizes. This process is
also capable of setting:
1) Pause type: Standard or Priority based.
2) Loop back mode.
3) Enable Jumbo frame mode (included here...)
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/qlge/qlge.h | 6 | ||||
-rw-r--r-- | drivers/net/qlge/qlge_main.c | 14 | ||||
-rw-r--r-- | drivers/net/qlge/qlge_mpi.c | 209 |
3 files changed, 228 insertions, 1 deletions
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index 5f60ec4efb3c..6f9fd24bf384 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h | |||
@@ -1450,6 +1450,7 @@ struct ql_adapter { | |||
1450 | 1450 | ||
1451 | u32 mailbox_in; | 1451 | u32 mailbox_in; |
1452 | u32 mailbox_out; | 1452 | u32 mailbox_out; |
1453 | struct mbox_params idc_mbc; | ||
1453 | struct mutex mpi_mutex; | 1454 | struct mutex mpi_mutex; |
1454 | 1455 | ||
1455 | int tx_ring_size; | 1456 | int tx_ring_size; |
@@ -1486,6 +1487,8 @@ struct ql_adapter { | |||
1486 | u32 port_link_up; | 1487 | u32 port_link_up; |
1487 | u32 port_init; | 1488 | u32 port_init; |
1488 | u32 link_status; | 1489 | u32 link_status; |
1490 | u32 link_config; | ||
1491 | u32 max_frame_size; | ||
1489 | 1492 | ||
1490 | union flash_params flash; | 1493 | union flash_params flash; |
1491 | 1494 | ||
@@ -1495,6 +1498,8 @@ struct ql_adapter { | |||
1495 | struct delayed_work asic_reset_work; | 1498 | struct delayed_work asic_reset_work; |
1496 | struct delayed_work mpi_reset_work; | 1499 | struct delayed_work mpi_reset_work; |
1497 | struct delayed_work mpi_work; | 1500 | struct delayed_work mpi_work; |
1501 | struct delayed_work mpi_port_cfg_work; | ||
1502 | struct completion ide_completion; | ||
1498 | struct nic_operations *nic_ops; | 1503 | struct nic_operations *nic_ops; |
1499 | u16 device_id; | 1504 | u16 device_id; |
1500 | }; | 1505 | }; |
@@ -1569,6 +1574,7 @@ void ql_queue_asic_error(struct ql_adapter *qdev); | |||
1569 | u32 ql_enable_completion_interrupt(struct ql_adapter *qdev, u32 intr); | 1574 | u32 ql_enable_completion_interrupt(struct ql_adapter *qdev, u32 intr); |
1570 | void ql_set_ethtool_ops(struct net_device *ndev); | 1575 | void ql_set_ethtool_ops(struct net_device *ndev); |
1571 | int ql_read_xgmac_reg64(struct ql_adapter *qdev, u32 reg, u64 *data); | 1576 | int ql_read_xgmac_reg64(struct ql_adapter *qdev, u32 reg, u64 *data); |
1577 | void ql_mpi_port_cfg_work(struct work_struct *work); | ||
1572 | int ql_mb_get_fw_state(struct ql_adapter *qdev); | 1578 | int ql_mb_get_fw_state(struct ql_adapter *qdev); |
1573 | 1579 | ||
1574 | #if 1 | 1580 | #if 1 |
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 29334d99b430..7c1ce5765759 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c | |||
@@ -837,7 +837,14 @@ exit: | |||
837 | 837 | ||
838 | static int ql_8000_port_initialize(struct ql_adapter *qdev) | 838 | static int ql_8000_port_initialize(struct ql_adapter *qdev) |
839 | { | 839 | { |
840 | return ql_mb_get_fw_state(qdev); | 840 | int status; |
841 | status = ql_mb_get_fw_state(qdev); | ||
842 | if (status) | ||
843 | goto exit; | ||
844 | /* Wake up a worker to get/set the TX/RX frame sizes. */ | ||
845 | queue_delayed_work(qdev->workqueue, &qdev->mpi_port_cfg_work, 0); | ||
846 | exit: | ||
847 | return status; | ||
841 | } | 848 | } |
842 | 849 | ||
843 | /* Take the MAC Core out of reset. | 850 | /* Take the MAC Core out of reset. |
@@ -3188,6 +3195,7 @@ static int ql_adapter_down(struct ql_adapter *qdev) | |||
3188 | cancel_delayed_work_sync(&qdev->asic_reset_work); | 3195 | cancel_delayed_work_sync(&qdev->asic_reset_work); |
3189 | cancel_delayed_work_sync(&qdev->mpi_reset_work); | 3196 | cancel_delayed_work_sync(&qdev->mpi_reset_work); |
3190 | cancel_delayed_work_sync(&qdev->mpi_work); | 3197 | cancel_delayed_work_sync(&qdev->mpi_work); |
3198 | cancel_delayed_work_sync(&qdev->mpi_port_cfg_work); | ||
3191 | 3199 | ||
3192 | /* The default queue at index 0 is always processed in | 3200 | /* The default queue at index 0 is always processed in |
3193 | * a workqueue. | 3201 | * a workqueue. |
@@ -3462,6 +3470,8 @@ static int qlge_change_mtu(struct net_device *ndev, int new_mtu) | |||
3462 | 3470 | ||
3463 | if (ndev->mtu == 1500 && new_mtu == 9000) { | 3471 | if (ndev->mtu == 1500 && new_mtu == 9000) { |
3464 | QPRINTK(qdev, IFUP, ERR, "Changing to jumbo MTU.\n"); | 3472 | QPRINTK(qdev, IFUP, ERR, "Changing to jumbo MTU.\n"); |
3473 | queue_delayed_work(qdev->workqueue, | ||
3474 | &qdev->mpi_port_cfg_work, 0); | ||
3465 | } else if (ndev->mtu == 9000 && new_mtu == 1500) { | 3475 | } else if (ndev->mtu == 9000 && new_mtu == 1500) { |
3466 | QPRINTK(qdev, IFUP, ERR, "Changing to normal MTU.\n"); | 3476 | QPRINTK(qdev, IFUP, ERR, "Changing to normal MTU.\n"); |
3467 | } else if ((ndev->mtu == 1500 && new_mtu == 1500) || | 3477 | } else if ((ndev->mtu == 1500 && new_mtu == 1500) || |
@@ -3771,7 +3781,9 @@ static int __devinit ql_init_device(struct pci_dev *pdev, | |||
3771 | INIT_DELAYED_WORK(&qdev->asic_reset_work, ql_asic_reset_work); | 3781 | INIT_DELAYED_WORK(&qdev->asic_reset_work, ql_asic_reset_work); |
3772 | INIT_DELAYED_WORK(&qdev->mpi_reset_work, ql_mpi_reset_work); | 3782 | INIT_DELAYED_WORK(&qdev->mpi_reset_work, ql_mpi_reset_work); |
3773 | INIT_DELAYED_WORK(&qdev->mpi_work, ql_mpi_work); | 3783 | INIT_DELAYED_WORK(&qdev->mpi_work, ql_mpi_work); |
3784 | INIT_DELAYED_WORK(&qdev->mpi_port_cfg_work, ql_mpi_port_cfg_work); | ||
3774 | mutex_init(&qdev->mpi_mutex); | 3785 | mutex_init(&qdev->mpi_mutex); |
3786 | init_completion(&qdev->ide_completion); | ||
3775 | 3787 | ||
3776 | if (!cards_found) { | 3788 | if (!cards_found) { |
3777 | dev_info(&pdev->dev, "%s\n", DRV_STRING); | 3789 | dev_info(&pdev->dev, "%s\n", DRV_STRING); |
diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c index 11102bec36b2..ef610c674dfb 100644 --- a/drivers/net/qlge/qlge_mpi.c +++ b/drivers/net/qlge/qlge_mpi.c | |||
@@ -138,6 +138,27 @@ end: | |||
138 | return status; | 138 | return status; |
139 | } | 139 | } |
140 | 140 | ||
141 | /* Process an inter-device event completion. | ||
142 | * If good, signal the caller's completion. | ||
143 | */ | ||
144 | static int ql_idc_cmplt_aen(struct ql_adapter *qdev) | ||
145 | { | ||
146 | int status; | ||
147 | struct mbox_params *mbcp = &qdev->idc_mbc; | ||
148 | mbcp->out_count = 4; | ||
149 | status = ql_get_mb_sts(qdev, mbcp); | ||
150 | if (status) { | ||
151 | QPRINTK(qdev, DRV, ERR, | ||
152 | "Could not read MPI, resetting RISC!\n"); | ||
153 | ql_queue_fw_error(qdev); | ||
154 | } else | ||
155 | /* Wake up the sleeping mpi_idc_work thread that is | ||
156 | * waiting for this event. | ||
157 | */ | ||
158 | complete(&qdev->ide_completion); | ||
159 | |||
160 | return status; | ||
161 | } | ||
141 | static void ql_link_up(struct ql_adapter *qdev, struct mbox_params *mbcp) | 162 | static void ql_link_up(struct ql_adapter *qdev, struct mbox_params *mbcp) |
142 | { | 163 | { |
143 | mbcp->out_count = 2; | 164 | mbcp->out_count = 2; |
@@ -241,6 +262,16 @@ static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp) | |||
241 | status = ql_get_mb_sts(qdev, mbcp); | 262 | status = ql_get_mb_sts(qdev, mbcp); |
242 | return status; | 263 | return status; |
243 | 264 | ||
265 | /* Process and inbound IDC event. | ||
266 | * This will happen when we're trying to | ||
267 | * change tx/rx max frame size, change pause | ||
268 | * paramters or loopback mode. | ||
269 | */ | ||
270 | case AEN_IDC_CMPLT: | ||
271 | case AEN_IDC_EXT: | ||
272 | status = ql_idc_cmplt_aen(qdev); | ||
273 | break; | ||
274 | |||
244 | case AEN_LINK_UP: | 275 | case AEN_LINK_UP: |
245 | ql_link_up(qdev, mbcp); | 276 | ql_link_up(qdev, mbcp); |
246 | break; | 277 | break; |
@@ -391,6 +422,182 @@ int ql_mb_get_fw_state(struct ql_adapter *qdev) | |||
391 | return status; | 422 | return status; |
392 | } | 423 | } |
393 | 424 | ||
425 | /* Get link settings and maximum frame size settings | ||
426 | * for the current port. | ||
427 | * Most likely will block. | ||
428 | */ | ||
429 | static int ql_mb_set_port_cfg(struct ql_adapter *qdev) | ||
430 | { | ||
431 | struct mbox_params mbc; | ||
432 | struct mbox_params *mbcp = &mbc; | ||
433 | int status = 0; | ||
434 | |||
435 | memset(mbcp, 0, sizeof(struct mbox_params)); | ||
436 | |||
437 | mbcp->in_count = 3; | ||
438 | mbcp->out_count = 1; | ||
439 | |||
440 | mbcp->mbox_in[0] = MB_CMD_SET_PORT_CFG; | ||
441 | mbcp->mbox_in[1] = qdev->link_config; | ||
442 | mbcp->mbox_in[2] = qdev->max_frame_size; | ||
443 | |||
444 | |||
445 | status = ql_mailbox_command(qdev, mbcp); | ||
446 | if (status) | ||
447 | return status; | ||
448 | |||
449 | if (mbcp->mbox_out[0] == MB_CMD_STS_INTRMDT) { | ||
450 | QPRINTK(qdev, DRV, ERR, | ||
451 | "Port Config sent, wait for IDC.\n"); | ||
452 | } else if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { | ||
453 | QPRINTK(qdev, DRV, ERR, | ||
454 | "Failed Set Port Configuration.\n"); | ||
455 | status = -EIO; | ||
456 | } | ||
457 | return status; | ||
458 | } | ||
459 | |||
460 | /* Get link settings and maximum frame size settings | ||
461 | * for the current port. | ||
462 | * Most likely will block. | ||
463 | */ | ||
464 | static int ql_mb_get_port_cfg(struct ql_adapter *qdev) | ||
465 | { | ||
466 | struct mbox_params mbc; | ||
467 | struct mbox_params *mbcp = &mbc; | ||
468 | int status = 0; | ||
469 | |||
470 | memset(mbcp, 0, sizeof(struct mbox_params)); | ||
471 | |||
472 | mbcp->in_count = 1; | ||
473 | mbcp->out_count = 3; | ||
474 | |||
475 | mbcp->mbox_in[0] = MB_CMD_GET_PORT_CFG; | ||
476 | |||
477 | status = ql_mailbox_command(qdev, mbcp); | ||
478 | if (status) | ||
479 | return status; | ||
480 | |||
481 | if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { | ||
482 | QPRINTK(qdev, DRV, ERR, | ||
483 | "Failed Get Port Configuration.\n"); | ||
484 | status = -EIO; | ||
485 | } else { | ||
486 | QPRINTK(qdev, DRV, DEBUG, | ||
487 | "Passed Get Port Configuration.\n"); | ||
488 | qdev->link_config = mbcp->mbox_out[1]; | ||
489 | qdev->max_frame_size = mbcp->mbox_out[2]; | ||
490 | } | ||
491 | return status; | ||
492 | } | ||
493 | |||
494 | /* IDC - Inter Device Communication... | ||
495 | * Some firmware commands require consent of adjacent FCOE | ||
496 | * function. This function waits for the OK, or a | ||
497 | * counter-request for a little more time.i | ||
498 | * The firmware will complete the request if the other | ||
499 | * function doesn't respond. | ||
500 | */ | ||
501 | static int ql_idc_wait(struct ql_adapter *qdev) | ||
502 | { | ||
503 | int status = -ETIMEDOUT; | ||
504 | long wait_time = 1 * HZ; | ||
505 | struct mbox_params *mbcp = &qdev->idc_mbc; | ||
506 | do { | ||
507 | /* Wait here for the command to complete | ||
508 | * via the IDC process. | ||
509 | */ | ||
510 | wait_time = | ||
511 | wait_for_completion_timeout(&qdev->ide_completion, | ||
512 | wait_time); | ||
513 | if (!wait_time) { | ||
514 | QPRINTK(qdev, DRV, ERR, | ||
515 | "IDC Timeout.\n"); | ||
516 | break; | ||
517 | } | ||
518 | /* Now examine the response from the IDC process. | ||
519 | * We might have a good completion or a request for | ||
520 | * more wait time. | ||
521 | */ | ||
522 | if (mbcp->mbox_out[0] == AEN_IDC_EXT) { | ||
523 | QPRINTK(qdev, DRV, ERR, | ||
524 | "IDC Time Extension from function.\n"); | ||
525 | wait_time += (mbcp->mbox_out[1] >> 8) & 0x0000000f; | ||
526 | } else if (mbcp->mbox_out[0] == AEN_IDC_CMPLT) { | ||
527 | QPRINTK(qdev, DRV, ERR, | ||
528 | "IDC Success.\n"); | ||
529 | status = 0; | ||
530 | break; | ||
531 | } else { | ||
532 | QPRINTK(qdev, DRV, ERR, | ||
533 | "IDC: Invalid State 0x%.04x.\n", | ||
534 | mbcp->mbox_out[0]); | ||
535 | status = -EIO; | ||
536 | break; | ||
537 | } | ||
538 | } while (wait_time); | ||
539 | |||
540 | return status; | ||
541 | } | ||
542 | |||
543 | /* API called in work thread context to set new TX/RX | ||
544 | * maximum frame size values to match MTU. | ||
545 | */ | ||
546 | static int ql_set_port_cfg(struct ql_adapter *qdev) | ||
547 | { | ||
548 | int status; | ||
549 | status = ql_mb_set_port_cfg(qdev); | ||
550 | if (status) | ||
551 | return status; | ||
552 | status = ql_idc_wait(qdev); | ||
553 | return status; | ||
554 | } | ||
555 | |||
556 | /* The following routines are worker threads that process | ||
557 | * events that may sleep waiting for completion. | ||
558 | */ | ||
559 | |||
560 | /* This thread gets the maximum TX and RX frame size values | ||
561 | * from the firmware and, if necessary, changes them to match | ||
562 | * the MTU setting. | ||
563 | */ | ||
564 | void ql_mpi_port_cfg_work(struct work_struct *work) | ||
565 | { | ||
566 | struct ql_adapter *qdev = | ||
567 | container_of(work, struct ql_adapter, mpi_port_cfg_work.work); | ||
568 | struct net_device *ndev = qdev->ndev; | ||
569 | int status; | ||
570 | |||
571 | status = ql_mb_get_port_cfg(qdev); | ||
572 | if (status) { | ||
573 | QPRINTK(qdev, DRV, ERR, | ||
574 | "Bug: Failed to get port config data.\n"); | ||
575 | goto err; | ||
576 | } | ||
577 | |||
578 | if (ndev->mtu <= 2500) | ||
579 | goto end; | ||
580 | else if (qdev->link_config & CFG_JUMBO_FRAME_SIZE && | ||
581 | qdev->max_frame_size == | ||
582 | CFG_DEFAULT_MAX_FRAME_SIZE) | ||
583 | goto end; | ||
584 | |||
585 | qdev->link_config |= CFG_JUMBO_FRAME_SIZE; | ||
586 | qdev->max_frame_size = CFG_DEFAULT_MAX_FRAME_SIZE; | ||
587 | status = ql_set_port_cfg(qdev); | ||
588 | if (status) { | ||
589 | QPRINTK(qdev, DRV, ERR, | ||
590 | "Bug: Failed to set port config data.\n"); | ||
591 | goto err; | ||
592 | } | ||
593 | end: | ||
594 | clear_bit(QL_PORT_CFG, &qdev->flags); | ||
595 | return; | ||
596 | err: | ||
597 | ql_queue_fw_error(qdev); | ||
598 | goto end; | ||
599 | } | ||
600 | |||
394 | void ql_mpi_work(struct work_struct *work) | 601 | void ql_mpi_work(struct work_struct *work) |
395 | { | 602 | { |
396 | struct ql_adapter *qdev = | 603 | struct ql_adapter *qdev = |
@@ -414,5 +621,7 @@ void ql_mpi_reset_work(struct work_struct *work) | |||
414 | { | 621 | { |
415 | struct ql_adapter *qdev = | 622 | struct ql_adapter *qdev = |
416 | container_of(work, struct ql_adapter, mpi_reset_work.work); | 623 | container_of(work, struct ql_adapter, mpi_reset_work.work); |
624 | cancel_delayed_work_sync(&qdev->mpi_work); | ||
625 | cancel_delayed_work_sync(&qdev->mpi_port_cfg_work); | ||
417 | ql_soft_reset_mpi_risc(qdev); | 626 | ql_soft_reset_mpi_risc(qdev); |
418 | } | 627 | } |