diff options
| author | Jens Axboe <axboe@suse.de> | 2006-01-06 03:28:07 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-06 11:36:09 -0500 |
| commit | 9b847548663ef1039dd49f0eb4463d001e596bc3 (patch) | |
| tree | 105a0eece522b1347bea57f609f6c04ed673fdb3 | |
| parent | 88202a0c84e1951d6630d1d557d4801a8cc5b5ef (diff) | |
[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: Jens Axboe <axboe@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -rw-r--r-- | drivers/scsi/ata_piix.c | 4 | ||||
| -rw-r--r-- | drivers/scsi/libata-core.c | 114 | ||||
| -rw-r--r-- | drivers/scsi/libata-scsi.c | 16 | ||||
| -rw-r--r-- | drivers/scsi/scsi_sysfs.c | 31 | ||||
| -rw-r--r-- | include/linux/ata.h | 2 | ||||
| -rw-r--r-- | include/linux/libata.h | 8 | ||||
| -rw-r--r-- | include/scsi/scsi_host.h | 6 |
7 files changed, 181 insertions, 0 deletions
diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index 0ea27873b9ff..f79630340028 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c | |||
| @@ -166,6 +166,8 @@ static struct pci_driver piix_pci_driver = { | |||
| 166 | .id_table = piix_pci_tbl, | 166 | .id_table = piix_pci_tbl, |
| 167 | .probe = piix_init_one, | 167 | .probe = piix_init_one, |
| 168 | .remove = ata_pci_remove_one, | 168 | .remove = ata_pci_remove_one, |
| 169 | .suspend = ata_pci_device_suspend, | ||
| 170 | .resume = ata_pci_device_resume, | ||
| 169 | }; | 171 | }; |
| 170 | 172 | ||
| 171 | static struct scsi_host_template piix_sht = { | 173 | static struct scsi_host_template piix_sht = { |
| @@ -186,6 +188,8 @@ static struct scsi_host_template piix_sht = { | |||
| 186 | .slave_configure = ata_scsi_slave_config, | 188 | .slave_configure = ata_scsi_slave_config, |
| 187 | .bios_param = ata_std_bios_param, | 189 | .bios_param = ata_std_bios_param, |
| 188 | .ordered_flush = 1, | 190 | .ordered_flush = 1, |
| 191 | .resume = ata_scsi_device_resume, | ||
| 192 | .suspend = ata_scsi_device_suspend, | ||
| 189 | }; | 193 | }; |
| 190 | 194 | ||
| 191 | static const struct ata_port_operations piix_pata_ops = { | 195 | static const struct ata_port_operations piix_pata_ops = { |
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 9ea102587914..9c66d4059399 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c | |||
| @@ -4154,6 +4154,96 @@ err_out: | |||
| 4154 | * Inherited from caller. | 4154 | * Inherited from caller. |
| 4155 | */ | 4155 | */ |
| 4156 | 4156 | ||
| 4157 | /* | ||
| 4158 | * Execute a 'simple' command, that only consists of the opcode 'cmd' itself, | ||
| 4159 | * without filling any other registers | ||
| 4160 | */ | ||
| 4161 | static int ata_do_simple_cmd(struct ata_port *ap, struct ata_device *dev, | ||
| 4162 | u8 cmd) | ||
| 4163 | { | ||
| 4164 | struct ata_taskfile tf; | ||
| 4165 | int err; | ||
| 4166 | |||
| 4167 | ata_tf_init(ap, &tf, dev->devno); | ||
| 4168 | |||
| 4169 | tf.command = cmd; | ||
| 4170 | tf.flags |= ATA_TFLAG_DEVICE; | ||
| 4171 | tf.protocol = ATA_PROT_NODATA; | ||
| 4172 | |||
| 4173 | err = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0); | ||
| 4174 | if (err) | ||
| 4175 | printk(KERN_ERR "%s: ata command failed: %d\n", | ||
| 4176 | __FUNCTION__, err); | ||
| 4177 | |||
| 4178 | return err; | ||
| 4179 | } | ||
| 4180 | |||
| 4181 | static int ata_flush_cache(struct ata_port *ap, struct ata_device *dev) | ||
| 4182 | { | ||
| 4183 | u8 cmd; | ||
| 4184 | |||
| 4185 | if (!ata_try_flush_cache(dev)) | ||
| 4186 | return 0; | ||
| 4187 | |||
| 4188 | if (ata_id_has_flush_ext(dev->id)) | ||
| 4189 | cmd = ATA_CMD_FLUSH_EXT; | ||
| 4190 | else | ||
| 4191 | cmd = ATA_CMD_FLUSH; | ||
| 4192 | |||
| 4193 | return ata_do_simple_cmd(ap, dev, cmd); | ||
| 4194 | } | ||
| 4195 | |||
| 4196 | static int ata_standby_drive(struct ata_port *ap, struct ata_device *dev) | ||
| 4197 | { | ||
| 4198 | return ata_do_simple_cmd(ap, dev, ATA_CMD_STANDBYNOW1); | ||
| 4199 | } | ||
| 4200 | |||
| 4201 | static int ata_start_drive(struct ata_port *ap, struct ata_device *dev) | ||
| 4202 | { | ||
| 4203 | return ata_do_simple_cmd(ap, dev, ATA_CMD_IDLEIMMEDIATE); | ||
| 4204 | } | ||
| 4205 | |||
| 4206 | /** | ||
| 4207 | * ata_device_resume - wakeup a previously suspended devices | ||
| 4208 | * | ||
| 4209 | * Kick the drive back into action, by sending it an idle immediate | ||
| 4210 | * command and making sure its transfer mode matches between drive | ||
| 4211 | * and host. | ||
| 4212 | * | ||
| 4213 | */ | ||
| 4214 | int ata_device_resume(struct ata_port *ap, struct ata_device *dev) | ||
| 4215 | { | ||
| 4216 | if (ap->flags & ATA_FLAG_SUSPENDED) { | ||
| 4217 | ap->flags &= ~ATA_FLAG_SUSPENDED; | ||
| 4218 | ata_set_mode(ap); | ||
| 4219 | } | ||
| 4220 | if (!ata_dev_present(dev)) | ||
| 4221 | return 0; | ||
| 4222 | if (dev->class == ATA_DEV_ATA) | ||
| 4223 | ata_start_drive(ap, dev); | ||
| 4224 | |||
| 4225 | return 0; | ||
| 4226 | } | ||
| 4227 | |||
| 4228 | /** | ||
| 4229 | * ata_device_suspend - prepare a device for suspend | ||
| 4230 | * | ||
| 4231 | * Flush the cache on the drive, if appropriate, then issue a | ||
| 4232 | * standbynow command. | ||
| 4233 | * | ||
| 4234 | */ | ||
| 4235 | int ata_device_suspend(struct ata_port *ap, struct ata_device *dev) | ||
| 4236 | { | ||
| 4237 | if (!ata_dev_present(dev)) | ||
| 4238 | return 0; | ||
| 4239 | if (dev->class == ATA_DEV_ATA) | ||
| 4240 | ata_flush_cache(ap, dev); | ||
| 4241 | |||
| 4242 | ata_standby_drive(ap, dev); | ||
| 4243 | ap->flags |= ATA_FLAG_SUSPENDED; | ||
| 4244 | return 0; | ||
| 4245 | } | ||
| 4246 | |||
| 4157 | int ata_port_start (struct ata_port *ap) | 4247 | int ata_port_start (struct ata_port *ap) |
| 4158 | { | 4248 | { |
| 4159 | struct device *dev = ap->host_set->dev; | 4249 | 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) | |||
| 4902 | 4992 | ||
| 4903 | return (tmp == bits->val) ? 1 : 0; | 4993 | return (tmp == bits->val) ? 1 : 0; |
| 4904 | } | 4994 | } |
| 4995 | |||
| 4996 | int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state) | ||
| 4997 | { | ||
| 4998 | pci_save_state(pdev); | ||
| 4999 | pci_disable_device(pdev); | ||
| 5000 | pci_set_power_state(pdev, PCI_D3hot); | ||
| 5001 | return 0; | ||
| 5002 | } | ||
| 5003 | |||
| 5004 | int ata_pci_device_resume(struct pci_dev *pdev) | ||
| 5005 | { | ||
| 5006 | pci_set_power_state(pdev, PCI_D0); | ||
| 5007 | pci_restore_state(pdev); | ||
| 5008 | pci_enable_device(pdev); | ||
| 5009 | pci_set_master(pdev); | ||
| 5010 | return 0; | ||
| 5011 | } | ||
| 4905 | #endif /* CONFIG_PCI */ | 5012 | #endif /* CONFIG_PCI */ |
| 4906 | 5013 | ||
| 4907 | 5014 | ||
| @@ -5005,4 +5112,11 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop); | |||
| 5005 | EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); | 5112 | EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); |
| 5006 | EXPORT_SYMBOL_GPL(ata_pci_init_one); | 5113 | EXPORT_SYMBOL_GPL(ata_pci_init_one); |
| 5007 | EXPORT_SYMBOL_GPL(ata_pci_remove_one); | 5114 | EXPORT_SYMBOL_GPL(ata_pci_remove_one); |
| 5115 | EXPORT_SYMBOL_GPL(ata_pci_device_suspend); | ||
| 5116 | EXPORT_SYMBOL_GPL(ata_pci_device_resume); | ||
| 5008 | #endif /* CONFIG_PCI */ | 5117 | #endif /* CONFIG_PCI */ |
| 5118 | |||
| 5119 | EXPORT_SYMBOL_GPL(ata_device_suspend); | ||
| 5120 | EXPORT_SYMBOL_GPL(ata_device_resume); | ||
| 5121 | EXPORT_SYMBOL_GPL(ata_scsi_device_suspend); | ||
| 5122 | EXPORT_SYMBOL_GPL(ata_scsi_device_resume); | ||
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index e0439be4b573..c1ebede14a48 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c | |||
| @@ -396,6 +396,22 @@ void ata_dump_status(unsigned id, struct ata_taskfile *tf) | |||
| 396 | } | 396 | } |
| 397 | } | 397 | } |
| 398 | 398 | ||
| 399 | int ata_scsi_device_resume(struct scsi_device *sdev) | ||
| 400 | { | ||
| 401 | struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0]; | ||
| 402 | struct ata_device *dev = &ap->device[sdev->id]; | ||
| 403 | |||
| 404 | return ata_device_resume(ap, dev); | ||
| 405 | } | ||
| 406 | |||
| 407 | int ata_scsi_device_suspend(struct scsi_device *sdev) | ||
| 408 | { | ||
| 409 | struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0]; | ||
| 410 | struct ata_device *dev = &ap->device[sdev->id]; | ||
| 411 | |||
| 412 | return ata_device_suspend(ap, dev); | ||
| 413 | } | ||
| 414 | |||
| 399 | /** | 415 | /** |
| 400 | * ata_to_sense_error - convert ATA error to SCSI error | 416 | * ata_to_sense_error - convert ATA error to SCSI error |
| 401 | * @id: ATA device number | 417 | * @id: ATA device number |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 15842b1f0f4a..ea7f3a433572 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
| @@ -263,9 +263,40 @@ static int scsi_bus_match(struct device *dev, struct device_driver *gendrv) | |||
| 263 | return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0; | 263 | return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0; |
| 264 | } | 264 | } |
| 265 | 265 | ||
| 266 | static int scsi_bus_suspend(struct device * dev, pm_message_t state) | ||
| 267 | { | ||
| 268 | struct scsi_device *sdev = to_scsi_device(dev); | ||
| 269 | struct scsi_host_template *sht = sdev->host->hostt; | ||
| 270 | int err; | ||
| 271 | |||
| 272 | err = scsi_device_quiesce(sdev); | ||
| 273 | if (err) | ||
| 274 | return err; | ||
| 275 | |||
| 276 | if (sht->suspend) | ||
| 277 | err = sht->suspend(sdev); | ||
| 278 | |||
| 279 | return err; | ||
| 280 | } | ||
| 281 | |||
| 282 | static int scsi_bus_resume(struct device * dev) | ||
| 283 | { | ||
| 284 | struct scsi_device *sdev = to_scsi_device(dev); | ||
| 285 | struct scsi_host_template *sht = sdev->host->hostt; | ||
| 286 | int err = 0; | ||
| 287 | |||
| 288 | if (sht->resume) | ||
| 289 | err = sht->resume(sdev); | ||
| 290 | |||
| 291 | scsi_device_resume(sdev); | ||
| 292 | return err; | ||
| 293 | } | ||
| 294 | |||
| 266 | struct bus_type scsi_bus_type = { | 295 | struct bus_type scsi_bus_type = { |
| 267 | .name = "scsi", | 296 | .name = "scsi", |
| 268 | .match = scsi_bus_match, | 297 | .match = scsi_bus_match, |
| 298 | .suspend = scsi_bus_suspend, | ||
| 299 | .resume = scsi_bus_resume, | ||
| 269 | }; | 300 | }; |
| 270 | 301 | ||
| 271 | int scsi_sysfs_register(void) | 302 | int scsi_sysfs_register(void) |
diff --git a/include/linux/ata.h b/include/linux/ata.h index d2873b732bb1..3eb80c391b39 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h | |||
| @@ -141,6 +141,8 @@ enum { | |||
| 141 | ATA_CMD_PACKET = 0xA0, | 141 | ATA_CMD_PACKET = 0xA0, |
| 142 | ATA_CMD_VERIFY = 0x40, | 142 | ATA_CMD_VERIFY = 0x40, |
| 143 | ATA_CMD_VERIFY_EXT = 0x42, | 143 | ATA_CMD_VERIFY_EXT = 0x42, |
| 144 | ATA_CMD_STANDBYNOW1 = 0xE0, | ||
| 145 | ATA_CMD_IDLEIMMEDIATE = 0xE1, | ||
| 144 | ATA_CMD_INIT_DEV_PARAMS = 0x91, | 146 | ATA_CMD_INIT_DEV_PARAMS = 0x91, |
| 145 | 147 | ||
| 146 | /* SETFEATURES stuff */ | 148 | /* SETFEATURES stuff */ |
diff --git a/include/linux/libata.h b/include/linux/libata.h index e828e172ccbf..cdab75c209a0 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
| @@ -124,6 +124,8 @@ enum { | |||
| 124 | ATA_FLAG_DEBUGMSG = (1 << 10), | 124 | ATA_FLAG_DEBUGMSG = (1 << 10), |
| 125 | ATA_FLAG_NO_ATAPI = (1 << 11), /* No ATAPI support */ | 125 | ATA_FLAG_NO_ATAPI = (1 << 11), /* No ATAPI support */ |
| 126 | 126 | ||
| 127 | ATA_FLAG_SUSPENDED = (1 << 12), /* port is suspended */ | ||
| 128 | |||
| 127 | ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */ | 129 | ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */ |
| 128 | ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */ | 130 | ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */ |
| 129 | ATA_QCFLAG_SINGLE = (1 << 4), /* no s/g, just a single buffer */ | 131 | ATA_QCFLAG_SINGLE = (1 << 4), /* no s/g, just a single buffer */ |
| @@ -436,6 +438,8 @@ extern void ata_std_ports(struct ata_ioports *ioaddr); | |||
| 436 | extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, | 438 | extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, |
| 437 | unsigned int n_ports); | 439 | unsigned int n_ports); |
| 438 | extern void ata_pci_remove_one (struct pci_dev *pdev); | 440 | extern void ata_pci_remove_one (struct pci_dev *pdev); |
| 441 | extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state); | ||
| 442 | extern int ata_pci_device_resume(struct pci_dev *pdev); | ||
| 439 | #endif /* CONFIG_PCI */ | 443 | #endif /* CONFIG_PCI */ |
| 440 | extern int ata_device_add(const struct ata_probe_ent *ent); | 444 | extern int ata_device_add(const struct ata_probe_ent *ent); |
| 441 | extern void ata_host_set_remove(struct ata_host_set *host_set); | 445 | extern void ata_host_set_remove(struct ata_host_set *host_set); |
| @@ -445,6 +449,10 @@ extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmn | |||
| 445 | extern int ata_scsi_error(struct Scsi_Host *host); | 449 | extern int ata_scsi_error(struct Scsi_Host *host); |
| 446 | extern int ata_scsi_release(struct Scsi_Host *host); | 450 | extern int ata_scsi_release(struct Scsi_Host *host); |
| 447 | extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc); | 451 | extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc); |
| 452 | extern int ata_scsi_device_resume(struct scsi_device *); | ||
| 453 | extern int ata_scsi_device_suspend(struct scsi_device *); | ||
| 454 | extern int ata_device_resume(struct ata_port *, struct ata_device *); | ||
| 455 | extern int ata_device_suspend(struct ata_port *, struct ata_device *); | ||
| 448 | extern int ata_ratelimit(void); | 456 | extern int ata_ratelimit(void); |
| 449 | 457 | ||
| 450 | /* | 458 | /* |
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 6cbb1982ed03..6297885a35e7 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h | |||
| @@ -296,6 +296,12 @@ struct scsi_host_template { | |||
| 296 | int (*proc_info)(struct Scsi_Host *, char *, char **, off_t, int, int); | 296 | int (*proc_info)(struct Scsi_Host *, char *, char **, off_t, int, int); |
| 297 | 297 | ||
| 298 | /* | 298 | /* |
| 299 | * suspend support | ||
| 300 | */ | ||
| 301 | int (*resume)(struct scsi_device *); | ||
| 302 | int (*suspend)(struct scsi_device *); | ||
| 303 | |||
| 304 | /* | ||
| 299 | * Name of proc directory | 305 | * Name of proc directory |
| 300 | */ | 306 | */ |
| 301 | char *proc_name; | 307 | char *proc_name; |
