Blame view

drivers/ide/ide-pm.c 7.16 KB
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
1
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
2
  #include <linux/gfp.h>
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
3
  #include <linux/ide.h>
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
4
5
6
  
  int generic_ide_suspend(struct device *dev, pm_message_t mesg)
  {
9974e43d9   Miklos Szeredi   ide: fix generic_...
7
  	ide_drive_t *drive = to_ide_device(dev);
fcb520772   Greg Kroah-Hartman   ide: remove drive...
8
  	ide_drive_t *pair = ide_get_pair_dev(drive);
898ec223f   Bartlomiej Zolnierkiewicz   ide: remove HWIF(...
9
  	ide_hwif_t *hwif = drive->hwif;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
10
  	struct request *rq;
a7928c157   Christoph Hellwig   block: move PM re...
11
  	struct ide_pm_state rqpm;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
12
  	int ret;
2bf427b25   Bartlomiej Zolnierkiewicz   ide: fix resume f...
13
14
15
16
17
  	if (ide_port_acpi(hwif)) {
  		/* call ACPI _GTM only once */
  		if ((drive->dn & 1) == 0 || pair == NULL)
  			ide_acpi_get_timing(hwif);
  	}
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
18
19
  
  	memset(&rqpm, 0, sizeof(rqpm));
71baba4b9   Mel Gorman   mm, page_alloc: r...
20
  	rq = blk_get_request(drive->queue, READ, __GFP_RECLAIM);
a7928c157   Christoph Hellwig   block: move PM re...
21
  	rq->cmd_type = REQ_TYPE_ATA_PM_SUSPEND;
fc38b521d   Tejun Heo   ide-pm: don't abu...
22
  	rq->special = &rqpm;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
23
24
25
26
27
28
29
  	rqpm.pm_step = IDE_PM_START_SUSPEND;
  	if (mesg.event == PM_EVENT_PRETHAW)
  		mesg.event = PM_EVENT_FREEZE;
  	rqpm.pm_state = mesg.event;
  
  	ret = blk_execute_rq(drive->queue, NULL, rq, 0);
  	blk_put_request(rq);
2bf427b25   Bartlomiej Zolnierkiewicz   ide: fix resume f...
30
31
32
33
34
  	if (ret == 0 && ide_port_acpi(hwif)) {
  		/* call ACPI _PS3 only after both devices are suspended */
  		if ((drive->dn & 1) || pair == NULL)
  			ide_acpi_set_state(hwif, 0);
  	}
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
35
36
37
  
  	return ret;
  }
a7928c157   Christoph Hellwig   block: move PM re...
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  static void ide_end_sync_rq(struct request *rq, int error)
  {
  	complete(rq->end_io_data);
  }
  
  static int ide_pm_execute_rq(struct request *rq)
  {
  	struct request_queue *q = rq->q;
  	DECLARE_COMPLETION_ONSTACK(wait);
  
  	rq->end_io_data = &wait;
  	rq->end_io = ide_end_sync_rq;
  
  	spin_lock_irq(q->queue_lock);
  	if (unlikely(blk_queue_dying(q))) {
  		rq->cmd_flags |= REQ_QUIET;
  		rq->errors = -ENXIO;
  		__blk_end_request_all(rq, rq->errors);
  		spin_unlock_irq(q->queue_lock);
  		return -ENXIO;
  	}
  	__elv_add_request(q, rq, ELEVATOR_INSERT_FRONT);
  	__blk_run_queue_uncond(q);
  	spin_unlock_irq(q->queue_lock);
  
  	wait_for_completion_io(&wait);
  
  	return rq->errors ? -EIO : 0;
  }
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
67
68
  int generic_ide_resume(struct device *dev)
  {
9974e43d9   Miklos Szeredi   ide: fix generic_...
69
  	ide_drive_t *drive = to_ide_device(dev);
fcb520772   Greg Kroah-Hartman   ide: remove drive...
70
  	ide_drive_t *pair = ide_get_pair_dev(drive);
898ec223f   Bartlomiej Zolnierkiewicz   ide: remove HWIF(...
71
  	ide_hwif_t *hwif = drive->hwif;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
72
  	struct request *rq;
a7928c157   Christoph Hellwig   block: move PM re...
73
  	struct ide_pm_state rqpm;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
74
  	int err;
2bf427b25   Bartlomiej Zolnierkiewicz   ide: fix resume f...
75
76
77
78
79
80
  	if (ide_port_acpi(hwif)) {
  		/* call ACPI _PS0 / _STM only once */
  		if ((drive->dn & 1) == 0 || pair == NULL) {
  			ide_acpi_set_state(hwif, 1);
  			ide_acpi_push_timing(hwif);
  		}
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
81

2bf427b25   Bartlomiej Zolnierkiewicz   ide: fix resume f...
82
83
  		ide_acpi_exec_tfs(drive);
  	}
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
84
85
  
  	memset(&rqpm, 0, sizeof(rqpm));
71baba4b9   Mel Gorman   mm, page_alloc: r...
86
  	rq = blk_get_request(drive->queue, READ, __GFP_RECLAIM);
a7928c157   Christoph Hellwig   block: move PM re...
87
  	rq->cmd_type = REQ_TYPE_ATA_PM_RESUME;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
88
  	rq->cmd_flags |= REQ_PREEMPT;
fc38b521d   Tejun Heo   ide-pm: don't abu...
89
  	rq->special = &rqpm;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
90
91
  	rqpm.pm_step = IDE_PM_START_RESUME;
  	rqpm.pm_state = PM_EVENT_ON;
a7928c157   Christoph Hellwig   block: move PM re...
92
  	err = ide_pm_execute_rq(rq);
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
93
94
95
  	blk_put_request(rq);
  
  	if (err == 0 && dev->driver) {
7f3c868ba   Bartlomiej Zolnierkiewicz   ide: remove ide_d...
96
  		struct ide_driver *drv = to_ide_driver(dev->driver);
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
97
98
99
100
101
102
103
104
105
106
  
  		if (drv->resume)
  			drv->resume(drive);
  	}
  
  	return err;
  }
  
  void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
  {
a7928c157   Christoph Hellwig   block: move PM re...
107
  	struct ide_pm_state *pm = rq->special;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  
  #ifdef DEBUG_PM
  	printk(KERN_INFO "%s: complete_power_step(step: %d)
  ",
  		drive->name, pm->pm_step);
  #endif
  	if (drive->media != ide_disk)
  		return;
  
  	switch (pm->pm_step) {
  	case IDE_PM_FLUSH_CACHE:	/* Suspend step 1 (flush cache) */
  		if (pm->pm_state == PM_EVENT_FREEZE)
  			pm->pm_step = IDE_PM_COMPLETED;
  		else
  			pm->pm_step = IDE_PM_STANDBY;
  		break;
  	case IDE_PM_STANDBY:		/* Suspend step 2 (standby) */
  		pm->pm_step = IDE_PM_COMPLETED;
  		break;
  	case IDE_PM_RESTORE_PIO:	/* Resume step 1 (restore PIO) */
  		pm->pm_step = IDE_PM_IDLE;
  		break;
  	case IDE_PM_IDLE:		/* Resume step 2 (idle)*/
  		pm->pm_step = IDE_PM_RESTORE_DMA;
  		break;
  	}
  }
  
  ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
  {
a7928c157   Christoph Hellwig   block: move PM re...
138
  	struct ide_pm_state *pm = rq->special;
fc38b521d   Tejun Heo   ide-pm: don't abu...
139
  	struct ide_cmd cmd = { };
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
140
141
142
143
144
145
146
147
148
149
150
151
  
  	switch (pm->pm_step) {
  	case IDE_PM_FLUSH_CACHE:	/* Suspend step 1 (flush cache) */
  		if (drive->media != ide_disk)
  			break;
  		/* Not supported? Switch to next step now. */
  		if (ata_id_flush_enabled(drive->id) == 0 ||
  		    (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) {
  			ide_complete_power_step(drive, rq);
  			return ide_stopped;
  		}
  		if (ata_id_flush_ext_enabled(drive->id))
fc38b521d   Tejun Heo   ide-pm: don't abu...
152
  			cmd.tf.command = ATA_CMD_FLUSH_EXT;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
153
  		else
fc38b521d   Tejun Heo   ide-pm: don't abu...
154
  			cmd.tf.command = ATA_CMD_FLUSH;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
155
156
  		goto out_do_tf;
  	case IDE_PM_STANDBY:		/* Suspend step 2 (standby) */
fc38b521d   Tejun Heo   ide-pm: don't abu...
157
  		cmd.tf.command = ATA_CMD_STANDBYNOW1;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
158
159
160
161
162
163
164
165
166
167
168
169
  		goto out_do_tf;
  	case IDE_PM_RESTORE_PIO:	/* Resume step 1 (restore PIO) */
  		ide_set_max_pio(drive);
  		/*
  		 * skip IDE_PM_IDLE for ATAPI devices
  		 */
  		if (drive->media != ide_disk)
  			pm->pm_step = IDE_PM_RESTORE_DMA;
  		else
  			ide_complete_power_step(drive, rq);
  		return ide_stopped;
  	case IDE_PM_IDLE:		/* Resume step 2 (idle) */
fc38b521d   Tejun Heo   ide-pm: don't abu...
170
  		cmd.tf.command = ATA_CMD_IDLEIMMEDIATE;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  		goto out_do_tf;
  	case IDE_PM_RESTORE_DMA:	/* Resume step 3 (restore DMA) */
  		/*
  		 * Right now, all we do is call ide_set_dma(drive),
  		 * we could be smarter and check for current xfer_speed
  		 * in struct drive etc...
  		 */
  		if (drive->hwif->dma_ops == NULL)
  			break;
  		/*
  		 * TODO: respect IDE_DFLAG_USING_DMA
  		 */
  		ide_set_dma(drive);
  		break;
  	}
  
  	pm->pm_step = IDE_PM_COMPLETED;
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
188

e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
189
190
191
  	return ide_stopped;
  
  out_do_tf:
fc38b521d   Tejun Heo   ide-pm: don't abu...
192
193
194
  	cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;
  	cmd.valid.in.tf  = IDE_VALID_IN_TF  | IDE_VALID_DEVICE;
  	cmd.protocol = ATA_PROT_NODATA;
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
195

fc38b521d   Tejun Heo   ide-pm: don't abu...
196
  	return do_rw_taskfile(drive, &cmd);
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
197
198
199
  }
  
  /**
3616b6536   Bartlomiej Zolnierkiewicz   ide: complete pow...
200
   *	ide_complete_pm_rq - end the current Power Management request
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
201
202
203
204
205
206
   *	@drive: target drive
   *	@rq: request
   *
   *	This function cleans up the current PM request and stops the queue
   *	if necessary.
   */
3616b6536   Bartlomiej Zolnierkiewicz   ide: complete pow...
207
  void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq)
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
208
209
  {
  	struct request_queue *q = drive->queue;
a7928c157   Christoph Hellwig   block: move PM re...
210
  	struct ide_pm_state *pm = rq->special;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
211
  	unsigned long flags;
3616b6536   Bartlomiej Zolnierkiewicz   ide: complete pow...
212
213
214
  	ide_complete_power_step(drive, rq);
  	if (pm->pm_step != IDE_PM_COMPLETED)
  		return;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
215
216
217
  #ifdef DEBUG_PM
  	printk("%s: completing PM request, %s
  ", drive->name,
a7928c157   Christoph Hellwig   block: move PM re...
218
  	       (rq->cmd_type == REQ_TYPE_ATA_PM_SUSPEND) ? "suspend" : "resume");
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
219
220
  #endif
  	spin_lock_irqsave(q->queue_lock, flags);
a7928c157   Christoph Hellwig   block: move PM re...
221
  	if (rq->cmd_type == REQ_TYPE_ATA_PM_SUSPEND)
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
222
  		blk_stop_queue(q);
2ea552102   Bartlomiej Zolnierkiewicz   ide: fix suspend ...
223
  	else
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
224
  		drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
225
  	spin_unlock_irqrestore(q->queue_lock, flags);
b65fac32c   Bartlomiej Zolnierkiewicz   ide: merge ide_hw...
226
  	drive->hwif->rq = NULL;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
227
228
229
230
231
232
233
  
  	if (blk_end_request(rq, 0, 0))
  		BUG();
  }
  
  void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
  {
a7928c157   Christoph Hellwig   block: move PM re...
234
  	struct ide_pm_state *pm = rq->special;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
235

a7928c157   Christoph Hellwig   block: move PM re...
236
  	if (rq->cmd_type == REQ_TYPE_ATA_PM_SUSPEND &&
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
237
238
239
  	    pm->pm_step == IDE_PM_START_SUSPEND)
  		/* Mark drive blocked when starting the suspend sequence. */
  		drive->dev_flags |= IDE_DFLAG_BLOCKED;
a7928c157   Christoph Hellwig   block: move PM re...
240
  	else if (rq->cmd_type == REQ_TYPE_ATA_PM_RESUME &&
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
241
242
243
244
245
246
247
248
249
250
  		 pm->pm_step == IDE_PM_START_RESUME) {
  		/*
  		 * The first thing we do on wakeup is to wait for BSY bit to
  		 * go away (with a looong timeout) as a drive on this hwif may
  		 * just be POSTing itself.
  		 * We do that before even selecting as the "other" device on
  		 * the bus may be broken enough to walk on our toes at this
  		 * point.
  		 */
  		ide_hwif_t *hwif = drive->hwif;
fdd88f0af   Sergei Shtylyov   ide: inline SELEC...
251
  		const struct ide_tp_ops *tp_ops = hwif->tp_ops;
2ea552102   Bartlomiej Zolnierkiewicz   ide: fix suspend ...
252
253
  		struct request_queue *q = drive->queue;
  		unsigned long flags;
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
254
255
256
257
258
259
260
261
262
  		int rc;
  #ifdef DEBUG_PM
  		printk("%s: Wakeup request inited, waiting for !BSY...
  ", drive->name);
  #endif
  		rc = ide_wait_not_busy(hwif, 35000);
  		if (rc)
  			printk(KERN_WARNING "%s: bus not ready on wakeup
  ", drive->name);
fdd88f0af   Sergei Shtylyov   ide: inline SELEC...
263
264
  		tp_ops->dev_select(drive);
  		tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS);
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
265
266
267
268
  		rc = ide_wait_not_busy(hwif, 100000);
  		if (rc)
  			printk(KERN_WARNING "%s: drive not ready on wakeup
  ", drive->name);
2ea552102   Bartlomiej Zolnierkiewicz   ide: fix suspend ...
269
270
271
272
  
  		spin_lock_irqsave(q->queue_lock, flags);
  		blk_start_queue(q);
  		spin_unlock_irqrestore(q->queue_lock, flags);
e2984c628   Bartlomiej Zolnierkiewicz   ide: move Power M...
273
274
  	}
  }