Skip to content

Commit 70c1595

Browse files
committed
ata: ahci: Allow ignoring the external/hotplug capability of ports
Commit 4edf150 ("ata: ahci: Disallow LPM policy control for external ports") introduced disabling link power management (LPM) for ports that are advertized as external/hotplug capable. This is necessary to force the maximum power policy (ATA_LPM_MAX_POWER) onto the port link to ensure that the hotplug capability of the port is functional. However, doing so blindly for all ports can prevent systems from going into a low power state, even if the external/hotplug ports on the system are unused. E.g., a laptop may see the internal SATA slot of a docking station as an external hotplug capable port, and in such case, the user may prefer to not use the port and to favor instead enabling LPM to allow the laptop to transition to low power states. Since there is no easy method to automatically detect such choice, introduce the new mask_port_ext module parameter to allow a user to ignore the external/hotplug capability of a port. The format for this parameter value is identical to the format used for the mask_port_map parameter: a mask can be defined for all AHCI adapters of a system or for a particular adapters identified with their PCI IDs (bus:dev.func format). The function ahci_get_port_map_mask() is renamed to ahci_get_port_mask() and modified to return a mask, either for the port map mask of an adapter (to ignore ports) or for the external/hotplug capability of an adapter. Differentiation between map_port_mask and map_port_ext_mask is done by passing the parameter string to ahci_get_port_mask() as a second argument. To be consistent with this change, the function ahci_apply_port_map_mask() is renamed ahci_port_mask() and changed to return a mask value. The mask for the external/hotplug capability for an adapter, if defined by the map_port_ext_mask parameter, is stored in the new field mask_port_ext of struct ahci_host_priv. ahci_mark_external_port() is modified to not set the ATA_PFLAG_EXTERNAL flag for a port if hpriv->mask_port_ext includes the number of the port. In such case, an information message is printed to notify that the external/hotplug capability is being ignored. Reported-by: Dieter Mummenschanz <[email protected]> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220465 Fixes: 4edf150 ("ata: ahci: Disallow LPM policy control for external ports") Signed-off-by: Damien Le Moal <[email protected]> Tested-by: Dieter Mummenschanz <[email protected]>
1 parent 58768b0 commit 70c1595

File tree

2 files changed

+41
-17
lines changed

2 files changed

+41
-17
lines changed

drivers/ata/ahci.c

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -689,40 +689,50 @@ MODULE_PARM_DESC(mask_port_map,
689689
"where <pci_dev> is the PCI ID of an AHCI controller in the "
690690
"form \"domain:bus:dev.func\"");
691691

692-
static void ahci_apply_port_map_mask(struct device *dev,
693-
struct ahci_host_priv *hpriv, char *mask_s)
692+
static char *ahci_mask_port_ext;
693+
module_param_named(mask_port_ext, ahci_mask_port_ext, charp, 0444);
694+
MODULE_PARM_DESC(mask_port_ext,
695+
"32-bits mask to ignore the external/hotplug capability of ports. "
696+
"Valid values are: "
697+
"\"<mask>\" to apply the same mask to all AHCI controller "
698+
"devices, and \"<pci_dev>=<mask>,<pci_dev>=<mask>,...\" to "
699+
"specify different masks for the controllers specified, "
700+
"where <pci_dev> is the PCI ID of an AHCI controller in the "
701+
"form \"domain:bus:dev.func\"");
702+
703+
static u32 ahci_port_mask(struct device *dev, char *mask_s)
694704
{
695705
unsigned int mask;
696706

697707
if (kstrtouint(mask_s, 0, &mask)) {
698708
dev_err(dev, "Invalid port map mask\n");
699-
return;
709+
return 0;
700710
}
701711

702-
hpriv->mask_port_map = mask;
712+
return mask;
703713
}
704714

705-
static void ahci_get_port_map_mask(struct device *dev,
706-
struct ahci_host_priv *hpriv)
715+
static u32 ahci_get_port_mask(struct device *dev, char *mask_p)
707716
{
708717
char *param, *end, *str, *mask_s;
709718
char *name;
719+
u32 mask = 0;
710720

711-
if (!strlen(ahci_mask_port_map))
712-
return;
721+
if (!mask_p || !strlen(mask_p))
722+
return 0;
713723

714-
str = kstrdup(ahci_mask_port_map, GFP_KERNEL);
724+
str = kstrdup(mask_p, GFP_KERNEL);
715725
if (!str)
716-
return;
726+
return 0;
717727

718728
/* Handle single mask case */
719729
if (!strchr(str, '=')) {
720-
ahci_apply_port_map_mask(dev, hpriv, str);
730+
mask = ahci_port_mask(dev, str);
721731
goto free;
722732
}
723733

724734
/*
725-
* Mask list case: parse the parameter to apply the mask only if
735+
* Mask list case: parse the parameter to get the mask only if
726736
* the device name matches.
727737
*/
728738
param = str;
@@ -752,11 +762,13 @@ static void ahci_get_port_map_mask(struct device *dev,
752762
param++;
753763
}
754764

755-
ahci_apply_port_map_mask(dev, hpriv, mask_s);
765+
mask = ahci_port_mask(dev, mask_s);
756766
}
757767

758768
free:
759769
kfree(str);
770+
771+
return mask;
760772
}
761773

762774
static void ahci_pci_save_initial_config(struct pci_dev *pdev,
@@ -782,8 +794,10 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev,
782794
}
783795

784796
/* Handle port map masks passed as module parameter. */
785-
if (ahci_mask_port_map)
786-
ahci_get_port_map_mask(&pdev->dev, hpriv);
797+
hpriv->mask_port_map =
798+
ahci_get_port_mask(&pdev->dev, ahci_mask_port_map);
799+
hpriv->mask_port_ext =
800+
ahci_get_port_mask(&pdev->dev, ahci_mask_port_ext);
787801

788802
ahci_save_initial_config(&pdev->dev, hpriv);
789803
}
@@ -1757,11 +1771,20 @@ static void ahci_mark_external_port(struct ata_port *ap)
17571771
void __iomem *port_mmio = ahci_port_base(ap);
17581772
u32 tmp;
17591773

1760-
/* mark external ports (hotplug-capable, eSATA) */
1774+
/*
1775+
* Mark external ports (hotplug-capable, eSATA), unless we were asked to
1776+
* ignore this feature.
1777+
*/
17611778
tmp = readl(port_mmio + PORT_CMD);
17621779
if (((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS)) ||
1763-
(tmp & PORT_CMD_HPCP))
1780+
(tmp & PORT_CMD_HPCP)) {
1781+
if (hpriv->mask_port_ext & (1U << ap->port_no)) {
1782+
ata_port_info(ap,
1783+
"Ignoring external/hotplug capability\n");
1784+
return;
1785+
}
17641786
ap->pflags |= ATA_PFLAG_EXTERNAL;
1787+
}
17651788
}
17661789

17671790
static void ahci_update_initial_lpm_policy(struct ata_port *ap)

drivers/ata/ahci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ struct ahci_host_priv {
330330
/* Input fields */
331331
unsigned int flags; /* AHCI_HFLAG_* */
332332
u32 mask_port_map; /* Mask of valid ports */
333+
u32 mask_port_ext; /* Mask of ports ext capability */
333334

334335
void __iomem * mmio; /* bus-independent mem map */
335336
u32 cap; /* cap to use */

0 commit comments

Comments
 (0)