Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b847548 authored by Jens Axboe's avatar Jens Axboe Committed by Linus Torvalds
Browse files

[PATCH] Suspend support for libata



This patch adds suspend patch to libata, and ata_piix in particular. For
most low level drivers, they should just need to add the 4 hooks to
work. As I can only test ata_piix, I didn't enable it for more
though.

Suspend support is the single most important feature on a notebook, and
most new notebooks have sata drives. It's quite embarrassing that we
_still_ do not support this. Right now, it's perfectly possible to
suspend the drive in mid-transfer.

Signed-off-by: default avatarJens Axboe <axboe@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 88202a0c
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -166,6 +166,8 @@ static struct pci_driver piix_pci_driver = {
	.id_table		= piix_pci_tbl,
	.id_table		= piix_pci_tbl,
	.probe			= piix_init_one,
	.probe			= piix_init_one,
	.remove			= ata_pci_remove_one,
	.remove			= ata_pci_remove_one,
	.suspend		= ata_pci_device_suspend,
	.resume			= ata_pci_device_resume,
};
};


static struct scsi_host_template piix_sht = {
static struct scsi_host_template piix_sht = {
@@ -186,6 +188,8 @@ static struct scsi_host_template piix_sht = {
	.slave_configure	= ata_scsi_slave_config,
	.slave_configure	= ata_scsi_slave_config,
	.bios_param		= ata_std_bios_param,
	.bios_param		= ata_std_bios_param,
	.ordered_flush		= 1,
	.ordered_flush		= 1,
	.resume			= ata_scsi_device_resume,
	.suspend		= ata_scsi_device_suspend,
};
};


static const struct ata_port_operations piix_pata_ops = {
static const struct ata_port_operations piix_pata_ops = {
+114 −0
Original line number Original line Diff line number Diff line
@@ -4154,6 +4154,96 @@ err_out:
 *	Inherited from caller.
 *	Inherited from caller.
 */
 */


/*
 * Execute a 'simple' command, that only consists of the opcode 'cmd' itself,
 * without filling any other registers
 */
static int ata_do_simple_cmd(struct ata_port *ap, struct ata_device *dev,
			     u8 cmd)
{
	struct ata_taskfile tf;
	int err;

	ata_tf_init(ap, &tf, dev->devno);

	tf.command = cmd;
	tf.flags |= ATA_TFLAG_DEVICE;
	tf.protocol = ATA_PROT_NODATA;

	err = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0);
	if (err)
		printk(KERN_ERR "%s: ata command failed: %d\n",
				__FUNCTION__, err);

	return err;
}

static int ata_flush_cache(struct ata_port *ap, struct ata_device *dev)
{
	u8 cmd;

	if (!ata_try_flush_cache(dev))
		return 0;

	if (ata_id_has_flush_ext(dev->id))
		cmd = ATA_CMD_FLUSH_EXT;
	else
		cmd = ATA_CMD_FLUSH;

	return ata_do_simple_cmd(ap, dev, cmd);
}

static int ata_standby_drive(struct ata_port *ap, struct ata_device *dev)
{
	return ata_do_simple_cmd(ap, dev, ATA_CMD_STANDBYNOW1);
}

static int ata_start_drive(struct ata_port *ap, struct ata_device *dev)
{
	return ata_do_simple_cmd(ap, dev, ATA_CMD_IDLEIMMEDIATE);
}

/**
 *	ata_device_resume - wakeup a previously suspended devices
 *
 *	Kick the drive back into action, by sending it an idle immediate
 *	command and making sure its transfer mode matches between drive
 *	and host.
 *
 */
int ata_device_resume(struct ata_port *ap, struct ata_device *dev)
{
	if (ap->flags & ATA_FLAG_SUSPENDED) {
		ap->flags &= ~ATA_FLAG_SUSPENDED;
		ata_set_mode(ap);
	}
	if (!ata_dev_present(dev))
		return 0;
	if (dev->class == ATA_DEV_ATA)
		ata_start_drive(ap, dev);

	return 0;
}

/**
 *	ata_device_suspend - prepare a device for suspend
 *
 *	Flush the cache on the drive, if appropriate, then issue a
 *	standbynow command.
 *
 */
int ata_device_suspend(struct ata_port *ap, struct ata_device *dev)
{
	if (!ata_dev_present(dev))
		return 0;
	if (dev->class == ATA_DEV_ATA)
		ata_flush_cache(ap, dev);

	ata_standby_drive(ap, dev);
	ap->flags |= ATA_FLAG_SUSPENDED;
	return 0;
}

int ata_port_start (struct ata_port *ap)
int ata_port_start (struct ata_port *ap)
{
{
	struct device *dev = ap->host_set->dev;
	struct device *dev = ap->host_set->dev;
@@ -4902,6 +4992,23 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)


	return (tmp == bits->val) ? 1 : 0;
	return (tmp == bits->val) ? 1 : 0;
}
}

int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
{
	pci_save_state(pdev);
	pci_disable_device(pdev);
	pci_set_power_state(pdev, PCI_D3hot);
	return 0;
}

int ata_pci_device_resume(struct pci_dev *pdev)
{
	pci_set_power_state(pdev, PCI_D0);
	pci_restore_state(pdev);
	pci_enable_device(pdev);
	pci_set_master(pdev);
	return 0;
}
#endif /* CONFIG_PCI */
#endif /* CONFIG_PCI */




@@ -5005,4 +5112,11 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop);
EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
EXPORT_SYMBOL_GPL(ata_pci_device_resume);
#endif /* CONFIG_PCI */
#endif /* CONFIG_PCI */

EXPORT_SYMBOL_GPL(ata_device_suspend);
EXPORT_SYMBOL_GPL(ata_device_resume);
EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
+16 −0
Original line number Original line Diff line number Diff line
@@ -396,6 +396,22 @@ void ata_dump_status(unsigned id, struct ata_taskfile *tf)
	}
	}
}
}


int ata_scsi_device_resume(struct scsi_device *sdev)
{
	struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
	struct ata_device *dev = &ap->device[sdev->id];

	return ata_device_resume(ap, dev);
}

int ata_scsi_device_suspend(struct scsi_device *sdev)
{
	struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
	struct ata_device *dev = &ap->device[sdev->id];

	return ata_device_suspend(ap, dev);
}

/**
/**
 *	ata_to_sense_error - convert ATA error to SCSI error
 *	ata_to_sense_error - convert ATA error to SCSI error
 *	@id: ATA device number
 *	@id: ATA device number
+31 −0
Original line number Original line Diff line number Diff line
@@ -263,9 +263,40 @@ static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
	return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
	return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
}
}


static int scsi_bus_suspend(struct device * dev, pm_message_t state)
{
	struct scsi_device *sdev = to_scsi_device(dev);
	struct scsi_host_template *sht = sdev->host->hostt;
	int err;

	err = scsi_device_quiesce(sdev);
	if (err)
		return err;

	if (sht->suspend)
		err = sht->suspend(sdev);

	return err;
}

static int scsi_bus_resume(struct device * dev)
{
	struct scsi_device *sdev = to_scsi_device(dev);
	struct scsi_host_template *sht = sdev->host->hostt;
	int err = 0;

	if (sht->resume)
		err = sht->resume(sdev);

	scsi_device_resume(sdev);
	return err;
}

struct bus_type scsi_bus_type = {
struct bus_type scsi_bus_type = {
        .name		= "scsi",
        .name		= "scsi",
        .match		= scsi_bus_match,
        .match		= scsi_bus_match,
	.suspend	= scsi_bus_suspend,
	.resume		= scsi_bus_resume,
};
};


int scsi_sysfs_register(void)
int scsi_sysfs_register(void)
+2 −0
Original line number Original line Diff line number Diff line
@@ -141,6 +141,8 @@ enum {
	ATA_CMD_PACKET		= 0xA0,
	ATA_CMD_PACKET		= 0xA0,
	ATA_CMD_VERIFY		= 0x40,
	ATA_CMD_VERIFY		= 0x40,
	ATA_CMD_VERIFY_EXT	= 0x42,
	ATA_CMD_VERIFY_EXT	= 0x42,
 	ATA_CMD_STANDBYNOW1	= 0xE0,
 	ATA_CMD_IDLEIMMEDIATE	= 0xE1,
	ATA_CMD_INIT_DEV_PARAMS	= 0x91,
	ATA_CMD_INIT_DEV_PARAMS	= 0x91,


	/* SETFEATURES stuff */
	/* SETFEATURES stuff */
Loading