]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ata: libata-core: Add 'external' to the libata.force kernel parameter
authorNiklas Cassel <cassel@kernel.org>
Thu, 30 Jan 2025 13:35:46 +0000 (14:35 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 20 Apr 2025 08:17:41 +0000 (10:17 +0200)
[ Upstream commit deca423213cb33feda15e261e7b5b992077a6a08 ]

Commit ae1f3db006b7 ("ata: ahci: do not enable LPM on external ports")
changed so that LPM is not enabled on external ports (hotplug-capable or
eSATA ports).

This is because hotplug and LPM are mutually exclusive, see 7.3.1 Hot Plug
Removal Detection and Power Management Interaction in AHCI 1.3.1.

This does require that firmware has set the appropate bits (HPCP or ESP)
in PxCMD (which is a per port register in the AHCI controller).

If the firmware has failed to mark a port as hotplug-capable or eSATA in
PxCMD, then there is currently not much a user can do.

If LPM is enabled on the port, hotplug insertions and removals will not be
detected on that port.

In order to allow a user to fix up broken firmware, add 'external' to the
libata.force kernel parameter.

libata.force can be specified either on the kernel command line, or as a
kernel module parameter.

For more information, see Documentation/admin-guide/kernel-parameters.txt.

Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Link: https://lore.kernel.org/r/20250130133544.219297-4-cassel@kernel.org
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Documentation/admin-guide/kernel-parameters.txt
drivers/ata/libata-core.c

index 3872bc6ec49d63772755504966ae70113f24a1db..d9c55dcbf4a215112efb0b53b451ca2ba8f2b972 100644 (file)
                        * max_sec_lba48: Set or clear transfer size limit to
                          65535 sectors.
 
+                       * external: Mark port as external (hotplug-capable).
+
                        * [no]lpm: Enable or disable link power management.
 
                        * [no]setxfer: Indicate if transfer speed mode setting
index d956735e2a7645b522e257319bfd7b7e9f9153be..0cb97181d10a9e2a109eb7827b73114456ef8c68 100644 (file)
@@ -88,6 +88,7 @@ struct ata_force_param {
        unsigned int    xfer_mask;
        unsigned int    quirk_on;
        unsigned int    quirk_off;
+       unsigned int    pflags_on;
        u16             lflags_on;
        u16             lflags_off;
 };
@@ -331,6 +332,35 @@ void ata_force_cbl(struct ata_port *ap)
        }
 }
 
+/**
+ *     ata_force_pflags - force port flags according to libata.force
+ *     @ap: ATA port of interest
+ *
+ *     Force port flags according to libata.force and whine about it.
+ *
+ *     LOCKING:
+ *     EH context.
+ */
+static void ata_force_pflags(struct ata_port *ap)
+{
+       int i;
+
+       for (i = ata_force_tbl_size - 1; i >= 0; i--) {
+               const struct ata_force_ent *fe = &ata_force_tbl[i];
+
+               if (fe->port != -1 && fe->port != ap->print_id)
+                       continue;
+
+               /* let pflags stack */
+               if (fe->param.pflags_on) {
+                       ap->pflags |= fe->param.pflags_on;
+                       ata_port_notice(ap,
+                                       "FORCE: port flag 0x%x forced -> 0x%x\n",
+                                       fe->param.pflags_on, ap->pflags);
+               }
+       }
+}
+
 /**
  *     ata_force_link_limits - force link limits according to libata.force
  *     @link: ATA link of interest
@@ -486,6 +516,7 @@ static void ata_force_quirks(struct ata_device *dev)
        }
 }
 #else
+static inline void ata_force_pflags(struct ata_port *ap) { }
 static inline void ata_force_link_limits(struct ata_link *link) { }
 static inline void ata_force_xfermask(struct ata_device *dev) { }
 static inline void ata_force_quirks(struct ata_device *dev) { }
@@ -5460,6 +5491,8 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
 #endif
        ata_sff_port_init(ap);
 
+       ata_force_pflags(ap);
+
        return ap;
 }
 EXPORT_SYMBOL_GPL(ata_port_alloc);
@@ -6272,6 +6305,9 @@ EXPORT_SYMBOL_GPL(ata_platform_remove_one);
        { "no" #name,   .lflags_on      = (flags) },    \
        { #name,        .lflags_off     = (flags) }
 
+#define force_pflag_on(name, flags)                    \
+       { #name,        .pflags_on      = (flags) }
+
 #define force_quirk_on(name, flag)                     \
        { #name,        .quirk_on       = (flag) }
 
@@ -6331,6 +6367,8 @@ static const struct ata_force_param force_tbl[] __initconst = {
        force_lflag_on(rstonce,         ATA_LFLAG_RST_ONCE),
        force_lflag_onoff(dbdelay,      ATA_LFLAG_NO_DEBOUNCE_DELAY),
 
+       force_pflag_on(external,        ATA_PFLAG_EXTERNAL),
+
        force_quirk_onoff(ncq,          ATA_QUIRK_NONCQ),
        force_quirk_onoff(ncqtrim,      ATA_QUIRK_NO_NCQ_TRIM),
        force_quirk_onoff(ncqati,       ATA_QUIRK_NO_NCQ_ON_ATI),