diff options
author | Alexandre Bounine <alexandre.bounine@idt.com> | 2010-10-27 18:34:30 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-27 21:03:15 -0400 |
commit | dd5648c9f53b5cbd9f948d752624400545f979fb (patch) | |
tree | 9092a86701a6b4fa5cd722d4f3fc8b803d08b94c /drivers/rapidio/rio.c | |
parent | 68fe4df5d21294401959fa61d5a7094705ed8f6f (diff) |
rapidio: add default handler for error-stopped state
The default error-stopped state handler provides recovery mechanism as
defined by RIO specification.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Micha Nelissen <micha@neli.hopto.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rapidio/rio.c')
-rw-r--r-- | drivers/rapidio/rio.c | 218 |
1 files changed, 187 insertions, 31 deletions
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 74e9d22d95fb..77bd4165238f 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c | |||
@@ -495,6 +495,148 @@ int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock) | |||
495 | } | 495 | } |
496 | 496 | ||
497 | /** | 497 | /** |
498 | * rio_get_input_status - Sends a Link-Request/Input-Status control symbol and | ||
499 | * returns link-response (if requested). | ||
500 | * @rdev: RIO devive to issue Input-status command | ||
501 | * @pnum: Device port number to issue the command | ||
502 | * @lnkresp: Response from a link partner | ||
503 | */ | ||
504 | static int | ||
505 | rio_get_input_status(struct rio_dev *rdev, int pnum, u32 *lnkresp) | ||
506 | { | ||
507 | struct rio_mport *mport = rdev->net->hport; | ||
508 | u16 destid = rdev->rswitch->destid; | ||
509 | u8 hopcount = rdev->rswitch->hopcount; | ||
510 | u32 regval; | ||
511 | int checkcount; | ||
512 | |||
513 | if (lnkresp) { | ||
514 | /* Read from link maintenance response register | ||
515 | * to clear valid bit */ | ||
516 | rio_mport_read_config_32(mport, destid, hopcount, | ||
517 | rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), | ||
518 | ®val); | ||
519 | udelay(50); | ||
520 | } | ||
521 | |||
522 | /* Issue Input-status command */ | ||
523 | rio_mport_write_config_32(mport, destid, hopcount, | ||
524 | rdev->phys_efptr + RIO_PORT_N_MNT_REQ_CSR(pnum), | ||
525 | RIO_MNT_REQ_CMD_IS); | ||
526 | |||
527 | /* Exit if the response is not expected */ | ||
528 | if (lnkresp == NULL) | ||
529 | return 0; | ||
530 | |||
531 | checkcount = 3; | ||
532 | while (checkcount--) { | ||
533 | udelay(50); | ||
534 | rio_mport_read_config_32(mport, destid, hopcount, | ||
535 | rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), | ||
536 | ®val); | ||
537 | if (regval & RIO_PORT_N_MNT_RSP_RVAL) { | ||
538 | *lnkresp = regval; | ||
539 | return 0; | ||
540 | } | ||
541 | } | ||
542 | |||
543 | return -EIO; | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * rio_clr_err_stopped - Clears port Error-stopped states. | ||
548 | * @rdev: Pointer to RIO device control structure | ||
549 | * @pnum: Switch port number to clear errors | ||
550 | * @err_status: port error status (if 0 reads register from device) | ||
551 | */ | ||
552 | static int rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, u32 err_status) | ||
553 | { | ||
554 | struct rio_mport *mport = rdev->net->hport; | ||
555 | u16 destid = rdev->rswitch->destid; | ||
556 | u8 hopcount = rdev->rswitch->hopcount; | ||
557 | struct rio_dev *nextdev = rdev->rswitch->nextdev[pnum]; | ||
558 | u32 regval; | ||
559 | u32 far_ackid, far_linkstat, near_ackid; | ||
560 | |||
561 | if (err_status == 0) | ||
562 | rio_mport_read_config_32(mport, destid, hopcount, | ||
563 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), | ||
564 | &err_status); | ||
565 | |||
566 | if (err_status & RIO_PORT_N_ERR_STS_PW_OUT_ES) { | ||
567 | pr_debug("RIO_EM: servicing Output Error-Stopped state\n"); | ||
568 | /* | ||
569 | * Send a Link-Request/Input-Status control symbol | ||
570 | */ | ||
571 | if (rio_get_input_status(rdev, pnum, ®val)) { | ||
572 | pr_debug("RIO_EM: Input-status response timeout\n"); | ||
573 | goto rd_err; | ||
574 | } | ||
575 | |||
576 | pr_debug("RIO_EM: SP%d Input-status response=0x%08x\n", | ||
577 | pnum, regval); | ||
578 | far_ackid = (regval & RIO_PORT_N_MNT_RSP_ASTAT) >> 5; | ||
579 | far_linkstat = regval & RIO_PORT_N_MNT_RSP_LSTAT; | ||
580 | rio_mport_read_config_32(mport, destid, hopcount, | ||
581 | rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), | ||
582 | ®val); | ||
583 | pr_debug("RIO_EM: SP%d_ACK_STS_CSR=0x%08x\n", pnum, regval); | ||
584 | near_ackid = (regval & RIO_PORT_N_ACK_INBOUND) >> 24; | ||
585 | pr_debug("RIO_EM: SP%d far_ackID=0x%02x far_linkstat=0x%02x" \ | ||
586 | " near_ackID=0x%02x\n", | ||
587 | pnum, far_ackid, far_linkstat, near_ackid); | ||
588 | |||
589 | /* | ||
590 | * If required, synchronize ackIDs of near and | ||
591 | * far sides. | ||
592 | */ | ||
593 | if ((far_ackid != ((regval & RIO_PORT_N_ACK_OUTSTAND) >> 8)) || | ||
594 | (far_ackid != (regval & RIO_PORT_N_ACK_OUTBOUND))) { | ||
595 | /* Align near outstanding/outbound ackIDs with | ||
596 | * far inbound. | ||
597 | */ | ||
598 | rio_mport_write_config_32(mport, destid, | ||
599 | hopcount, rdev->phys_efptr + | ||
600 | RIO_PORT_N_ACK_STS_CSR(pnum), | ||
601 | (near_ackid << 24) | | ||
602 | (far_ackid << 8) | far_ackid); | ||
603 | /* Align far outstanding/outbound ackIDs with | ||
604 | * near inbound. | ||
605 | */ | ||
606 | far_ackid++; | ||
607 | if (nextdev) | ||
608 | rio_write_config_32(nextdev, | ||
609 | nextdev->phys_efptr + | ||
610 | RIO_PORT_N_ACK_STS_CSR(RIO_GET_PORT_NUM(nextdev->swpinfo)), | ||
611 | (far_ackid << 24) | | ||
612 | (near_ackid << 8) | near_ackid); | ||
613 | else | ||
614 | pr_debug("RIO_EM: Invalid nextdev pointer (NULL)\n"); | ||
615 | } | ||
616 | rd_err: | ||
617 | rio_mport_read_config_32(mport, destid, hopcount, | ||
618 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), | ||
619 | &err_status); | ||
620 | pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status); | ||
621 | } | ||
622 | |||
623 | if ((err_status & RIO_PORT_N_ERR_STS_PW_INP_ES) && nextdev) { | ||
624 | pr_debug("RIO_EM: servicing Input Error-Stopped state\n"); | ||
625 | rio_get_input_status(nextdev, | ||
626 | RIO_GET_PORT_NUM(nextdev->swpinfo), NULL); | ||
627 | udelay(50); | ||
628 | |||
629 | rio_mport_read_config_32(mport, destid, hopcount, | ||
630 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), | ||
631 | &err_status); | ||
632 | pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status); | ||
633 | } | ||
634 | |||
635 | return (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | | ||
636 | RIO_PORT_N_ERR_STS_PW_INP_ES)) ? 1 : 0; | ||
637 | } | ||
638 | |||
639 | /** | ||
498 | * rio_inb_pwrite_handler - process inbound port-write message | 640 | * rio_inb_pwrite_handler - process inbound port-write message |
499 | * @pw_msg: pointer to inbound port-write message | 641 | * @pw_msg: pointer to inbound port-write message |
500 | * | 642 | * |
@@ -507,7 +649,7 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) | |||
507 | struct rio_mport *mport; | 649 | struct rio_mport *mport; |
508 | u8 hopcount; | 650 | u8 hopcount; |
509 | u16 destid; | 651 | u16 destid; |
510 | u32 err_status; | 652 | u32 err_status, em_perrdet, em_ltlerrdet; |
511 | int rc, portnum; | 653 | int rc, portnum; |
512 | 654 | ||
513 | rdev = rio_get_comptag(pw_msg->em.comptag, NULL); | 655 | rdev = rio_get_comptag(pw_msg->em.comptag, NULL); |
@@ -524,12 +666,11 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) | |||
524 | { | 666 | { |
525 | u32 i; | 667 | u32 i; |
526 | for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) { | 668 | for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) { |
527 | pr_debug("0x%02x: %08x %08x %08x %08x", | 669 | pr_debug("0x%02x: %08x %08x %08x %08x\n", |
528 | i*4, pw_msg->raw[i], pw_msg->raw[i + 1], | 670 | i*4, pw_msg->raw[i], pw_msg->raw[i + 1], |
529 | pw_msg->raw[i + 2], pw_msg->raw[i + 3]); | 671 | pw_msg->raw[i + 2], pw_msg->raw[i + 3]); |
530 | i += 4; | 672 | i += 4; |
531 | } | 673 | } |
532 | pr_debug("\n"); | ||
533 | } | 674 | } |
534 | #endif | 675 | #endif |
535 | 676 | ||
@@ -573,29 +714,28 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) | |||
573 | &err_status); | 714 | &err_status); |
574 | pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status); | 715 | pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status); |
575 | 716 | ||
576 | if (pw_msg->em.errdetect) { | 717 | if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) { |
577 | pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n", | ||
578 | portnum, pw_msg->em.errdetect); | ||
579 | /* Clear EM Port N Error Detect CSR */ | ||
580 | rio_mport_write_config_32(mport, destid, hopcount, | ||
581 | rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0); | ||
582 | } | ||
583 | 718 | ||
584 | if (pw_msg->em.ltlerrdet) { | 719 | if (!(rdev->rswitch->port_ok & (1 << portnum))) { |
585 | pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n", | 720 | rdev->rswitch->port_ok |= (1 << portnum); |
586 | pw_msg->em.ltlerrdet); | 721 | rio_set_port_lockout(rdev, portnum, 0); |
587 | /* Clear EM L/T Layer Error Detect CSR */ | 722 | /* Schedule Insertion Service */ |
588 | rio_mport_write_config_32(mport, destid, hopcount, | 723 | pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n", |
589 | rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0); | 724 | rio_name(rdev), portnum); |
590 | } | 725 | } |
591 | 726 | ||
592 | /* Clear Port Errors */ | 727 | /* Clear error-stopped states (if reported). |
593 | rio_mport_write_config_32(mport, destid, hopcount, | 728 | * Depending on the link partner state, two attempts |
594 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), | 729 | * may be needed for successful recovery. |
595 | err_status & RIO_PORT_N_ERR_STS_CLR_MASK); | 730 | */ |
731 | if (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | | ||
732 | RIO_PORT_N_ERR_STS_PW_INP_ES)) { | ||
733 | if (rio_clr_err_stopped(rdev, portnum, err_status)) | ||
734 | rio_clr_err_stopped(rdev, portnum, 0); | ||
735 | } | ||
736 | } else { /* if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) */ | ||
596 | 737 | ||
597 | if (rdev->rswitch->port_ok & (1 << portnum)) { | 738 | if (rdev->rswitch->port_ok & (1 << portnum)) { |
598 | if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) { | ||
599 | rdev->rswitch->port_ok &= ~(1 << portnum); | 739 | rdev->rswitch->port_ok &= ~(1 << portnum); |
600 | rio_set_port_lockout(rdev, portnum, 1); | 740 | rio_set_port_lockout(rdev, portnum, 1); |
601 | 741 | ||
@@ -608,17 +748,33 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) | |||
608 | pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n", | 748 | pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n", |
609 | rio_name(rdev), portnum); | 749 | rio_name(rdev), portnum); |
610 | } | 750 | } |
611 | } else { | 751 | } |
612 | if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) { | ||
613 | rdev->rswitch->port_ok |= (1 << portnum); | ||
614 | rio_set_port_lockout(rdev, portnum, 0); | ||
615 | 752 | ||
616 | /* Schedule Insertion Service */ | 753 | rio_mport_read_config_32(mport, destid, hopcount, |
617 | pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n", | 754 | rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet); |
618 | rio_name(rdev), portnum); | 755 | if (em_perrdet) { |
619 | } | 756 | pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n", |
757 | portnum, em_perrdet); | ||
758 | /* Clear EM Port N Error Detect CSR */ | ||
759 | rio_mport_write_config_32(mport, destid, hopcount, | ||
760 | rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0); | ||
620 | } | 761 | } |
621 | 762 | ||
763 | rio_mport_read_config_32(mport, destid, hopcount, | ||
764 | rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet); | ||
765 | if (em_ltlerrdet) { | ||
766 | pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n", | ||
767 | em_ltlerrdet); | ||
768 | /* Clear EM L/T Layer Error Detect CSR */ | ||
769 | rio_mport_write_config_32(mport, destid, hopcount, | ||
770 | rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0); | ||
771 | } | ||
772 | |||
773 | /* Clear remaining error bits */ | ||
774 | rio_mport_write_config_32(mport, destid, hopcount, | ||
775 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), | ||
776 | err_status & RIO_PORT_N_ERR_STS_CLR_MASK); | ||
777 | |||
622 | /* Clear Port-Write Pending bit */ | 778 | /* Clear Port-Write Pending bit */ |
623 | rio_mport_write_config_32(mport, destid, hopcount, | 779 | rio_mport_write_config_32(mport, destid, hopcount, |
624 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), | 780 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), |