aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorPoul-Henning Kamp <phk@FreeBSD.org>2008-03-17 10:33:23 +0000
committerPoul-Henning Kamp <phk@FreeBSD.org>2008-03-17 10:33:23 +0000
commit72d945abcc61c6a0c4c9dc35a5cfab4159ef8e62 (patch)
tree42facee2a8a6b82282cf32d98ef5558ca3765746 /sys/dev
parent272870cf7bbcb8fb0204ec80e2ef0c6849eabd23 (diff)
Notes
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ata/ata-all.c6
-rw-r--r--sys/dev/ata/ata-all.h3
-rw-r--r--sys/dev/ata/ata-disk.c50
-rw-r--r--sys/dev/ata/ata-disk.h2
4 files changed, 59 insertions, 2 deletions
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c
index 963b2b506dca..e275b6a042fa 100644
--- a/sys/dev/ata/ata-all.c
+++ b/sys/dev/ata/ata-all.c
@@ -514,6 +514,12 @@ ata_device_ioctl(device_t dev, u_long cmd, caddr_t data)
case IOCATAGMODE:
*mode = atadev->mode;
return 0;
+ case IOCATASSPINDOWN:
+ atadev->spindown = *mode;
+ return 0;
+ case IOCATAGSPINDOWN:
+ *mode = atadev->spindown;
+ return 0;
default:
return ENOTTY;
}
diff --git a/sys/dev/ata/ata-all.h b/sys/dev/ata/ata-all.h
index f590c35a694b..28adac9daba6 100644
--- a/sys/dev/ata/ata-all.h
+++ b/sys/dev/ata/ata-all.h
@@ -403,6 +403,9 @@ struct ata_device {
struct ata_params param; /* ata param structure */
int mode; /* current transfermode */
u_int32_t max_iosize; /* max IO size */
+ int spindown; /* idle spindown timeout */
+ struct callout spindown_timer;
+ int spindown_state;
int flags;
#define ATA_D_USE_CHS 0x0001
#define ATA_D_MEDIA_CHANGED 0x0002
diff --git a/sys/dev/ata/ata-disk.c b/sys/dev/ata/ata-disk.c
index bac091fd771a..1ed4c208fbf6 100644
--- a/sys/dev/ata/ata-disk.c
+++ b/sys/dev/ata/ata-disk.c
@@ -171,6 +171,8 @@ ad_attach(device_t dev)
device_add_child(dev, "subdisk", device_get_unit(dev));
ad_firmware_geom_adjust(dev, adp->disk);
bus_generic_attach(dev);
+
+ callout_init(&atadev->spindown_timer, 1);
return 0;
}
@@ -178,6 +180,7 @@ static int
ad_detach(device_t dev)
{
struct ad_softc *adp = device_get_ivars(dev);
+ struct ata_device *atadev = device_get_softc(dev);
device_t *children;
int nchildren, i;
@@ -185,6 +188,9 @@ ad_detach(device_t dev)
if (!device_get_ivars(dev))
return ENXIO;
+ /* destroy the power timeout */
+ callout_drain(&atadev->spindown_timer);
+
/* detach & delete all children */
if (!device_get_children(dev, &children, &nchildren)) {
for (i = 0; i < nchildren; i++)
@@ -229,6 +235,38 @@ ad_reinit(device_t dev)
return 0;
}
+static void
+ad_power_callback(struct ata_request *request)
+{
+ device_printf(request->dev, "drive spun down.\n");
+ ata_free_request(request);
+}
+
+static void
+ad_spindown(void *priv)
+{
+ device_t dev = priv;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ata_request *request;
+
+ if(atadev->spindown == 0)
+ return;
+ device_printf(dev, "Idle, spin down\n");
+ atadev->spindown_state = 1;
+ if (!(request = ata_alloc_request())) {
+ device_printf(dev, "FAILURE - out of memory in ad_spindown\n");
+ return;
+ }
+ request->flags = ATA_R_CONTROL;
+ request->dev = dev;
+ request->timeout = 5;
+ request->retries = 1;
+ request->callback = ad_power_callback;
+ request->u.ata.command = ATA_STANDBY_IMMEDIATE;
+ ata_queue_request(request);
+}
+
+
static void
ad_strategy(struct bio *bp)
{
@@ -236,6 +274,10 @@ ad_strategy(struct bio *bp)
struct ata_device *atadev = device_get_softc(dev);
struct ata_request *request;
+ if (atadev->spindown != 0)
+ callout_reset(&atadev->spindown_timer, hz * atadev->spindown,
+ ad_spindown, dev);
+
if (!(request = ata_alloc_request())) {
device_printf(dev, "FAILURE - out of memory in start\n");
biofinish(bp, NULL, ENOMEM);
@@ -246,7 +288,13 @@ ad_strategy(struct bio *bp)
request->dev = dev;
request->bio = bp;
request->callback = ad_done;
- request->timeout = 5;
+ if (atadev->spindown_state) {
+ device_printf(dev, "request while spun down, starting.\n");
+ atadev->spindown_state = 0;
+ request->timeout = 31;
+ } else {
+ request->timeout = 5;
+ }
request->retries = 2;
request->data = bp->bio_data;
request->bytecount = bp->bio_bcount;
diff --git a/sys/dev/ata/ata-disk.h b/sys/dev/ata/ata-disk.h
index 1ebd3e0d5b64..562b32ff04c8 100644
--- a/sys/dev/ata/ata-disk.h
+++ b/sys/dev/ata/ata-disk.h
@@ -34,7 +34,7 @@ struct ad_softc {
u_int32_t transfersize; /* size of each transfer */
int num_tags; /* number of tags supported */
int flags; /* drive flags */
-#define AD_F_LABELLING 0x0001
+#define AD_F_LABELLING 0x0001
#define AD_F_CHS_USED 0x0002
#define AD_F_32B_ENABLED 0x0004
#define AD_F_TAG_ENABLED 0x0008