diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
| -rw-r--r-- | drivers/ata/libata-core.c | 196 |
1 files changed, 195 insertions, 1 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index e512903b8db..63035d71a61 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
| @@ -620,6 +620,177 @@ void ata_dev_disable(struct ata_device *dev) | |||
| 620 | } | 620 | } |
| 621 | } | 621 | } |
| 622 | 622 | ||
| 623 | static int ata_dev_set_dipm(struct ata_device *dev, enum link_pm policy) | ||
| 624 | { | ||
| 625 | struct ata_link *link = dev->link; | ||
| 626 | struct ata_port *ap = link->ap; | ||
| 627 | u32 scontrol; | ||
| 628 | unsigned int err_mask; | ||
| 629 | int rc; | ||
| 630 | |||
| 631 | /* | ||
| 632 | * disallow DIPM for drivers which haven't set | ||
| 633 | * ATA_FLAG_IPM. This is because when DIPM is enabled, | ||
| 634 | * phy ready will be set in the interrupt status on | ||
| 635 | * state changes, which will cause some drivers to | ||
| 636 | * think there are errors - additionally drivers will | ||
| 637 | * need to disable hot plug. | ||
| 638 | */ | ||
| 639 | if (!(ap->flags & ATA_FLAG_IPM) || !ata_dev_enabled(dev)) { | ||
| 640 | ap->pm_policy = NOT_AVAILABLE; | ||
| 641 | return -EINVAL; | ||
| 642 | } | ||
| 643 | |||
| 644 | /* | ||
| 645 | * For DIPM, we will only enable it for the | ||
| 646 | * min_power setting. | ||
| 647 | * | ||
| 648 | * Why? Because Disks are too stupid to know that | ||
| 649 | * If the host rejects a request to go to SLUMBER | ||
| 650 | * they should retry at PARTIAL, and instead it | ||
| 651 | * just would give up. So, for medium_power to | ||
| 652 | * work at all, we need to only allow HIPM. | ||
| 653 | */ | ||
| 654 | rc = sata_scr_read(link, SCR_CONTROL, &scontrol); | ||
| 655 | if (rc) | ||
| 656 | return rc; | ||
| 657 | |||
| 658 | switch (policy) { | ||
| 659 | case MIN_POWER: | ||
| 660 | /* no restrictions on IPM transitions */ | ||
| 661 | scontrol &= ~(0x3 << 8); | ||
| 662 | rc = sata_scr_write(link, SCR_CONTROL, scontrol); | ||
| 663 | if (rc) | ||
| 664 | return rc; | ||
| 665 | |||
| 666 | /* enable DIPM */ | ||
| 667 | if (dev->flags & ATA_DFLAG_DIPM) | ||
| 668 | err_mask = ata_dev_set_feature(dev, | ||
| 669 | SETFEATURES_SATA_ENABLE, SATA_DIPM); | ||
| 670 | break; | ||
| 671 | case MEDIUM_POWER: | ||
| 672 | /* allow IPM to PARTIAL */ | ||
| 673 | scontrol &= ~(0x1 << 8); | ||
| 674 | scontrol |= (0x2 << 8); | ||
| 675 | rc = sata_scr_write(link, SCR_CONTROL, scontrol); | ||
| 676 | if (rc) | ||
| 677 | return rc; | ||
| 678 | |||
| 679 | /* disable DIPM */ | ||
| 680 | if (ata_dev_enabled(dev) && (dev->flags & ATA_DFLAG_DIPM)) | ||
| 681 | err_mask = ata_dev_set_feature(dev, | ||
| 682 | SETFEATURES_SATA_DISABLE, SATA_DIPM); | ||
| 683 | break; | ||
| 684 | case NOT_AVAILABLE: | ||
| 685 | case MAX_PERFORMANCE: | ||
| 686 | /* disable all IPM transitions */ | ||
| 687 | scontrol |= (0x3 << 8); | ||
| 688 | rc = sata_scr_write(link, SCR_CONTROL, scontrol); | ||
| 689 | if (rc) | ||
| 690 | return rc; | ||
| 691 | |||
| 692 | /* disable DIPM */ | ||
| 693 | if (ata_dev_enabled(dev) && (dev->flags & ATA_DFLAG_DIPM)) | ||
| 694 | err_mask = ata_dev_set_feature(dev, | ||
| 695 | SETFEATURES_SATA_DISABLE, SATA_DIPM); | ||
| 696 | break; | ||
| 697 | } | ||
| 698 | |||
| 699 | /* FIXME: handle SET FEATURES failure */ | ||
| 700 | (void) err_mask; | ||
| 701 | |||
| 702 | return 0; | ||
| 703 | } | ||
| 704 | |||
| 705 | /** | ||
| 706 | * ata_dev_enable_pm - enable SATA interface power management | ||
| 707 | * @device - device to enable ipm for | ||
| 708 | * @policy - the link power management policy | ||
| 709 | * | ||
| 710 | * Enable SATA Interface power management. This will enable | ||
| 711 | * Device Interface Power Management (DIPM) for min_power | ||
| 712 | * policy, and then call driver specific callbacks for | ||
| 713 | * enabling Host Initiated Power management. | ||
| 714 | * | ||
| 715 | * Locking: Caller. | ||
| 716 | * Returns: -EINVAL if IPM is not supported, 0 otherwise. | ||
| 717 | */ | ||
| 718 | void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy) | ||
| 719 | { | ||
| 720 | int rc = 0; | ||
| 721 | struct ata_port *ap = dev->link->ap; | ||
| 722 | |||
| 723 | /* set HIPM first, then DIPM */ | ||
| 724 | if (ap->ops->enable_pm) | ||
| 725 | rc = ap->ops->enable_pm(ap, policy); | ||
| 726 | if (rc) | ||
| 727 | goto enable_pm_out; | ||
| 728 | rc = ata_dev_set_dipm(dev, policy); | ||
| 729 | |||
| 730 | enable_pm_out: | ||
| 731 | if (rc) | ||
| 732 | ap->pm_policy = MAX_PERFORMANCE; | ||
| 733 | else | ||
| 734 | ap->pm_policy = policy; | ||
| 735 | return /* rc */; /* hopefully we can use 'rc' eventually */ | ||
| 736 | } | ||
| 737 | |||
| 738 | /** | ||
| 739 | * ata_dev_disable_pm - disable SATA interface power management | ||
| 740 | * @device - device to enable ipm for | ||
| 741 | * | ||
| 742 | * Disable SATA Interface power management. This will disable | ||
| 743 | * Device Interface Power Management (DIPM) without changing | ||
| 744 | * policy, call driver specific callbacks for disabling Host | ||
| 745 | * Initiated Power management. | ||
| 746 | * | ||
| 747 | * Locking: Caller. | ||
| 748 | * Returns: void | ||
| 749 | */ | ||
| 750 | static void ata_dev_disable_pm(struct ata_device *dev) | ||
| 751 | { | ||
| 752 | struct ata_port *ap = dev->link->ap; | ||
| 753 | |||
| 754 | ata_dev_set_dipm(dev, MAX_PERFORMANCE); | ||
| 755 | if (ap->ops->disable_pm) | ||
| 756 | ap->ops->disable_pm(ap); | ||
| 757 | } | ||
| 758 | |||
| 759 | void ata_lpm_schedule(struct ata_port *ap, enum link_pm policy) | ||
| 760 | { | ||
| 761 | ap->pm_policy = policy; | ||
| 762 | ap->link.eh_info.action |= ATA_EHI_LPM; | ||
| 763 | ap->link.eh_info.flags |= ATA_EHI_NO_AUTOPSY; | ||
| 764 | ata_port_schedule_eh(ap); | ||
| 765 | } | ||
| 766 | |||
| 767 | static void ata_lpm_enable(struct ata_host *host) | ||
| 768 | { | ||
| 769 | struct ata_link *link; | ||
| 770 | struct ata_port *ap; | ||
| 771 | struct ata_device *dev; | ||
| 772 | int i; | ||
| 773 | |||
| 774 | for (i = 0; i < host->n_ports; i++) { | ||
| 775 | ap = host->ports[i]; | ||
| 776 | ata_port_for_each_link(link, ap) { | ||
| 777 | ata_link_for_each_dev(dev, link) | ||
| 778 | ata_dev_disable_pm(dev); | ||
| 779 | } | ||
| 780 | } | ||
| 781 | } | ||
| 782 | |||
| 783 | static void ata_lpm_disable(struct ata_host *host) | ||
| 784 | { | ||
| 785 | int i; | ||
| 786 | |||
| 787 | for (i = 0; i < host->n_ports; i++) { | ||
| 788 | struct ata_port *ap = host->ports[i]; | ||
| 789 | ata_lpm_schedule(ap, ap->pm_policy); | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | |||
| 623 | /** | 794 | /** |
| 624 | * ata_devchk - PATA device presence detection | 795 | * ata_devchk - PATA device presence detection |
| 625 | * @ap: ATA channel to examine | 796 | * @ap: ATA channel to examine |
| @@ -2101,6 +2272,13 @@ int ata_dev_configure(struct ata_device *dev) | |||
| 2101 | if (dev->flags & ATA_DFLAG_LBA48) | 2272 | if (dev->flags & ATA_DFLAG_LBA48) |
| 2102 | dev->max_sectors = ATA_MAX_SECTORS_LBA48; | 2273 | dev->max_sectors = ATA_MAX_SECTORS_LBA48; |
| 2103 | 2274 | ||
| 2275 | if (!(dev->horkage & ATA_HORKAGE_IPM)) { | ||
| 2276 | if (ata_id_has_hipm(dev->id)) | ||
| 2277 | dev->flags |= ATA_DFLAG_HIPM; | ||
| 2278 | if (ata_id_has_dipm(dev->id)) | ||
| 2279 | dev->flags |= ATA_DFLAG_DIPM; | ||
| 2280 | } | ||
| 2281 | |||
| 2104 | if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) { | 2282 | if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) { |
| 2105 | /* Let the user know. We don't want to disallow opens for | 2283 | /* Let the user know. We don't want to disallow opens for |
| 2106 | rescue purposes, or in case the vendor is just a blithering | 2284 | rescue purposes, or in case the vendor is just a blithering |
| @@ -2126,6 +2304,13 @@ int ata_dev_configure(struct ata_device *dev) | |||
| 2126 | dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128, | 2304 | dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128, |
| 2127 | dev->max_sectors); | 2305 | dev->max_sectors); |
| 2128 | 2306 | ||
| 2307 | if (ata_dev_blacklisted(dev) & ATA_HORKAGE_IPM) { | ||
| 2308 | dev->horkage |= ATA_HORKAGE_IPM; | ||
| 2309 | |||
| 2310 | /* reset link pm_policy for this port to no pm */ | ||
| 2311 | ap->pm_policy = MAX_PERFORMANCE; | ||
| 2312 | } | ||
| 2313 | |||
| 2129 | if (ap->ops->dev_config) | 2314 | if (ap->ops->dev_config) |
| 2130 | ap->ops->dev_config(dev); | 2315 | ap->ops->dev_config(dev); |
| 2131 | 2316 | ||
| @@ -6362,6 +6547,12 @@ int ata_host_suspend(struct ata_host *host, pm_message_t mesg) | |||
| 6362 | { | 6547 | { |
| 6363 | int rc; | 6548 | int rc; |
| 6364 | 6549 | ||
| 6550 | /* | ||
| 6551 | * disable link pm on all ports before requesting | ||
| 6552 | * any pm activity | ||
| 6553 | */ | ||
| 6554 | ata_lpm_enable(host); | ||
| 6555 | |||
| 6365 | rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1); | 6556 | rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1); |
| 6366 | if (rc == 0) | 6557 | if (rc == 0) |
| 6367 | host->dev->power.power_state = mesg; | 6558 | host->dev->power.power_state = mesg; |
| @@ -6384,6 +6575,9 @@ void ata_host_resume(struct ata_host *host) | |||
| 6384 | ata_host_request_pm(host, PMSG_ON, ATA_EH_SOFTRESET, | 6575 | ata_host_request_pm(host, PMSG_ON, ATA_EH_SOFTRESET, |
| 6385 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); | 6576 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); |
| 6386 | host->dev->power.power_state = PMSG_ON; | 6577 | host->dev->power.power_state = PMSG_ON; |
| 6578 | |||
| 6579 | /* reenable link pm */ | ||
| 6580 | ata_lpm_disable(host); | ||
| 6387 | } | 6581 | } |
| 6388 | #endif | 6582 | #endif |
| 6389 | 6583 | ||
| @@ -6926,6 +7120,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) | |||
| 6926 | struct ata_port *ap = host->ports[i]; | 7120 | struct ata_port *ap = host->ports[i]; |
| 6927 | 7121 | ||
| 6928 | ata_scsi_scan_host(ap, 1); | 7122 | ata_scsi_scan_host(ap, 1); |
| 7123 | ata_lpm_schedule(ap, ap->pm_policy); | ||
| 6929 | } | 7124 | } |
| 6930 | 7125 | ||
| 6931 | return 0; | 7126 | return 0; |
| @@ -7322,7 +7517,6 @@ const struct ata_port_info ata_dummy_port_info = { | |||
| 7322 | * likely to change as new drivers are added and updated. | 7517 | * likely to change as new drivers are added and updated. |
| 7323 | * Do not depend on ABI/API stability. | 7518 | * Do not depend on ABI/API stability. |
| 7324 | */ | 7519 | */ |
| 7325 | |||
| 7326 | EXPORT_SYMBOL_GPL(sata_deb_timing_normal); | 7520 | EXPORT_SYMBOL_GPL(sata_deb_timing_normal); |
| 7327 | EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); | 7521 | EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); |
| 7328 | EXPORT_SYMBOL_GPL(sata_deb_timing_long); | 7522 | EXPORT_SYMBOL_GPL(sata_deb_timing_long); |
