diff options
-rw-r--r-- | drivers/ata/sata_mv.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 1991eb22e388..b948dc866e04 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c | |||
@@ -545,6 +545,8 @@ static int mv_pmp_hardreset(struct ata_link *link, unsigned int *class, | |||
545 | static int mv_softreset(struct ata_link *link, unsigned int *class, | 545 | static int mv_softreset(struct ata_link *link, unsigned int *class, |
546 | unsigned long deadline); | 546 | unsigned long deadline); |
547 | static void mv_pmp_error_handler(struct ata_port *ap); | 547 | static void mv_pmp_error_handler(struct ata_port *ap); |
548 | static void mv_process_crpb_entries(struct ata_port *ap, | ||
549 | struct mv_port_priv *pp); | ||
548 | 550 | ||
549 | /* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below | 551 | /* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below |
550 | * because we have to allow room for worst case splitting of | 552 | * because we have to allow room for worst case splitting of |
@@ -1156,6 +1158,10 @@ static void mv_config_fbs(void __iomem *port_mmio, int want_ncq, int want_fbs) | |||
1156 | if (want_fbs) { | 1158 | if (want_fbs) { |
1157 | new_fiscfg = old_fiscfg | FISCFG_SINGLE_SYNC; | 1159 | new_fiscfg = old_fiscfg | FISCFG_SINGLE_SYNC; |
1158 | new_ltmode = old_ltmode | LTMODE_BIT8; | 1160 | new_ltmode = old_ltmode | LTMODE_BIT8; |
1161 | if (want_ncq) | ||
1162 | new_haltcond &= ~EDMA_ERR_DEV; | ||
1163 | else | ||
1164 | new_fiscfg |= FISCFG_WAIT_DEV_ERR; | ||
1159 | } | 1165 | } |
1160 | 1166 | ||
1161 | if (new_fiscfg != old_fiscfg) | 1167 | if (new_fiscfg != old_fiscfg) |
@@ -1627,6 +1633,154 @@ static void mv_pmp_error_handler(struct ata_port *ap) | |||
1627 | sata_pmp_error_handler(ap); | 1633 | sata_pmp_error_handler(ap); |
1628 | } | 1634 | } |
1629 | 1635 | ||
1636 | static unsigned int mv_get_err_pmp_map(struct ata_port *ap) | ||
1637 | { | ||
1638 | void __iomem *port_mmio = mv_ap_base(ap); | ||
1639 | |||
1640 | return readl(port_mmio + SATA_TESTCTL_OFS) >> 16; | ||
1641 | } | ||
1642 | |||
1643 | static int mv_count_pmp_links(unsigned int pmp_map) | ||
1644 | { | ||
1645 | unsigned int link_count = 0; | ||
1646 | |||
1647 | while (pmp_map) { | ||
1648 | link_count += (pmp_map & 1); | ||
1649 | pmp_map >>= 1; | ||
1650 | } | ||
1651 | return link_count; | ||
1652 | } | ||
1653 | |||
1654 | static void mv_pmp_eh_prep(struct ata_port *ap, unsigned int pmp_map) | ||
1655 | { | ||
1656 | struct ata_eh_info *ehi; | ||
1657 | unsigned int pmp; | ||
1658 | |||
1659 | /* | ||
1660 | * Initialize EH info for PMPs which saw device errors | ||
1661 | */ | ||
1662 | ehi = &ap->link.eh_info; | ||
1663 | for (pmp = 0; pmp_map != 0; pmp++) { | ||
1664 | unsigned int this_pmp = (1 << pmp); | ||
1665 | if (pmp_map & this_pmp) { | ||
1666 | struct ata_link *link = &ap->pmp_link[pmp]; | ||
1667 | |||
1668 | pmp_map &= ~this_pmp; | ||
1669 | ehi = &link->eh_info; | ||
1670 | ata_ehi_clear_desc(ehi); | ||
1671 | ata_ehi_push_desc(ehi, "dev err"); | ||
1672 | ehi->err_mask |= AC_ERR_DEV; | ||
1673 | ehi->action |= ATA_EH_RESET; | ||
1674 | ata_link_abort(link); | ||
1675 | } | ||
1676 | } | ||
1677 | } | ||
1678 | |||
1679 | static int mv_handle_fbs_ncq_dev_err(struct ata_port *ap) | ||
1680 | { | ||
1681 | struct mv_port_priv *pp = ap->private_data; | ||
1682 | int failed_links; | ||
1683 | unsigned int old_map, new_map; | ||
1684 | |||
1685 | /* | ||
1686 | * Device error during FBS+NCQ operation: | ||
1687 | * | ||
1688 | * Set a port flag to prevent further I/O being enqueued. | ||
1689 | * Leave the EDMA running to drain outstanding commands from this port. | ||
1690 | * Perform the post-mortem/EH only when all responses are complete. | ||
1691 | * Follow recovery sequence from 6042/7042 datasheet (7.3.15.4.2.2). | ||
1692 | */ | ||
1693 | if (!(pp->pp_flags & MV_PP_FLAG_DELAYED_EH)) { | ||
1694 | pp->pp_flags |= MV_PP_FLAG_DELAYED_EH; | ||
1695 | pp->delayed_eh_pmp_map = 0; | ||
1696 | } | ||
1697 | old_map = pp->delayed_eh_pmp_map; | ||
1698 | new_map = old_map | mv_get_err_pmp_map(ap); | ||
1699 | |||
1700 | if (old_map != new_map) { | ||
1701 | pp->delayed_eh_pmp_map = new_map; | ||
1702 | mv_pmp_eh_prep(ap, new_map & ~old_map); | ||
1703 | } | ||
1704 | failed_links = mv_count_pmp_links(new_map); | ||
1705 | |||
1706 | ata_port_printk(ap, KERN_INFO, "%s: pmp_map=%04x qc_map=%04x " | ||
1707 | "failed_links=%d nr_active_links=%d\n", | ||
1708 | __func__, pp->delayed_eh_pmp_map, | ||
1709 | ap->qc_active, failed_links, | ||
1710 | ap->nr_active_links); | ||
1711 | |||
1712 | if (ap->nr_active_links <= failed_links) { | ||
1713 | mv_process_crpb_entries(ap, pp); | ||
1714 | mv_stop_edma(ap); | ||
1715 | mv_eh_freeze(ap); | ||
1716 | ata_port_printk(ap, KERN_INFO, "%s: done\n", __func__); | ||
1717 | return 1; /* handled */ | ||
1718 | } | ||
1719 | ata_port_printk(ap, KERN_INFO, "%s: waiting\n", __func__); | ||
1720 | return 1; /* handled */ | ||
1721 | } | ||
1722 | |||
1723 | static int mv_handle_fbs_non_ncq_dev_err(struct ata_port *ap) | ||
1724 | { | ||
1725 | /* | ||
1726 | * Possible future enhancement: | ||
1727 | * | ||
1728 | * FBS+non-NCQ operation is not yet implemented. | ||
1729 | * See related notes in mv_edma_cfg(). | ||
1730 | * | ||
1731 | * Device error during FBS+non-NCQ operation: | ||
1732 | * | ||
1733 | * We need to snapshot the shadow registers for each failed command. | ||
1734 | * Follow recovery sequence from 6042/7042 datasheet (7.3.15.4.2.3). | ||
1735 | */ | ||
1736 | return 0; /* not handled */ | ||
1737 | } | ||
1738 | |||
1739 | static int mv_handle_dev_err(struct ata_port *ap, u32 edma_err_cause) | ||
1740 | { | ||
1741 | struct mv_port_priv *pp = ap->private_data; | ||
1742 | |||
1743 | if (!(pp->pp_flags & MV_PP_FLAG_EDMA_EN)) | ||
1744 | return 0; /* EDMA was not active: not handled */ | ||
1745 | if (!(pp->pp_flags & MV_PP_FLAG_FBS_EN)) | ||
1746 | return 0; /* FBS was not active: not handled */ | ||
1747 | |||
1748 | if (!(edma_err_cause & EDMA_ERR_DEV)) | ||
1749 | return 0; /* non DEV error: not handled */ | ||
1750 | edma_err_cause &= ~EDMA_ERR_IRQ_TRANSIENT; | ||
1751 | if (edma_err_cause & ~(EDMA_ERR_DEV | EDMA_ERR_SELF_DIS)) | ||
1752 | return 0; /* other problems: not handled */ | ||
1753 | |||
1754 | if (pp->pp_flags & MV_PP_FLAG_NCQ_EN) { | ||
1755 | /* | ||
1756 | * EDMA should NOT have self-disabled for this case. | ||
1757 | * If it did, then something is wrong elsewhere, | ||
1758 | * and we cannot handle it here. | ||
1759 | */ | ||
1760 | if (edma_err_cause & EDMA_ERR_SELF_DIS) { | ||
1761 | ata_port_printk(ap, KERN_WARNING, | ||
1762 | "%s: err_cause=0x%x pp_flags=0x%x\n", | ||
1763 | __func__, edma_err_cause, pp->pp_flags); | ||
1764 | return 0; /* not handled */ | ||
1765 | } | ||
1766 | return mv_handle_fbs_ncq_dev_err(ap); | ||
1767 | } else { | ||
1768 | /* | ||
1769 | * EDMA should have self-disabled for this case. | ||
1770 | * If it did not, then something is wrong elsewhere, | ||
1771 | * and we cannot handle it here. | ||
1772 | */ | ||
1773 | if (!(edma_err_cause & EDMA_ERR_SELF_DIS)) { | ||
1774 | ata_port_printk(ap, KERN_WARNING, | ||
1775 | "%s: err_cause=0x%x pp_flags=0x%x\n", | ||
1776 | __func__, edma_err_cause, pp->pp_flags); | ||
1777 | return 0; /* not handled */ | ||
1778 | } | ||
1779 | return mv_handle_fbs_non_ncq_dev_err(ap); | ||
1780 | } | ||
1781 | return 0; /* not handled */ | ||
1782 | } | ||
1783 | |||
1630 | static void mv_unexpected_intr(struct ata_port *ap, int edma_was_enabled) | 1784 | static void mv_unexpected_intr(struct ata_port *ap, int edma_was_enabled) |
1631 | { | 1785 | { |
1632 | struct ata_eh_info *ehi = &ap->link.eh_info; | 1786 | struct ata_eh_info *ehi = &ap->link.eh_info; |
@@ -1683,6 +1837,15 @@ static void mv_err_intr(struct ata_port *ap) | |||
1683 | ata_port_printk(ap, KERN_INFO, "%s: err_cause=%08x pp_flags=0x%x\n", | 1837 | ata_port_printk(ap, KERN_INFO, "%s: err_cause=%08x pp_flags=0x%x\n", |
1684 | __func__, edma_err_cause, pp->pp_flags); | 1838 | __func__, edma_err_cause, pp->pp_flags); |
1685 | 1839 | ||
1840 | if (edma_err_cause & EDMA_ERR_DEV) { | ||
1841 | /* | ||
1842 | * Device errors during FIS-based switching operation | ||
1843 | * require special handling. | ||
1844 | */ | ||
1845 | if (mv_handle_dev_err(ap, edma_err_cause)) | ||
1846 | return; | ||
1847 | } | ||
1848 | |||
1686 | qc = mv_get_active_qc(ap); | 1849 | qc = mv_get_active_qc(ap); |
1687 | ata_ehi_clear_desc(ehi); | 1850 | ata_ehi_clear_desc(ehi); |
1688 | ata_ehi_push_desc(ehi, "edma_err_cause=%08x pp_flags=%08x", | 1851 | ata_ehi_push_desc(ehi, "edma_err_cause=%08x pp_flags=%08x", |
@@ -1861,6 +2024,8 @@ static void mv_port_intr(struct ata_port *ap, u32 port_cause) | |||
1861 | */ | 2024 | */ |
1862 | if (edma_was_enabled && (port_cause & DONE_IRQ)) { | 2025 | if (edma_was_enabled && (port_cause & DONE_IRQ)) { |
1863 | mv_process_crpb_entries(ap, pp); | 2026 | mv_process_crpb_entries(ap, pp); |
2027 | if (pp->pp_flags & MV_PP_FLAG_DELAYED_EH) | ||
2028 | mv_handle_fbs_ncq_dev_err(ap); | ||
1864 | } | 2029 | } |
1865 | /* | 2030 | /* |
1866 | * Handle chip-reported errors, or continue on to handle PIO. | 2031 | * Handle chip-reported errors, or continue on to handle PIO. |