Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 328642
b: refs/heads/master
c: 65fe1f0
h: refs/heads/master
v: v3
  • Loading branch information
Shane Huang authored and Jeff Garzik committed Sep 13, 2012
1 parent bbfa795 commit b859eb1
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 12 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 583661a89ed2e484bd295e7b4606099340478c38
refs/heads/master: 65fe1f0f66a57380229a4ced844188103135f37b
14 changes: 14 additions & 0 deletions trunk/drivers/ata/ahci.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ enum {
HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */
HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */
HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */
HOST_CAP2_SDS = (1 << 3), /* Support device sleep */
HOST_CAP2_SADM = (1 << 4), /* Support aggressive DevSlp */
HOST_CAP2_DESO = (1 << 5), /* DevSlp from slumber only */

/* registers for each SATA port */
PORT_LST_ADDR = 0x00, /* command list DMA addr */
Expand All @@ -133,6 +136,7 @@ enum {
PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */
PORT_SCR_NTF = 0x3c, /* SATA phy register: SNotification */
PORT_FBS = 0x40, /* FIS-based Switching */
PORT_DEVSLP = 0x44, /* device sleep */

/* PORT_IRQ_{STAT,MASK} bits */
PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */
Expand Down Expand Up @@ -186,6 +190,7 @@ enum {
PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */
PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */

/* PORT_FBS bits */
PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */
PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */
PORT_FBS_DEV_OFFSET = 8, /* FBS device to issue offset */
Expand All @@ -194,6 +199,15 @@ enum {
PORT_FBS_DEC = (1 << 1), /* FBS device error clear */
PORT_FBS_EN = (1 << 0), /* Enable FBS */

/* PORT_DEVSLP bits */
PORT_DEVSLP_DM_OFFSET = 25, /* DITO multiplier offset */
PORT_DEVSLP_DM_MASK = (0xf << 25), /* DITO multiplier mask */
PORT_DEVSLP_DITO_OFFSET = 15, /* DITO offset */
PORT_DEVSLP_MDAT_OFFSET = 10, /* Minimum assertion time */
PORT_DEVSLP_DETO_OFFSET = 2, /* DevSlp exit timeout */
PORT_DEVSLP_DSP = (1 << 1), /* DevSlp present */
PORT_DEVSLP_ADSE = (1 << 0), /* Aggressive DevSlp enable */

/* hpriv->flags bits */

#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
Expand Down
97 changes: 96 additions & 1 deletion trunk/drivers/ata/libahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include "ahci.h"
#include "libata.h"

static int ahci_skip_host_reset;
int ahci_ignore_sss;
Expand Down Expand Up @@ -76,6 +77,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc);
static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc);
static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep);
static void ahci_enable_fbs(struct ata_port *ap);
static void ahci_disable_fbs(struct ata_port *ap);
static void ahci_pmp_attach(struct ata_port *ap);
Expand Down Expand Up @@ -193,6 +195,10 @@ module_param(ahci_em_messages, int, 0444);
MODULE_PARM_DESC(ahci_em_messages,
"AHCI Enclosure Management Message control (0 = off, 1 = on)");

int devslp_idle_timeout = 1000; /* device sleep idle timeout in ms */
module_param(devslp_idle_timeout, int, 0644);
MODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout");

static void ahci_enable_ahci(void __iomem *mmio)
{
int i;
Expand Down Expand Up @@ -702,6 +708,16 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
}
}

/* set aggressive device sleep */
if ((hpriv->cap2 & HOST_CAP2_SDS) &&
(hpriv->cap2 & HOST_CAP2_SADM) &&
(link->device->flags & ATA_DFLAG_DEVSLP)) {
if (policy == ATA_LPM_MIN_POWER)
ahci_set_aggressive_devslp(ap, true);
else
ahci_set_aggressive_devslp(ap, false);
}

if (policy == ATA_LPM_MAX_POWER) {
sata_link_scr_lpm(link, policy, false);

Expand Down Expand Up @@ -1890,6 +1906,81 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
ahci_kick_engine(ap);
}

static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ata_device *dev = ap->link.device;
u32 devslp, dm, dito, mdat, deto;
int rc;
unsigned int err_mask;

devslp = readl(port_mmio + PORT_DEVSLP);
if (!(devslp & PORT_DEVSLP_DSP)) {
dev_err(ap->host->dev, "port does not support device sleep\n");
return;
}

/* disable device sleep */
if (!sleep) {
if (devslp & PORT_DEVSLP_ADSE) {
writel(devslp & ~PORT_DEVSLP_ADSE,
port_mmio + PORT_DEVSLP);
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_DISABLE,
SATA_DEVSLP);
if (err_mask && err_mask != AC_ERR_DEV)
ata_dev_warn(dev, "failed to disable DEVSLP\n");
}
return;
}

/* device sleep was already enabled */
if (devslp & PORT_DEVSLP_ADSE)
return;

/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
rc = ahci_stop_engine(ap);
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->sata_settings[ATA_LOG_DEVSLP_VALID] &
ATA_LOG_DEVSLP_VALID_MASK) {
mdat = dev->sata_settings[ATA_LOG_DEVSLP_MDAT] &
ATA_LOG_DEVSLP_MDAT_MASK;
if (!mdat)
mdat = 10;
deto = dev->sata_settings[ATA_LOG_DEVSLP_DETO];
if (!deto)
deto = 20;
} else {
mdat = 10;
deto = 20;
}

devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
(mdat << PORT_DEVSLP_MDAT_OFFSET) |
(deto << PORT_DEVSLP_DETO_OFFSET) |
PORT_DEVSLP_ADSE);
writel(devslp, port_mmio + PORT_DEVSLP);

ahci_start_engine(ap);

/* enable device sleep feature for the drive */
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE,
SATA_DEVSLP);
if (err_mask && err_mask != AC_ERR_DEV)
ata_dev_warn(dev, "failed to enable DEVSLP\n");
}

static void ahci_enable_fbs(struct ata_port *ap)
{
struct ahci_port_priv *pp = ap->private_data;
Expand Down Expand Up @@ -2164,7 +2255,8 @@ void ahci_print_info(struct ata_host *host, const char *scc_s)
"flags: "
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s\n"
"%s%s%s%s%s%s%s"
"%s%s\n"
,

cap & HOST_CAP_64 ? "64bit " : "",
Expand All @@ -2184,6 +2276,9 @@ void ahci_print_info(struct ata_host *host, const char *scc_s)
cap & HOST_CAP_CCC ? "ccc " : "",
cap & HOST_CAP_EMS ? "ems " : "",
cap & HOST_CAP_SXS ? "sxs " : "",
cap2 & HOST_CAP2_DESO ? "deso " : "",
cap2 & HOST_CAP2_SADM ? "sadm " : "",
cap2 & HOST_CAP2_SDS ? "sds " : "",
cap2 & HOST_CAP2_APST ? "apst " : "",
cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "",
cap2 & HOST_CAP2_BOH ? "boh " : ""
Expand Down
29 changes: 24 additions & 5 deletions trunk/drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2155,6 +2155,7 @@ int ata_dev_configure(struct ata_device *dev)
int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
const u16 *id = dev->id;
unsigned long xfer_mask;
unsigned int err_mask;
char revbuf[7]; /* XYZ-99\0 */
char fwrevbuf[ATA_ID_FW_REV_LEN+1];
char modelbuf[ATA_ID_PROD_LEN+1];
Expand Down Expand Up @@ -2323,6 +2324,26 @@ int ata_dev_configure(struct ata_device *dev)
}
}

/* check and mark DevSlp capability */
if (ata_id_has_devslp(dev->id))
dev->flags |= ATA_DFLAG_DEVSLP;

/* Obtain SATA Settings page from Identify Device Data Log,
* which contains DevSlp timing variables etc.
* Exclude old devices with ata_id_has_ncq()
*/
if (ata_id_has_ncq(dev->id)) {
err_mask = ata_read_log_page(dev,
ATA_LOG_SATA_ID_DEV_DATA,
ATA_LOG_SATA_SETTINGS,
dev->sata_settings,
1);
if (err_mask)
ata_dev_dbg(dev,
"failed to get Identify Device Data, Emask 0x%x\n",
err_mask);
}

dev->cdb_len = 16;
}

Expand Down Expand Up @@ -2351,8 +2372,6 @@ int ata_dev_configure(struct ata_device *dev)
(ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) &&
(!sata_pmp_attached(ap) ||
sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf) == 0)) {
unsigned int err_mask;

/* issue SET feature command to turn this on */
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE, SATA_AN);
Expand Down Expand Up @@ -3598,7 +3617,7 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
switch (policy) {
case ATA_LPM_MAX_POWER:
/* disable all LPM transitions */
scontrol |= (0x3 << 8);
scontrol |= (0x7 << 8);
/* initiate transition to active state */
if (spm_wakeup) {
scontrol |= (0x4 << 12);
Expand All @@ -3608,12 +3627,12 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
case ATA_LPM_MED_POWER:
/* allow LPM to PARTIAL */
scontrol &= ~(0x1 << 8);
scontrol |= (0x2 << 8);
scontrol |= (0x6 << 8);
break;
case ATA_LPM_MIN_POWER:
if (ata_link_nr_enabled(link) > 0)
/* no restrictions on LPM transitions */
scontrol &= ~(0x3 << 8);
scontrol &= ~(0x7 << 8);
else {
/* empty port, power off */
scontrol &= ~0xf;
Expand Down
12 changes: 7 additions & 5 deletions trunk/drivers/ata/libata-eh.c
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,7 @@ static const char *ata_err_string(unsigned int err_mask)
/**
* ata_read_log_page - read a specific log page
* @dev: target device
* @log: log to read
* @page: page to read
* @buf: buffer to store read page
* @sectors: number of sectors to read
Expand All @@ -1499,17 +1500,18 @@ static const char *ata_err_string(unsigned int err_mask)
* RETURNS:
* 0 on success, AC_ERR_* mask otherwise.
*/
static unsigned int ata_read_log_page(struct ata_device *dev,
u8 page, void *buf, unsigned int sectors)
unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
u8 page, void *buf, unsigned int sectors)
{
struct ata_taskfile tf;
unsigned int err_mask;

DPRINTK("read log page - page %d\n", page);
DPRINTK("read log page - log 0x%x, page 0x%x\n", log, page);

ata_tf_init(dev, &tf);
tf.command = ATA_CMD_READ_LOG_EXT;
tf.lbal = page;
tf.lbal = log;
tf.lbam = page;
tf.nsect = sectors;
tf.hob_nsect = sectors >> 8;
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_LBA48 | ATA_TFLAG_DEVICE;
Expand Down Expand Up @@ -1545,7 +1547,7 @@ static int ata_eh_read_log_10h(struct ata_device *dev,
u8 csum;
int i;

err_mask = ata_read_log_page(dev, ATA_LOG_SATA_NCQ, buf, 1);
err_mask = ata_read_log_page(dev, ATA_LOG_SATA_NCQ, 0, buf, 1);
if (err_mask)
return -EIO;

Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/ata/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ extern void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
unsigned int action);
extern void ata_eh_done(struct ata_link *link, struct ata_device *dev,
unsigned int action);
extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
u8 page, void *buf, unsigned int sectors);
extern void ata_eh_autopsy(struct ata_port *ap);
const char *ata_get_cmd_descript(u8 command);
extern void ata_eh_report(struct ata_port *ap);
Expand Down
9 changes: 9 additions & 0 deletions trunk/include/linux/ata.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ enum {

/* READ_LOG_EXT pages */
ATA_LOG_SATA_NCQ = 0x10,
ATA_LOG_SATA_ID_DEV_DATA = 0x30,
ATA_LOG_SATA_SETTINGS = 0x08,
ATA_LOG_DEVSLP_MDAT = 0x30,
ATA_LOG_DEVSLP_MDAT_MASK = 0x1F,
ATA_LOG_DEVSLP_DETO = 0x31,
ATA_LOG_DEVSLP_VALID = 0x37,
ATA_LOG_DEVSLP_VALID_MASK = 0x80,

/* READ/WRITE LONG (obsolete) */
ATA_CMD_READ_LONG = 0x22,
Expand Down Expand Up @@ -348,6 +355,7 @@ enum {
SATA_FPDMA_IN_ORDER = 0x04, /* FPDMA in-order data delivery */
SATA_AN = 0x05, /* Asynchronous Notification */
SATA_SSP = 0x06, /* Software Settings Preservation */
SATA_DEVSLP = 0x09, /* Device Sleep */

/* feature values for SET_MAX */
ATA_SET_MAX_ADDR = 0x00,
Expand Down Expand Up @@ -584,6 +592,7 @@ static inline int ata_is_data(u8 prot)

#define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20)
#define ata_id_has_da(id) ((id)[ATA_ID_SATA_CAPABILITY_2] & (1 << 4))
#define ata_id_has_devslp(id) ((id)[ATA_ID_FEATURE_SUPP] & (1 << 8))

static inline bool ata_id_has_hipm(const u16 *id)
{
Expand Down
4 changes: 4 additions & 0 deletions trunk/include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ enum {
ATA_DFLAG_DETACHED = (1 << 25),

ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */
ATA_DFLAG_DEVSLP = (1 << 27), /* device supports Device Sleep */

ATA_DEV_UNKNOWN = 0, /* unknown device */
ATA_DEV_ATA = 1, /* ATA device */
Expand Down Expand Up @@ -649,6 +650,9 @@ struct ata_device {
u32 gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
};

/* Identify Device Data Log (30h), SATA Settings (page 08h) */
u8 sata_settings[ATA_SECT_SIZE];

/* error history */
int spdn_cnt;
/* ering is CLEAR_END, read comment above CLEAR_END */
Expand Down

0 comments on commit b859eb1

Please sign in to comment.