mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 07:34:12 +08:00
libata: reimplement ACPI invocation
This patch reimplements ACPI invocation such that, instead of exporting ACPI details to the rest of libata, ACPI event handlers - ata_acpi_on_resume() and ata_acpi_on_devcfg() - are used. These two functions are responsible for determining whether specific ACPI method is used and when. On resume, _GTF is scheduled by setting ATA_DFLAG_ACPI_PENDING device flag. This is done this way to avoid performing the action on wrong device device (device swapping while suspended). On every ata_dev_configure(), ata_acpi_on_devcfg() is called, which performs _SDD and _GTF. _GTF is performed only after resuming and, if SATA, hardreset as the ACPI spec specifies. As _GTF may contain arbitrary commands, IDENTIFY page is re-read after _GTF taskfiles are executed. If one of ACPI methods fails, ata_acpi_on_devcfg() retries on the first failure. If it fails again on the second try, ACPI is disabled on the device. Note that successful configuration clears ACPI failed status. With all feature checks moved to the above two functions, do_drive_set_taskfiles() is trivial and thus collapsed into ata_acpi_exec_tfs(), which is now static and converted to return the number of executed taskfiles to be used by ata_acpi_on_resume(). As failures are handled properly, ata_acpi_push_id() now returns -errno on errors instead of unconditional zero. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
69b16a5f4c
commit
6746544c3b
@ -272,92 +272,48 @@ static int taskfile_load_raw(struct ata_device *dev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ata_dev_set_taskfiles - write the drive taskfile settings from _GTF
|
|
||||||
* @dev: target ATA device
|
|
||||||
* @gtf: pointer to array of _GTF taskfiles to execute
|
|
||||||
* @gtf_count: number of taskfiles
|
|
||||||
*
|
|
||||||
* This applies to both PATA and SATA drives.
|
|
||||||
*
|
|
||||||
* Execute taskfiles in @gtf.
|
|
||||||
*
|
|
||||||
* LOCKING:
|
|
||||||
* EH context.
|
|
||||||
*
|
|
||||||
* RETURNS:
|
|
||||||
* 0 on success, -errno on failure.
|
|
||||||
*/
|
|
||||||
static int ata_dev_set_taskfiles(struct ata_device *dev,
|
|
||||||
struct ata_acpi_gtf *gtf, int gtf_count)
|
|
||||||
{
|
|
||||||
struct ata_port *ap = dev->ap;
|
|
||||||
int ix;
|
|
||||||
|
|
||||||
if (ata_msg_probe(ap))
|
|
||||||
ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER: port#: %d\n",
|
|
||||||
__FUNCTION__, ap->port_no);
|
|
||||||
|
|
||||||
if (!(ap->flags & ATA_FLAG_ACPI_SATA))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!ata_dev_enabled(dev) || (ap->flags & ATA_FLAG_DISABLED))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
|
|
||||||
for (ix = 0; ix < gtf_count; ix++)
|
|
||||||
taskfile_load_raw(dev, gtf++);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ata_acpi_exec_tfs - get then write drive taskfile settings
|
* ata_acpi_exec_tfs - get then write drive taskfile settings
|
||||||
* @ap: the ata_port for the drive
|
* @dev: target ATA device
|
||||||
*
|
*
|
||||||
* This applies to both PATA and SATA drives.
|
* Evaluate _GTF and excute returned taskfiles.
|
||||||
*
|
*
|
||||||
* LOCKING:
|
* LOCKING:
|
||||||
* EH context.
|
* EH context.
|
||||||
*
|
*
|
||||||
* RETURNS:
|
* RETURNS:
|
||||||
* 0 on success, -errno on failure.
|
* Number of executed taskfiles on success, 0 if _GTF doesn't exist or
|
||||||
|
* doesn't contain valid data. -errno on other errors.
|
||||||
*/
|
*/
|
||||||
int ata_acpi_exec_tfs(struct ata_port *ap)
|
static int ata_acpi_exec_tfs(struct ata_device *dev)
|
||||||
{
|
{
|
||||||
int ix, ret = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TBD - implement PATA support. For now,
|
|
||||||
* we should not run GTF on PATA devices since some
|
|
||||||
* PATA require execution of GTM/STM before GTF.
|
|
||||||
*/
|
|
||||||
if (!(ap->flags & ATA_FLAG_ACPI_SATA))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (ix = 0; ix < ATA_MAX_DEVICES; ix++) {
|
|
||||||
struct ata_device *dev = &ap->device[ix];
|
|
||||||
struct ata_acpi_gtf *gtf = NULL;
|
struct ata_acpi_gtf *gtf = NULL;
|
||||||
int gtf_count;
|
|
||||||
void *ptr_to_free = NULL;
|
void *ptr_to_free = NULL;
|
||||||
|
int gtf_count, i, rc;
|
||||||
|
|
||||||
if (!ata_dev_enabled(dev))
|
/* get taskfiles */
|
||||||
continue;
|
rc = ata_dev_get_GTF(dev, >f, &ptr_to_free);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
gtf_count = rc;
|
||||||
|
|
||||||
ret = ata_dev_get_GTF(dev, >f, &ptr_to_free);
|
/* execute them */
|
||||||
if (ret == 0)
|
for (i = 0, rc = 0; i < gtf_count; i++) {
|
||||||
continue;
|
int tmp;
|
||||||
if (ret < 0)
|
|
||||||
break;
|
|
||||||
gtf_count = ret;
|
|
||||||
|
|
||||||
ret = ata_dev_set_taskfiles(dev, gtf, gtf_count);
|
/* ACPI errors are eventually ignored. Run till the
|
||||||
kfree(ptr_to_free);
|
* end even after errors.
|
||||||
if (ret < 0)
|
*/
|
||||||
break;
|
tmp = taskfile_load_raw(dev, gtf++);
|
||||||
|
if (!rc)
|
||||||
|
rc = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
kfree(ptr_to_free);
|
||||||
|
|
||||||
|
if (rc == 0)
|
||||||
|
return gtf_count;
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -376,7 +332,7 @@ int ata_acpi_exec_tfs(struct ata_port *ap)
|
|||||||
* RETURNS:
|
* RETURNS:
|
||||||
* 0 on success, -errno on failure.
|
* 0 on success, -errno on failure.
|
||||||
*/
|
*/
|
||||||
int ata_acpi_push_id(struct ata_device *dev)
|
static int ata_acpi_push_id(struct ata_device *dev)
|
||||||
{
|
{
|
||||||
struct ata_port *ap = dev->ap;
|
struct ata_port *ap = dev->ap;
|
||||||
int err;
|
int err;
|
||||||
@ -396,7 +352,7 @@ int ata_acpi_push_id(struct ata_device *dev)
|
|||||||
if (ata_msg_probe(ap))
|
if (ata_msg_probe(ap))
|
||||||
ata_dev_printk(dev, KERN_DEBUG,
|
ata_dev_printk(dev, KERN_DEBUG,
|
||||||
"%s: Not a SATA device\n", __FUNCTION__);
|
"%s: Not a SATA device\n", __FUNCTION__);
|
||||||
goto out;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Give the drive Identify data to the drive via the _SDD method */
|
/* Give the drive Identify data to the drive via the _SDD method */
|
||||||
@ -418,9 +374,99 @@ int ata_acpi_push_id(struct ata_device *dev)
|
|||||||
ata_dev_printk(dev, KERN_WARNING,
|
ata_dev_printk(dev, KERN_WARNING,
|
||||||
"ACPI _SDD failed (AE 0x%x)\n", status);
|
"ACPI _SDD failed (AE 0x%x)\n", status);
|
||||||
|
|
||||||
/* always return success */
|
return err;
|
||||||
out:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ata_acpi_on_resume - ATA ACPI hook called on resume
|
||||||
|
* @ap: target ATA port
|
||||||
|
*
|
||||||
|
* This function is called when @ap is resumed - right after port
|
||||||
|
* itself is resumed but before any EH action is taken.
|
||||||
|
*
|
||||||
|
* LOCKING:
|
||||||
|
* EH context.
|
||||||
|
*/
|
||||||
|
void ata_acpi_on_resume(struct ata_port *ap)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* schedule _GTF */
|
||||||
|
for (i = 0; i < ATA_MAX_DEVICES; i++)
|
||||||
|
ap->device[i].flags |= ATA_DFLAG_ACPI_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ata_acpi_on_devcfg - ATA ACPI hook called on device donfiguration
|
||||||
|
* @dev: target ATA device
|
||||||
|
*
|
||||||
|
* This function is called when @dev is about to be configured.
|
||||||
|
* IDENTIFY data might have been modified after this hook is run.
|
||||||
|
*
|
||||||
|
* LOCKING:
|
||||||
|
* EH context.
|
||||||
|
*
|
||||||
|
* RETURNS:
|
||||||
|
* Positive number if IDENTIFY data needs to be refreshed, 0 if not,
|
||||||
|
* -errno on failure.
|
||||||
|
*/
|
||||||
|
int ata_acpi_on_devcfg(struct ata_device *dev)
|
||||||
|
{
|
||||||
|
struct ata_port *ap = dev->ap;
|
||||||
|
struct ata_eh_context *ehc = &ap->eh_context;
|
||||||
|
int acpi_sata = ap->flags & ATA_FLAG_ACPI_SATA;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* XXX: _STM isn't implemented yet, skip if IDE for now */
|
||||||
|
if (!acpi_sata)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!dev->acpi_handle)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* do we need to do _GTF? */
|
||||||
|
if (!(dev->flags & ATA_DFLAG_ACPI_PENDING) &&
|
||||||
|
!(acpi_sata && (ehc->i.flags & ATA_EHI_DID_HARDRESET)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* do _SDD if SATA */
|
||||||
|
if (acpi_sata) {
|
||||||
|
rc = ata_acpi_push_id(dev);
|
||||||
|
if (rc)
|
||||||
|
goto acpi_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do _GTF */
|
||||||
|
rc = ata_acpi_exec_tfs(dev);
|
||||||
|
if (rc < 0)
|
||||||
|
goto acpi_err;
|
||||||
|
|
||||||
|
dev->flags &= ~ATA_DFLAG_ACPI_PENDING;
|
||||||
|
|
||||||
|
/* refresh IDENTIFY page if any _GTF command has been executed */
|
||||||
|
if (rc > 0) {
|
||||||
|
rc = ata_dev_reread_id(dev, 0);
|
||||||
|
if (rc < 0) {
|
||||||
|
ata_dev_printk(dev, KERN_ERR, "failed to IDENTIFY "
|
||||||
|
"after ACPI commands\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
acpi_err:
|
||||||
|
/* let EH retry on the first failure, disable ACPI on the second */
|
||||||
|
if (dev->flags & ATA_DFLAG_ACPI_FAILED) {
|
||||||
|
ata_dev_printk(dev, KERN_WARNING, "ACPI on devcfg failed the "
|
||||||
|
"second time, disabling (errno=%d)\n", rc);
|
||||||
|
|
||||||
|
dev->acpi_handle = NULL;
|
||||||
|
|
||||||
|
/* if port is working, request IDENTIFY reload and continue */
|
||||||
|
if (!(ap->pflags & ATA_PFLAG_FROZEN))
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
dev->flags |= ATA_DFLAG_ACPI_FAILED;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
@ -1845,7 +1845,8 @@ static void ata_dev_config_ncq(struct ata_device *dev,
|
|||||||
int ata_dev_configure(struct ata_device *dev)
|
int ata_dev_configure(struct ata_device *dev)
|
||||||
{
|
{
|
||||||
struct ata_port *ap = dev->ap;
|
struct ata_port *ap = dev->ap;
|
||||||
int print_info = ap->eh_context.i.flags & ATA_EHI_PRINTINFO;
|
struct ata_eh_context *ehc = &ap->eh_context;
|
||||||
|
int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
|
||||||
const u16 *id = dev->id;
|
const u16 *id = dev->id;
|
||||||
unsigned int xfer_mask;
|
unsigned int xfer_mask;
|
||||||
char revbuf[7]; /* XYZ-99\0 */
|
char revbuf[7]; /* XYZ-99\0 */
|
||||||
@ -1862,15 +1863,10 @@ int ata_dev_configure(struct ata_device *dev)
|
|||||||
if (ata_msg_probe(ap))
|
if (ata_msg_probe(ap))
|
||||||
ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__);
|
ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__);
|
||||||
|
|
||||||
/* set _SDD */
|
/* let ACPI work its magic */
|
||||||
rc = ata_acpi_push_id(dev);
|
rc = ata_acpi_on_devcfg(dev);
|
||||||
if (rc) {
|
if (rc)
|
||||||
ata_dev_printk(dev, KERN_WARNING, "failed to set _SDD(%d)\n",
|
return rc;
|
||||||
rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* retrieve and execute the ATA task file of _GTF */
|
|
||||||
ata_acpi_exec_tfs(ap);
|
|
||||||
|
|
||||||
/* print device capabilities */
|
/* print device capabilities */
|
||||||
if (ata_msg_probe(ap))
|
if (ata_msg_probe(ap))
|
||||||
|
@ -2207,6 +2207,9 @@ static void ata_eh_handle_port_resume(struct ata_port *ap)
|
|||||||
if (ap->ops->port_resume)
|
if (ap->ops->port_resume)
|
||||||
rc = ap->ops->port_resume(ap);
|
rc = ap->ops->port_resume(ap);
|
||||||
|
|
||||||
|
/* tell ACPI that we're resuming */
|
||||||
|
ata_acpi_on_resume(ap);
|
||||||
|
|
||||||
/* report result */
|
/* report result */
|
||||||
spin_lock_irqsave(ap->lock, flags);
|
spin_lock_irqsave(ap->lock, flags);
|
||||||
ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
|
ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
|
||||||
|
@ -99,18 +99,12 @@ extern struct ata_port *ata_port_alloc(struct ata_host *host);
|
|||||||
/* libata-acpi.c */
|
/* libata-acpi.c */
|
||||||
#ifdef CONFIG_ATA_ACPI
|
#ifdef CONFIG_ATA_ACPI
|
||||||
extern void ata_acpi_associate(struct ata_host *host);
|
extern void ata_acpi_associate(struct ata_host *host);
|
||||||
extern int ata_acpi_exec_tfs(struct ata_port *ap);
|
extern void ata_acpi_on_resume(struct ata_port *ap);
|
||||||
extern int ata_acpi_push_id(struct ata_device *dev);
|
extern int ata_acpi_on_devcfg(struct ata_device *adev);
|
||||||
#else
|
#else
|
||||||
static inline void ata_acpi_associate(struct ata_host *host) { }
|
static inline void ata_acpi_associate(struct ata_host *host) { }
|
||||||
static inline int ata_acpi_exec_tfs(struct ata_port *ap)
|
static inline void ata_acpi_on_resume(struct ata_port *ap) { }
|
||||||
{
|
static inline int ata_acpi_on_devcfg(struct ata_device *adev) { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline int ata_acpi_push_id(struct ata_device *dev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* libata-scsi.c */
|
/* libata-scsi.c */
|
||||||
|
@ -136,6 +136,8 @@ enum {
|
|||||||
ATA_DFLAG_CDB_INTR = (1 << 2), /* device asserts INTRQ when ready for CDB */
|
ATA_DFLAG_CDB_INTR = (1 << 2), /* device asserts INTRQ when ready for CDB */
|
||||||
ATA_DFLAG_NCQ = (1 << 3), /* device supports NCQ */
|
ATA_DFLAG_NCQ = (1 << 3), /* device supports NCQ */
|
||||||
ATA_DFLAG_FLUSH_EXT = (1 << 4), /* do FLUSH_EXT instead of FLUSH */
|
ATA_DFLAG_FLUSH_EXT = (1 << 4), /* do FLUSH_EXT instead of FLUSH */
|
||||||
|
ATA_DFLAG_ACPI_PENDING = (1 << 5), /* ACPI resume action pending */
|
||||||
|
ATA_DFLAG_ACPI_FAILED = (1 << 6), /* ACPI on devcfg has failed */
|
||||||
ATA_DFLAG_CFG_MASK = (1 << 8) - 1,
|
ATA_DFLAG_CFG_MASK = (1 << 8) - 1,
|
||||||
|
|
||||||
ATA_DFLAG_PIO = (1 << 8), /* device limited to PIO mode */
|
ATA_DFLAG_PIO = (1 << 8), /* device limited to PIO mode */
|
||||||
|
Loading…
Reference in New Issue
Block a user