diff -urNp -x '*.orig' linux-4.9/Documentation/scsi/link_power_management_policy.txt linux-4.9/Documentation/scsi/link_power_management_policy.txt --- linux-4.9/Documentation/scsi/link_power_management_policy.txt 2016-12-11 20:17:54.000000000 +0100 +++ linux-4.9/Documentation/scsi/link_power_management_policy.txt 2021-02-24 16:30:09.260516752 +0100 @@ -1,8 +1,11 @@ This parameter allows the user to set the link (interface) power management. -There are 3 possible options: +There are 4 possible options: Value Effect ---------------------------------------------------------------------------- +firmware_defaults Inherit configuration from the state programmed by + the firmware during system init. + min_power Tell the controller to try to make the link use the least possible power when possible. This may sacrifice some performance due to increased latency diff -urNp -x '*.orig' linux-4.9/drivers/ata/acard-ahci.c linux-4.9/drivers/ata/acard-ahci.c --- linux-4.9/drivers/ata/acard-ahci.c 2021-02-24 16:29:54.606714867 +0100 +++ linux-4.9/drivers/ata/acard-ahci.c 2021-02-24 16:30:09.257183312 +0100 @@ -480,6 +480,9 @@ static int acard_ahci_init_one(struct pc ata_port_pbar_desc(ap, AHCI_PCI_BAR, 0x100 + ap->port_no * 0x80, "port"); + rc = ahci_setup_port_privdata(ap); + if (rc) + return rc; /* set initial link pm policy */ /* ap->pm_policy = NOT_AVAILABLE; diff -urNp -x '*.orig' linux-4.9/drivers/ata/ahci.c linux-4.9/drivers/ata/ahci.c --- linux-4.9/drivers/ata/ahci.c 2021-02-24 16:29:54.606714867 +0100 +++ linux-4.9/drivers/ata/ahci.c 2021-02-24 16:30:09.257183312 +0100 @@ -1743,6 +1743,9 @@ static int ahci_init_one(struct pci_dev if (ap->flags & ATA_FLAG_EM) ap->em_message_type = hpriv->em_msg_type; + rc = ahci_setup_port_privdata(ap); + if (rc) + return rc; /* disabled/not-implemented port */ if (!(hpriv->port_map & (1 << i))) diff -urNp -x '*.orig' linux-4.9/drivers/ata/ahci.h linux-4.9/drivers/ata/ahci.h --- linux-4.9/drivers/ata/ahci.h 2016-12-11 20:17:54.000000000 +0100 +++ linux-4.9/drivers/ata/ahci.h 2021-02-24 16:30:09.257183312 +0100 @@ -322,6 +322,12 @@ struct ahci_port_priv { /* enclosure management info per PM slot */ struct ahci_em_priv em_priv[EM_MAX_SLOTS]; char *irq_desc; /* desc in /proc/interrupts */ + bool init_alpe; /* alpe enabled by default */ + bool init_asp; /* asp enabled by default */ + bool init_devslp; /* devslp enabled by default */ + u32 init_dito; /* initial dito configuration */ + u32 init_deto; /* initial deto configuration */ + u32 init_mdat; /* initial mdat configuration */ }; struct ahci_host_priv { @@ -387,6 +393,7 @@ extern struct ata_port_operations ahci_p extern struct ata_port_operations ahci_pmp_retry_srst_ops; unsigned int ahci_dev_classify(struct ata_port *ap); +int ahci_setup_port_privdata(struct ata_port *ap); void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, u32 opts); void ahci_save_initial_config(struct device *dev, diff -urNp -x '*.orig' linux-4.9/drivers/ata/libahci.c linux-4.9/drivers/ata/libahci.c --- linux-4.9/drivers/ata/libahci.c 2021-02-24 16:29:54.606714867 +0100 +++ linux-4.9/drivers/ata/libahci.c 2021-02-24 16:30:09.263850192 +0100 @@ -743,6 +743,7 @@ static int ahci_set_lpm(struct ata_link { struct ata_port *ap = link->ap; struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *ppriv = ap->private_data; struct ahci_port_priv *pp = ap->private_data; void __iomem *port_mmio = ahci_port_base(ap); @@ -763,10 +764,10 @@ static int ahci_set_lpm(struct ata_link if (hpriv->cap & HOST_CAP_ALPM) { u32 cmd = readl(port_mmio + PORT_CMD); + if (!(hints & ATA_LPM_WAKE_ONLY)) + cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) { - if (!(hints & ATA_LPM_WAKE_ONLY)) - cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); cmd |= PORT_CMD_ICC_ACTIVE; writel(cmd, port_mmio + PORT_CMD); @@ -777,6 +778,13 @@ static int ahci_set_lpm(struct ata_link if (hints & ATA_LPM_WAKE_ONLY) return 0; + } else if (policy == ATA_LPM_FIRMWARE_DEFAULTS) { + if (ppriv->init_alpe) + cmd |= PORT_CMD_ALPE; + if (ppriv->init_asp) + cmd |= PORT_CMD_ASP; + + writel(cmd, port_mmio + PORT_CMD); } else { cmd |= PORT_CMD_ALPE; if (policy == ATA_LPM_MIN_POWER) @@ -791,10 +799,18 @@ static int ahci_set_lpm(struct ata_link if ((hpriv->cap2 & HOST_CAP2_SDS) && (hpriv->cap2 & HOST_CAP2_SADM) && (link->device->flags & ATA_DFLAG_DEVSLP)) { - if (policy == ATA_LPM_MIN_POWER) + switch (policy) { + case ATA_LPM_MIN_POWER: ahci_set_aggressive_devslp(ap, true); - else + break; + case ATA_LPM_FIRMWARE_DEFAULTS: + case ATA_LPM_MED_POWER: + ahci_set_aggressive_devslp(ap, ppriv->init_devslp); + break; + default: ahci_set_aggressive_devslp(ap, false); + break; + } } if (policy == ATA_LPM_MAX_POWER) { @@ -2076,6 +2092,7 @@ static void ahci_post_internal_cmd(struc static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) { struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *ppriv = ap->private_data; void __iomem *port_mmio = ahci_port_base(ap); struct ata_device *dev = ap->link.device; u32 devslp, dm, dito, mdat, deto; @@ -2111,26 +2128,32 @@ static void ahci_set_aggressive_devslp(s if (rc) return; - dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET; - dito = devslp_idle_timeout / (dm + 1); - if (dito > 0x3ff) - dito = 0x3ff; - - /* Use the nominal value 10 ms if the read MDAT is zero, - * the nominal value of DETO is 20 ms. - */ - if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & - ATA_LOG_DEVSLP_VALID_MASK) { - mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & - ATA_LOG_DEVSLP_MDAT_MASK; - if (!mdat) + if (ppriv->init_devslp) { + dito = ppriv->init_dito; + deto = ppriv->init_deto; + mdat = ppriv->init_mdat; + } else { + dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET; + dito = devslp_idle_timeout / (dm + 1); + if (dito > 0x3ff) + dito = 0x3ff; + + /* Use the nominal value 10 ms if the read MDAT is zero, + * the nominal value of DETO is 20 ms. + */ + if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & + ATA_LOG_DEVSLP_VALID_MASK) { + mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & + ATA_LOG_DEVSLP_MDAT_MASK; + if (!mdat) + mdat = 10; + deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; + if (!deto) + deto = 20; + } else { mdat = 10; - deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; - if (!deto) deto = 20; - } else { - mdat = 10; - deto = 20; + } } /* Make dito, mdat, deto bits to 0s */ @@ -2298,19 +2321,53 @@ static int ahci_port_suspend(struct ata_ } #endif +/* + * Allocate port privdata and read back initial power management configuration + */ +int ahci_setup_port_privdata(struct ata_port *ap) +{ + struct ahci_port_priv *pp; + u32 cmd, devslp; + void __iomem *port_mmio = ahci_port_base(ap); + + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + + ap->private_data = pp; + + cmd = readl(port_mmio + PORT_CMD); + + if (cmd & PORT_CMD_ALPE) + pp->init_alpe = true; + + if (cmd & PORT_CMD_ASP) + pp->init_asp = true; + + devslp = readl(port_mmio + PORT_DEVSLP); + + /* devslp unsupported or disabled */ + if (!(devslp & PORT_DEVSLP_DSP) || !(devslp & PORT_DEVSLP_ADSE)) + return 0; + + pp->init_devslp = true; + pp->init_dito = (devslp >> PORT_DEVSLP_DITO_OFFSET) & 0x3ff; + pp->init_deto = (devslp >> PORT_DEVSLP_DETO_OFFSET) & 0xff; + pp->init_mdat = (devslp >> PORT_DEVSLP_MDAT_OFFSET) & 0x1f; + + return 0; +} +EXPORT_SYMBOL_GPL(ahci_setup_port_privdata); + static int ahci_port_start(struct ata_port *ap) { struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; struct device *dev = ap->host->dev; - struct ahci_port_priv *pp; void *mem; dma_addr_t mem_dma; size_t dma_sz, rx_fis_sz; - pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); - if (!pp) - return -ENOMEM; - if (ap->host->n_ports > 1) { pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL); if (!pp->irq_desc) { @@ -2389,8 +2446,6 @@ static int ahci_port_start(struct ata_po ap->lock = &pp->lock; } - ap->private_data = pp; - /* engage engines, captain */ return ahci_port_resume(ap); } diff -urNp -x '*.orig' linux-4.9/drivers/ata/libahci_platform.c linux-4.9/drivers/ata/libahci_platform.c --- linux-4.9/drivers/ata/libahci_platform.c 2021-02-24 16:29:54.606714867 +0100 +++ linux-4.9/drivers/ata/libahci_platform.c 2021-02-24 16:30:09.257183312 +0100 @@ -571,6 +571,10 @@ int ahci_platform_init_host(struct platf if (ap->flags & ATA_FLAG_EM) ap->em_message_type = hpriv->em_msg_type; + rc = ahci_setup_port_privdata(ap); + if (rc) + return rc; + /* disabled/not-implemented port */ if (!(hpriv->port_map & (1 << i))) ap->ops = &ata_dummy_port_ops; diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-core.c linux-4.9/drivers/ata/libata-core.c --- linux-4.9/drivers/ata/libata-core.c 2021-02-24 16:29:54.610048307 +0100 +++ linux-4.9/drivers/ata/libata-core.c 2021-02-24 16:30:09.263850192 +0100 @@ -2028,6 +2028,9 @@ retry: } } + if (id[79] & (1 << SATA_DIPM)) + dev->init_dipm = true; + *p_class = class; return 0; @@ -3835,6 +3838,11 @@ int sata_link_scr_lpm(struct ata_link *l return rc; switch (policy) { + case ATA_LPM_FIRMWARE_DEFAULTS: + /* use the values we read at probe */ + scontrol &= ~(0x7 << 8); + scontrol |= (link->init_lpm << 8); + break; case ATA_LPM_MAX_POWER: /* disable all LPM transitions */ scontrol |= (0x7 << 8); @@ -3845,10 +3853,6 @@ int sata_link_scr_lpm(struct ata_link *l } break; case ATA_LPM_MED_POWER: - /* allow LPM to PARTIAL */ - scontrol &= ~(0x1 << 8); - scontrol |= (0x6 << 8); - break; case ATA_LPM_MIN_POWER: if (ata_link_nr_enabled(link) > 0) /* no restrictions on LPM transitions */ @@ -5814,11 +5818,11 @@ void ata_link_init(struct ata_port *ap, } /** - * sata_link_init_spd - Initialize link->sata_spd_limit - * @link: Link to configure sata_spd_limit for + * sata_link_init_config - Initialize link->sata_spd_limit and init_lpm + * @link: Link to configure sata_spd_limit and init_lpm for * - * Initialize @link->[hw_]sata_spd_limit to the currently - * configured value. + * Initialize @link->[hw_]sata_spd_limit and @link->init_lpm to the + * currently configured value. * * LOCKING: * Kernel thread context (may sleep). @@ -5826,7 +5830,7 @@ void ata_link_init(struct ata_port *ap, * RETURNS: * 0 on success, -errno on failure. */ -int sata_link_init_spd(struct ata_link *link) +int sata_link_init_config(struct ata_link *link) { u8 spd; int rc; @@ -5843,6 +5847,8 @@ int sata_link_init_spd(struct ata_link * link->sata_spd_limit = link->hw_sata_spd_limit; + link->init_lpm = (link->saved_scontrol >> 8) & 0x7; + return 0; } @@ -6392,9 +6398,9 @@ int ata_host_register(struct ata_host *h ap->cbl = ATA_CBL_SATA; /* init sata_spd_limit to the current value */ - sata_link_init_spd(&ap->link); + sata_link_init_config(&ap->link); if (ap->slave_link) - sata_link_init_spd(ap->slave_link); + sata_link_init_config(ap->slave_link); /* print per-port info to dmesg */ xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-eh.c linux-4.9/drivers/ata/libata-eh.c --- linux-4.9/drivers/ata/libata-eh.c 2021-02-24 16:29:54.610048307 +0100 +++ linux-4.9/drivers/ata/libata-eh.c 2021-02-24 16:30:09.267183632 +0100 @@ -3534,9 +3534,7 @@ static int ata_eh_set_lpm(struct ata_lin return 0; /* - * DIPM is enabled only for MIN_POWER as some devices - * misbehave when the host NACKs transition to SLUMBER. Order - * device and link configurations such that the host always + * Order device and link configurations such that the host always * allows DIPM requests. */ ata_for_each_dev(dev, link, ENABLED) { @@ -3555,7 +3553,7 @@ static int ata_eh_set_lpm(struct ata_lin hints &= ~ATA_LPM_HIPM; /* disable DIPM before changing link config */ - if (policy != ATA_LPM_MIN_POWER && dipm) { + if (policy < ATA_LPM_MED_POWER && dipm) { err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_DISABLE, SATA_DIPM); if (err_mask && err_mask != AC_ERR_DEV) { @@ -3596,10 +3594,13 @@ static int ata_eh_set_lpm(struct ata_lin if (ap && ap->slave_link) ap->slave_link->lpm_policy = policy; - /* host config updated, enable DIPM if transitioning to MIN_POWER */ + /* host config updated, enable DIPM if transitioning to MED_POWER, + * MIN_POWER or FIRMWARE_DEFAULT when enabled by firmware + */ ata_for_each_dev(dev, link, ENABLED) { - if (policy == ATA_LPM_MIN_POWER && !no_dipm && - ata_id_has_dipm(dev->id)) { + if ((policy >= ATA_LPM_MED_POWER && !no_dipm && + ata_id_has_dipm(dev->id)) || + (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) { err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE, SATA_DIPM); if (err_mask && err_mask != AC_ERR_DEV) { diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-pmp.c linux-4.9/drivers/ata/libata-pmp.c --- linux-4.9/drivers/ata/libata-pmp.c 2021-02-24 16:29:54.610048307 +0100 +++ linux-4.9/drivers/ata/libata-pmp.c 2021-02-24 16:30:09.260516752 +0100 @@ -538,7 +538,7 @@ int sata_pmp_attach(struct ata_device *d ap->ops->pmp_attach(ap); ata_for_each_link(tlink, ap, EDGE) - sata_link_init_spd(tlink); + sata_link_init_config(tlink); return 0; diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-scsi.c linux-4.9/drivers/ata/libata-scsi.c --- linux-4.9/drivers/ata/libata-scsi.c 2021-02-24 16:29:54.610048307 +0100 +++ linux-4.9/drivers/ata/libata-scsi.c 2021-02-24 16:30:09.263850192 +0100 @@ -107,6 +107,7 @@ static const u8 def_control_mpage[CONTRO static const char *ata_lpm_policy_names[] = { [ATA_LPM_UNKNOWN] = "max_performance", [ATA_LPM_MAX_POWER] = "max_performance", + [ATA_LPM_FIRMWARE_DEFAULTS] = "firmware_defaults", [ATA_LPM_MED_POWER] = "medium_power", [ATA_LPM_MIN_POWER] = "min_power", }; diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata.h linux-4.9/drivers/ata/libata.h --- linux-4.9/drivers/ata/libata.h 2016-12-11 20:17:54.000000000 +0100 +++ linux-4.9/drivers/ata/libata.h 2021-02-24 16:30:09.260516752 +0100 @@ -99,7 +99,7 @@ extern bool ata_phys_link_online(struct extern bool ata_phys_link_offline(struct ata_link *link); extern void ata_dev_init(struct ata_device *dev); extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp); -extern int sata_link_init_spd(struct ata_link *link); +extern int sata_link_init_config(struct ata_link *link); extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); diff -urNp -x '*.orig' linux-4.9/drivers/ata/sata_highbank.c linux-4.9/drivers/ata/sata_highbank.c --- linux-4.9/drivers/ata/sata_highbank.c 2016-12-11 20:17:54.000000000 +0100 +++ linux-4.9/drivers/ata/sata_highbank.c 2021-02-24 16:30:09.260516752 +0100 @@ -557,6 +557,10 @@ static int ahci_highbank_probe(struct pl if (ap->flags & ATA_FLAG_EM) ap->em_message_type = hpriv->em_msg_type; + rc = ahci_setup_port_privdata(ap); + if (rc) + goto err0; + /* disabled/not-implemented port */ if (!(hpriv->port_map & (1 << i))) ap->ops = &ata_dummy_port_ops; diff -urNp -x '*.orig' linux-4.9/include/linux/libata.h linux-4.9/include/linux/libata.h --- linux-4.9/include/linux/libata.h 2021-02-24 16:29:56.030093715 +0100 +++ linux-4.9/include/linux/libata.h 2021-02-24 16:30:09.263850192 +0100 @@ -520,6 +520,7 @@ enum ata_completion_errors { enum ata_lpm_policy { ATA_LPM_UNKNOWN, ATA_LPM_MAX_POWER, + ATA_LPM_FIRMWARE_DEFAULTS, ATA_LPM_MED_POWER, ATA_LPM_MIN_POWER, }; @@ -740,6 +741,8 @@ struct ata_device { int spdn_cnt; /* ering is CLEAR_END, read comment above CLEAR_END */ struct ata_ering ering; + /* Initial DIPM configuration */ + bool init_dipm; }; /* Fields between ATA_DEVICE_CLEAR_BEGIN and ATA_DEVICE_CLEAR_END are @@ -811,6 +814,7 @@ struct ata_link { struct ata_device device[ATA_MAX_DEVICES]; unsigned long last_lpm_change; /* when last LPM change happened */ + u8 init_lpm; /* initial lpm configuration */ }; #define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag) #define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0])