blob: 416ce54af7ad60f565e41db463a3f41f1d1b30fc [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Bartlomiej Zolnierkiewicz59bca8c2008-02-01 23:09:33 +01002 * Copyright (C) 2000-2002 Michael Cornwell <cornwell@acm.org>
3 * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org>
4 * Copyright (C) 2001-2002 Klaus Smolin
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * IBM Storage Technology Division
Bartlomiej Zolnierkiewicz59bca8c2008-02-01 23:09:33 +01006 * Copyright (C) 2003-2004, 2007 Bartlomiej Zolnierkiewicz
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
8 * The big the bad and the ugly.
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/module.h>
12#include <linux/types.h>
13#include <linux/string.h>
14#include <linux/kernel.h>
15#include <linux/timer.h>
16#include <linux/mm.h>
Andrew Morton651c29a2006-02-15 15:17:37 -080017#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/interrupt.h>
19#include <linux/major.h>
20#include <linux/errno.h>
21#include <linux/genhd.h>
22#include <linux/blkpg.h>
23#include <linux/slab.h>
24#include <linux/pci.h>
25#include <linux/delay.h>
26#include <linux/hdreg.h>
27#include <linux/ide.h>
28#include <linux/bitops.h>
Jens Axboe55c16a72007-07-25 08:13:56 +020029#include <linux/scatterlist.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31#include <asm/byteorder.h>
32#include <asm/irq.h>
33#include <asm/uaccess.h>
34#include <asm/io.h>
35
Bartlomiej Zolnierkiewicz089c5c72008-04-28 23:44:39 +020036void ide_tf_dump(const char *s, struct ide_taskfile *tf)
37{
38#ifdef DEBUG
39 printk("%s: tf: feat 0x%02x nsect 0x%02x lbal 0x%02x "
40 "lbam 0x%02x lbah 0x%02x dev 0x%02x cmd 0x%02x\n",
41 s, tf->feature, tf->nsect, tf->lbal,
42 tf->lbam, tf->lbah, tf->device, tf->command);
43 printk("%s: hob: nsect 0x%02x lbal 0x%02x "
44 "lbam 0x%02x lbah 0x%02x\n",
45 s, tf->hob_nsect, tf->hob_lbal,
46 tf->hob_lbam, tf->hob_lbah);
47#endif
48}
49
Bartlomiej Zolnierkiewicz9e422372008-01-25 22:17:07 +010050void ide_tf_load(ide_drive_t *drive, ide_task_t *task)
51{
52 ide_hwif_t *hwif = drive->hwif;
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020053 struct ide_io_ports *io_ports = &hwif->io_ports;
Bartlomiej Zolnierkiewicz9e422372008-01-25 22:17:07 +010054 struct ide_taskfile *tf = &task->tf;
55 u8 HIHI = (task->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF;
56
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010057 if (task->tf_flags & IDE_TFLAG_FLAGGED)
58 HIHI = 0xFF;
59
Bartlomiej Zolnierkiewicz81ca6912008-01-26 20:13:08 +010060 ide_set_irq(drive, 1);
Bartlomiej Zolnierkiewicz9e422372008-01-25 22:17:07 +010061
62 if ((task->tf_flags & IDE_TFLAG_NO_SELECT_MASK) == 0)
63 SELECT_MASK(drive, 0);
64
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010065 if (task->tf_flags & IDE_TFLAG_OUT_DATA)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020066 hwif->OUTW((tf->hob_data << 8) | tf->data, io_ports->data_addr);
Bartlomiej Zolnierkiewicz9e422372008-01-25 22:17:07 +010067
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010068 if (task->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020069 hwif->OUTB(tf->hob_feature, io_ports->feature_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010070 if (task->tf_flags & IDE_TFLAG_OUT_HOB_NSECT)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020071 hwif->OUTB(tf->hob_nsect, io_ports->nsect_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010072 if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAL)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020073 hwif->OUTB(tf->hob_lbal, io_ports->lbal_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010074 if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAM)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020075 hwif->OUTB(tf->hob_lbam, io_ports->lbam_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010076 if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAH)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020077 hwif->OUTB(tf->hob_lbah, io_ports->lbah_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010078
79 if (task->tf_flags & IDE_TFLAG_OUT_FEATURE)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020080 hwif->OUTB(tf->feature, io_ports->feature_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010081 if (task->tf_flags & IDE_TFLAG_OUT_NSECT)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020082 hwif->OUTB(tf->nsect, io_ports->nsect_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010083 if (task->tf_flags & IDE_TFLAG_OUT_LBAL)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020084 hwif->OUTB(tf->lbal, io_ports->lbal_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010085 if (task->tf_flags & IDE_TFLAG_OUT_LBAM)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020086 hwif->OUTB(tf->lbam, io_ports->lbam_addr);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +010087 if (task->tf_flags & IDE_TFLAG_OUT_LBAH)
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020088 hwif->OUTB(tf->lbah, io_ports->lbah_addr);
Bartlomiej Zolnierkiewicz9e422372008-01-25 22:17:07 +010089
Bartlomiej Zolnierkiewicz807e35d2008-01-25 22:17:10 +010090 if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
Bartlomiej Zolnierkiewicz23579a22008-04-18 00:46:26 +020091 hwif->OUTB((tf->device & HIHI) | drive->select.all,
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +020092 io_ports->device_addr);
Bartlomiej Zolnierkiewicz9e422372008-01-25 22:17:07 +010093}
94
Linus Torvalds1da177e2005-04-16 15:20:36 -070095int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
96{
97 ide_task_t args;
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +010098
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 memset(&args, 0, sizeof(ide_task_t));
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100100 args.tf.nsect = 0x01;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 if (drive->media == ide_disk)
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100102 args.tf.command = WIN_IDENTIFY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 else
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100104 args.tf.command = WIN_PIDENTIFY;
Bartlomiej Zolnierkiewicz657cc1a2008-01-26 20:13:10 +0100105 args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100106 args.data_phase = TASKFILE_IN;
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100107 return ide_raw_taskfile(drive, &args, buf, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108}
109
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +0100110static int inline task_dma_ok(ide_task_t *task)
111{
Bartlomiej Zolnierkiewiczf6e29e32008-01-25 22:17:16 +0100112 if (blk_fs_request(task->rq) || (task->tf_flags & IDE_TFLAG_FLAGGED))
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +0100113 return 1;
114
115 switch (task->tf.command) {
116 case WIN_WRITEDMA_ONCE:
117 case WIN_WRITEDMA:
118 case WIN_WRITEDMA_EXT:
119 case WIN_READDMA_ONCE:
120 case WIN_READDMA:
121 case WIN_READDMA_EXT:
122 case WIN_IDENTIFY_DMA:
123 return 1;
124 }
125
126 return 0;
127}
128
Bartlomiej Zolnierkiewicz1192e522008-01-25 22:17:16 +0100129static ide_startstop_t task_no_data_intr(ide_drive_t *);
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100130static ide_startstop_t set_geometry_intr(ide_drive_t *);
131static ide_startstop_t recal_intr(ide_drive_t *);
132static ide_startstop_t set_multmode_intr(ide_drive_t *);
Bartlomiej Zolnierkiewiczf6e29e32008-01-25 22:17:16 +0100133static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct request *);
134static ide_startstop_t task_in_intr(ide_drive_t *);
Bartlomiej Zolnierkiewicz1192e522008-01-25 22:17:16 +0100135
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
137{
138 ide_hwif_t *hwif = HWIF(drive);
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100139 struct ide_taskfile *tf = &task->tf;
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100140 ide_handler_t *handler = NULL;
Bartlomiej Zolnierkiewiczf37afda2008-04-26 22:25:24 +0200141 const struct ide_dma_ops *dma_ops = hwif->dma_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
Bartlomiej Zolnierkiewicz1edee602008-01-25 22:17:15 +0100143 if (task->data_phase == TASKFILE_MULTI_IN ||
144 task->data_phase == TASKFILE_MULTI_OUT) {
145 if (!drive->mult_count) {
146 printk(KERN_ERR "%s: multimode not set!\n",
147 drive->name);
148 return ide_stopped;
149 }
150 }
151
152 if (task->tf_flags & IDE_TFLAG_FLAGGED)
153 task->tf_flags |= IDE_TFLAG_FLAGGED_SET_IN_FLAGS;
154
Bartlomiej Zolnierkiewicz089c5c72008-04-28 23:44:39 +0200155 if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) {
156 ide_tf_dump(drive->name, tf);
Bartlomiej Zolnierkiewiczf6e29e32008-01-25 22:17:16 +0100157 ide_tf_load(drive, task);
Bartlomiej Zolnierkiewicz089c5c72008-04-28 23:44:39 +0200158 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Bartlomiej Zolnierkiewicz10d90152008-01-25 22:17:16 +0100160 switch (task->data_phase) {
161 case TASKFILE_MULTI_OUT:
162 case TASKFILE_OUT:
Bartlomiej Zolnierkiewicz4c3032d2008-04-27 15:38:32 +0200163 hwif->OUTBSYNC(drive, tf->command, hwif->io_ports.command_addr);
Bartlomiej Zolnierkiewicz10d90152008-01-25 22:17:16 +0100164 ndelay(400); /* FIXME */
165 return pre_task_out_intr(drive, task->rq);
166 case TASKFILE_MULTI_IN:
167 case TASKFILE_IN:
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100168 handler = task_in_intr;
Bartlomiej Zolnierkiewicz1192e522008-01-25 22:17:16 +0100169 /* fall-through */
Bartlomiej Zolnierkiewicz10d90152008-01-25 22:17:16 +0100170 case TASKFILE_NO_DATA:
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100171 if (handler == NULL)
172 handler = task_no_data_intr;
Bartlomiej Zolnierkiewicz1192e522008-01-25 22:17:16 +0100173 /* WIN_{SPECIFY,RESTORE,SETMULT} use custom handlers */
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100174 if (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) {
175 switch (tf->command) {
176 case WIN_SPECIFY: handler = set_geometry_intr; break;
177 case WIN_RESTORE: handler = recal_intr; break;
178 case WIN_SETMULT: handler = set_multmode_intr; break;
179 }
180 }
181 ide_execute_command(drive, tf->command, handler,
182 WAIT_WORSTCASE, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 return ide_started;
Bartlomiej Zolnierkiewicz10d90152008-01-25 22:17:16 +0100184 default:
185 if (task_dma_ok(task) == 0 || drive->using_dma == 0 ||
Bartlomiej Zolnierkiewicz5e37bdc2008-04-26 22:25:24 +0200186 dma_ops->dma_setup(drive))
Bartlomiej Zolnierkiewicz10d90152008-01-25 22:17:16 +0100187 return ide_stopped;
Bartlomiej Zolnierkiewicz5e37bdc2008-04-26 22:25:24 +0200188 dma_ops->dma_exec_cmd(drive, tf->command);
189 dma_ops->dma_start(drive);
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +0100190 return ide_started;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192}
Bartlomiej Zolnierkiewiczf6e29e32008-01-25 22:17:16 +0100193EXPORT_SYMBOL_GPL(do_rw_taskfile);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195/*
196 * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
197 */
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100198static ide_startstop_t set_multmode_intr(ide_drive_t *drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100200 u8 stat = ide_read_status(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100202 if (OK_STAT(stat, READY_STAT, BAD_STAT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 drive->mult_count = drive->mult_req;
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100204 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 drive->mult_req = drive->mult_count = 0;
206 drive->special.b.recalibrate = 1;
207 (void) ide_dump_status(drive, "set_multmode", stat);
208 }
209 return ide_stopped;
210}
211
212/*
213 * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
214 */
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100215static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 int retries = 5;
218 u8 stat;
219
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100220 while (((stat = ide_read_status(drive)) & BUSY_STAT) && retries--)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 udelay(10);
222
223 if (OK_STAT(stat, READY_STAT, BAD_STAT))
224 return ide_stopped;
225
226 if (stat & (ERR_STAT|DRQ_STAT))
227 return ide_error(drive, "set_geometry_intr", stat);
228
Eric Sesterhenn125e1872006-06-23 02:06:06 -0700229 BUG_ON(HWGROUP(drive)->handler != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL);
231 return ide_started;
232}
233
234/*
235 * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
236 */
Bartlomiej Zolnierkiewicz57d73662008-01-25 22:17:16 +0100237static ide_startstop_t recal_intr(ide_drive_t *drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238{
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100239 u8 stat = ide_read_status(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100241 if (!OK_STAT(stat, READY_STAT, BAD_STAT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 return ide_error(drive, "recal_intr", stat);
243 return ide_stopped;
244}
245
246/*
247 * Handler for commands without a data phase
248 */
Bartlomiej Zolnierkiewicz1192e522008-01-25 22:17:16 +0100249static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
251 ide_task_t *args = HWGROUP(drive)->rq->special;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 u8 stat;
253
Ingo Molnar366c7f52006-07-03 00:25:25 -0700254 local_irq_enable_in_hardirq();
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100255 stat = ide_read_status(drive);
256
257 if (!OK_STAT(stat, READY_STAT, BAD_STAT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 return ide_error(drive, "task_no_data_intr", stat);
259 /* calls ide_end_drive_cmd */
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 if (args)
Bartlomiej Zolnierkiewicz64a57fe2008-02-06 02:57:51 +0100262 ide_end_drive_cmd(drive, stat, ide_read_error(drive));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 return ide_stopped;
265}
266
Adrian Bunkda6f4c72008-02-01 23:09:16 +0100267static u8 wait_drive_not_busy(ide_drive_t *drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
Masatake YAMATOb42fa132007-07-03 22:28:34 +0200269 int retries;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 u8 stat;
271
272 /*
273 * Last sector was transfered, wait until drive is ready.
Bartlomiej Zolnierkiewicz26245652008-01-26 20:13:11 +0100274 * This can take up to 10 usec, but we will wait max 1 ms.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 */
Masatake YAMATOb42fa132007-07-03 22:28:34 +0200276 for (retries = 0; retries < 100; retries++) {
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100277 stat = ide_read_status(drive);
278
279 if (stat & BUSY_STAT)
Masatake YAMATOb42fa132007-07-03 22:28:34 +0200280 udelay(10);
281 else
282 break;
283 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
Masatake YAMATOb42fa132007-07-03 22:28:34 +0200285 if (stat & BUSY_STAT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 printk(KERN_ERR "%s: drive still BUSY!\n", drive->name);
287
288 return stat;
289}
290
Bartlomiej Zolnierkiewicz92d3ab22008-04-28 23:44:36 +0200291static void ide_pio_sector(ide_drive_t *drive, struct request *rq,
292 unsigned int write)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293{
294 ide_hwif_t *hwif = drive->hwif;
295 struct scatterlist *sg = hwif->sg_table;
Jens Axboe55c16a72007-07-25 08:13:56 +0200296 struct scatterlist *cursg = hwif->cursg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 struct page *page;
298#ifdef CONFIG_HIGHMEM
299 unsigned long flags;
300#endif
301 unsigned int offset;
302 u8 *buf;
303
Jens Axboe55c16a72007-07-25 08:13:56 +0200304 cursg = hwif->cursg;
305 if (!cursg) {
306 cursg = sg;
307 hwif->cursg = sg;
308 }
309
Jens Axboe45711f12007-10-22 21:19:53 +0200310 page = sg_page(cursg);
Jens Axboe55c16a72007-07-25 08:13:56 +0200311 offset = cursg->offset + hwif->cursg_ofs * SECTOR_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 /* get the current page and offset */
314 page = nth_page(page, (offset >> PAGE_SHIFT));
315 offset %= PAGE_SIZE;
316
317#ifdef CONFIG_HIGHMEM
318 local_irq_save(flags);
319#endif
320 buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset;
321
322 hwif->nleft--;
323 hwif->cursg_ofs++;
324
Jens Axboe55c16a72007-07-25 08:13:56 +0200325 if ((hwif->cursg_ofs * SECTOR_SIZE) == cursg->length) {
326 hwif->cursg = sg_next(hwif->cursg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 hwif->cursg_ofs = 0;
328 }
329
330 /* do the actual data transfer */
331 if (write)
Bartlomiej Zolnierkiewicz9567b342008-04-28 23:44:36 +0200332 hwif->output_data(drive, rq, buf, SECTOR_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 else
Bartlomiej Zolnierkiewicz9567b342008-04-28 23:44:36 +0200334 hwif->input_data(drive, rq, buf, SECTOR_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336 kunmap_atomic(buf, KM_BIO_SRC_IRQ);
337#ifdef CONFIG_HIGHMEM
338 local_irq_restore(flags);
339#endif
340}
341
Bartlomiej Zolnierkiewicz92d3ab22008-04-28 23:44:36 +0200342static void ide_pio_multi(ide_drive_t *drive, struct request *rq,
343 unsigned int write)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
345 unsigned int nsect;
346
347 nsect = min_t(unsigned int, drive->hwif->nleft, drive->mult_count);
348 while (nsect--)
Bartlomiej Zolnierkiewicz92d3ab22008-04-28 23:44:36 +0200349 ide_pio_sector(drive, rq, write);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350}
351
Arjan van de Ven858119e2006-01-14 13:20:43 -0800352static void ide_pio_datablock(ide_drive_t *drive, struct request *rq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 unsigned int write)
354{
Tejun Heo35cf2b92008-01-26 20:13:10 +0100355 u8 saved_io_32bit = drive->io_32bit;
356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 if (rq->bio) /* fs request */
358 rq->errors = 0;
359
Tejun Heo35cf2b92008-01-26 20:13:10 +0100360 if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
361 ide_task_t *task = rq->special;
362
363 if (task->tf_flags & IDE_TFLAG_IO_16BIT)
364 drive->io_32bit = 0;
365 }
366
Andrew Morton651c29a2006-02-15 15:17:37 -0800367 touch_softlockup_watchdog();
368
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 switch (drive->hwif->data_phase) {
370 case TASKFILE_MULTI_IN:
371 case TASKFILE_MULTI_OUT:
Bartlomiej Zolnierkiewicz92d3ab22008-04-28 23:44:36 +0200372 ide_pio_multi(drive, rq, write);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 break;
374 default:
Bartlomiej Zolnierkiewicz92d3ab22008-04-28 23:44:36 +0200375 ide_pio_sector(drive, rq, write);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 break;
377 }
Tejun Heo35cf2b92008-01-26 20:13:10 +0100378
379 drive->io_32bit = saved_io_32bit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380}
381
382static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq,
383 const char *s, u8 stat)
384{
385 if (rq->bio) {
386 ide_hwif_t *hwif = drive->hwif;
387 int sectors = hwif->nsect - hwif->nleft;
388
389 switch (hwif->data_phase) {
390 case TASKFILE_IN:
391 if (hwif->nleft)
392 break;
393 /* fall through */
394 case TASKFILE_OUT:
395 sectors--;
396 break;
397 case TASKFILE_MULTI_IN:
398 if (hwif->nleft)
399 break;
400 /* fall through */
401 case TASKFILE_MULTI_OUT:
402 sectors -= drive->mult_count;
403 default:
404 break;
405 }
406
407 if (sectors > 0) {
408 ide_driver_t *drv;
409
410 drv = *(ide_driver_t **)rq->rq_disk->private_data;
411 drv->end_request(drive, 1, sectors);
412 }
413 }
414 return ide_error(drive, s, stat);
415}
416
Tejun Heo4d7a9842008-01-26 20:13:11 +0100417void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418{
Jens Axboe4aff5e22006-08-10 08:44:47 +0200419 if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
Bartlomiej Zolnierkiewicz64a57fe2008-02-06 02:57:51 +0100420 u8 err = ide_read_error(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
Tejun Heo4d7a9842008-01-26 20:13:11 +0100422 ide_end_drive_cmd(drive, stat, err);
423 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 }
425
Richard Purdie03731fb2006-03-31 02:31:15 -0800426 if (rq->rq_disk) {
427 ide_driver_t *drv;
428
429 drv = *(ide_driver_t **)rq->rq_disk->private_data;;
Bartlomiej Zolnierkiewicz79f21b82008-01-26 20:13:11 +0100430 drv->end_request(drive, 1, rq->nr_sectors);
Richard Purdie03731fb2006-03-31 02:31:15 -0800431 } else
Bartlomiej Zolnierkiewicz79f21b82008-01-26 20:13:11 +0100432 ide_end_request(drive, 1, rq->nr_sectors);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433}
434
435/*
Linus Torvalds6c3c3152008-03-18 21:26:24 -0700436 * We got an interrupt on a task_in case, but no errors and no DRQ.
437 *
438 * It might be a spurious irq (shared irq), but it might be a
439 * command that had no output.
440 */
441static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq, u8 stat)
442{
443 /* Command all done? */
444 if (OK_STAT(stat, READY_STAT, BUSY_STAT)) {
445 task_end_request(drive, rq, stat);
446 return ide_stopped;
447 }
448
449 /* Assume it was a spurious irq */
450 ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);
451 return ide_started;
452}
453
454/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 * Handler for command with PIO data-in phase (Read/Read Multiple).
456 */
Bartlomiej Zolnierkiewiczf6e29e32008-01-25 22:17:16 +0100457static ide_startstop_t task_in_intr(ide_drive_t *drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458{
459 ide_hwif_t *hwif = drive->hwif;
460 struct request *rq = HWGROUP(drive)->rq;
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100461 u8 stat = ide_read_status(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462
Linus Torvalds6c3c3152008-03-18 21:26:24 -0700463 /* Error? */
464 if (stat & ERR_STAT)
Harvey Harrisoneb639632008-04-26 22:25:20 +0200465 return task_error(drive, rq, __func__, stat);
Linus Torvalds6c3c3152008-03-18 21:26:24 -0700466
467 /* Didn't want any data? Odd. */
468 if (!(stat & DRQ_STAT))
469 return task_in_unexpected(drive, rq, stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 ide_pio_datablock(drive, rq, 0);
472
Linus Torvalds6c3c3152008-03-18 21:26:24 -0700473 /* Are we done? Check status and finish transfer. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 if (!hwif->nleft) {
475 stat = wait_drive_not_busy(drive);
Bartlomiej Zolnierkiewicz73d7de02008-01-26 20:13:10 +0100476 if (!OK_STAT(stat, 0, BAD_STAT))
Harvey Harrisoneb639632008-04-26 22:25:20 +0200477 return task_error(drive, rq, __func__, stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 task_end_request(drive, rq, stat);
479 return ide_stopped;
480 }
481
482 /* Still data left to transfer. */
483 ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);
484
485 return ide_started;
486}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488/*
489 * Handler for command with PIO data-out phase (Write/Write Multiple).
490 */
491static ide_startstop_t task_out_intr (ide_drive_t *drive)
492{
493 ide_hwif_t *hwif = drive->hwif;
494 struct request *rq = HWGROUP(drive)->rq;
Bartlomiej Zolnierkiewiczc47137a2008-02-06 02:57:51 +0100495 u8 stat = ide_read_status(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
497 if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))
Harvey Harrisoneb639632008-04-26 22:25:20 +0200498 return task_error(drive, rq, __func__, stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
500 /* Deal with unexpected ATA data phase. */
501 if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft)
Harvey Harrisoneb639632008-04-26 22:25:20 +0200502 return task_error(drive, rq, __func__, stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
504 if (!hwif->nleft) {
505 task_end_request(drive, rq, stat);
506 return ide_stopped;
507 }
508
509 /* Still data left to transfer. */
510 ide_pio_datablock(drive, rq, 1);
511 ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);
512
513 return ide_started;
514}
515
Bartlomiej Zolnierkiewiczf6e29e32008-01-25 22:17:16 +0100516static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517{
518 ide_startstop_t startstop;
519
Bartlomiej Zolnierkiewicz4906f3b2008-01-26 20:13:11 +0100520 if (ide_wait_stat(&startstop, drive, DRQ_STAT,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 drive->bad_wstat, WAIT_DRQ)) {
522 printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n",
523 drive->name,
524 drive->hwif->data_phase ? "MULT" : "",
525 drive->addressing ? "_EXT" : "");
526 return startstop;
527 }
528
529 if (!drive->unmask)
530 local_irq_disable();
531
532 ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);
533 ide_pio_datablock(drive, rq, 1);
534
535 return ide_started;
536}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100538int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539{
540 struct request rq;
541
542 memset(&rq, 0, sizeof(rq));
Bartlomiej Zolnierkiewicz02ac2462007-11-05 21:42:27 +0100543 rq.ref_count = 1;
Jens Axboe4aff5e22006-08-10 08:44:47 +0200544 rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 rq.buffer = buf;
546
547 /*
548 * (ks) We transfer currently only whole sectors.
549 * This is suffient for now. But, it would be great,
550 * if we would find a solution to transfer any size.
551 * To support special commands like READ LONG.
552 */
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100553 rq.hard_nr_sectors = rq.nr_sectors = nsect;
554 rq.hard_cur_sectors = rq.current_nr_sectors = nsect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100556 if (task->tf_flags & IDE_TFLAG_WRITE)
557 rq.cmd_flags |= REQ_RW;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100559 rq.special = task;
560 task->rq = &rq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 return ide_do_drive_cmd(drive, &rq, ide_wait);
563}
564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565EXPORT_SYMBOL(ide_raw_taskfile);
566
Bartlomiej Zolnierkiewicz9a3c49b2008-01-25 22:17:07 +0100567int ide_no_data_taskfile(ide_drive_t *drive, ide_task_t *task)
568{
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100569 task->data_phase = TASKFILE_NO_DATA;
Bartlomiej Zolnierkiewicz9a3c49b2008-01-25 22:17:07 +0100570
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100571 return ide_raw_taskfile(drive, task, NULL, 0);
Bartlomiej Zolnierkiewicz9a3c49b2008-01-25 22:17:07 +0100572}
573EXPORT_SYMBOL_GPL(ide_no_data_taskfile);
574
Bartlomiej Zolnierkiewicz26a5b042007-11-05 21:42:27 +0100575#ifdef CONFIG_IDE_TASK_IOCTL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
577{
578 ide_task_request_t *req_task;
579 ide_task_t args;
580 u8 *outbuf = NULL;
581 u8 *inbuf = NULL;
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100582 u8 *data_buf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 int err = 0;
584 int tasksize = sizeof(struct ide_task_request_s);
Alan Cox3a42bb22006-10-16 16:31:02 +0100585 unsigned int taskin = 0;
586 unsigned int taskout = 0;
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100587 u16 nsect = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 char __user *buf = (char __user *)arg;
589
590// printk("IDE Taskfile ...\n");
591
Deepak Saxenaf5e3c2f2005-11-07 01:01:25 -0800592 req_task = kzalloc(tasksize, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 if (req_task == NULL) return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 if (copy_from_user(req_task, buf, tasksize)) {
595 kfree(req_task);
596 return -EFAULT;
597 }
598
Alan Cox3a42bb22006-10-16 16:31:02 +0100599 taskout = req_task->out_size;
600 taskin = req_task->in_size;
601
602 if (taskin > 65536 || taskout > 65536) {
603 err = -EINVAL;
604 goto abort;
605 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 if (taskout) {
608 int outtotal = tasksize;
Deepak Saxenaf5e3c2f2005-11-07 01:01:25 -0800609 outbuf = kzalloc(taskout, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 if (outbuf == NULL) {
611 err = -ENOMEM;
612 goto abort;
613 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 if (copy_from_user(outbuf, buf + outtotal, taskout)) {
615 err = -EFAULT;
616 goto abort;
617 }
618 }
619
620 if (taskin) {
621 int intotal = tasksize + taskout;
Deepak Saxenaf5e3c2f2005-11-07 01:01:25 -0800622 inbuf = kzalloc(taskin, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 if (inbuf == NULL) {
624 err = -ENOMEM;
625 goto abort;
626 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 if (copy_from_user(inbuf, buf + intotal, taskin)) {
628 err = -EFAULT;
629 goto abort;
630 }
631 }
632
633 memset(&args, 0, sizeof(ide_task_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100635 memcpy(&args.tf_array[0], req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE - 2);
636 memcpy(&args.tf_array[6], req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE);
Bartlomiej Zolnierkiewicz866e2ec2008-01-25 22:17:14 +0100637
638 args.data_phase = req_task->data_phase;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Bartlomiej Zolnierkiewicz657cc1a2008-01-26 20:13:10 +0100640 args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE |
641 IDE_TFLAG_IN_TF;
Bartlomiej Zolnierkiewicza3bbb9d2008-01-25 22:17:10 +0100642 if (drive->addressing == 1)
Bartlomiej Zolnierkiewicz657cc1a2008-01-26 20:13:10 +0100643 args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB);
Bartlomiej Zolnierkiewicza3bbb9d2008-01-25 22:17:10 +0100644
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +0100645 if (req_task->out_flags.all) {
646 args.tf_flags |= IDE_TFLAG_FLAGGED;
647
648 if (req_task->out_flags.b.data)
649 args.tf_flags |= IDE_TFLAG_OUT_DATA;
650
651 if (req_task->out_flags.b.nsector_hob)
652 args.tf_flags |= IDE_TFLAG_OUT_HOB_NSECT;
653 if (req_task->out_flags.b.sector_hob)
654 args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAL;
655 if (req_task->out_flags.b.lcyl_hob)
656 args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAM;
657 if (req_task->out_flags.b.hcyl_hob)
658 args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAH;
659
660 if (req_task->out_flags.b.error_feature)
661 args.tf_flags |= IDE_TFLAG_OUT_FEATURE;
662 if (req_task->out_flags.b.nsector)
663 args.tf_flags |= IDE_TFLAG_OUT_NSECT;
664 if (req_task->out_flags.b.sector)
665 args.tf_flags |= IDE_TFLAG_OUT_LBAL;
666 if (req_task->out_flags.b.lcyl)
667 args.tf_flags |= IDE_TFLAG_OUT_LBAM;
668 if (req_task->out_flags.b.hcyl)
669 args.tf_flags |= IDE_TFLAG_OUT_LBAH;
Bartlomiej Zolnierkiewicza3bbb9d2008-01-25 22:17:10 +0100670 } else {
671 args.tf_flags |= IDE_TFLAG_OUT_TF;
672 if (args.tf_flags & IDE_TFLAG_LBA48)
673 args.tf_flags |= IDE_TFLAG_OUT_HOB;
Bartlomiej Zolnierkiewicz74095a92008-01-25 22:17:07 +0100674 }
675
Bartlomiej Zolnierkiewicz866e2ec2008-01-25 22:17:14 +0100676 if (req_task->in_flags.b.data)
677 args.tf_flags |= IDE_TFLAG_IN_DATA;
678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 switch(req_task->data_phase) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 case TASKFILE_MULTI_OUT:
681 if (!drive->mult_count) {
682 /* (hs): give up if multcount is not set */
683 printk(KERN_ERR "%s: %s Multimode Write " \
684 "multcount is not set\n",
Harvey Harrisoneb639632008-04-26 22:25:20 +0200685 drive->name, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 err = -EPERM;
687 goto abort;
688 }
689 /* fall through */
690 case TASKFILE_OUT:
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100691 /* fall through */
692 case TASKFILE_OUT_DMAQ:
693 case TASKFILE_OUT_DMA:
694 nsect = taskout / SECTOR_SIZE;
695 data_buf = outbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 break;
697 case TASKFILE_MULTI_IN:
698 if (!drive->mult_count) {
699 /* (hs): give up if multcount is not set */
700 printk(KERN_ERR "%s: %s Multimode Read failure " \
701 "multcount is not set\n",
Harvey Harrisoneb639632008-04-26 22:25:20 +0200702 drive->name, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 err = -EPERM;
704 goto abort;
705 }
706 /* fall through */
707 case TASKFILE_IN:
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100708 /* fall through */
709 case TASKFILE_IN_DMAQ:
710 case TASKFILE_IN_DMA:
711 nsect = taskin / SECTOR_SIZE;
712 data_buf = inbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 break;
714 case TASKFILE_NO_DATA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 break;
716 default:
717 err = -EFAULT;
718 goto abort;
719 }
720
Bartlomiej Zolnierkiewiczac026ff2008-01-25 22:17:14 +0100721 if (req_task->req_cmd == IDE_DRIVE_TASK_NO_DATA)
722 nsect = 0;
723 else if (!nsect) {
724 nsect = (args.tf.hob_nsect << 8) | args.tf.nsect;
725
726 if (!nsect) {
727 printk(KERN_ERR "%s: in/out command without data\n",
728 drive->name);
729 err = -EFAULT;
730 goto abort;
731 }
732 }
733
734 if (req_task->req_cmd == IDE_DRIVE_TASK_RAW_WRITE)
735 args.tf_flags |= IDE_TFLAG_WRITE;
736
737 err = ide_raw_taskfile(drive, &args, data_buf, nsect);
738
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100739 memcpy(req_task->hob_ports, &args.tf_array[0], HDIO_DRIVE_HOB_HDR_SIZE - 2);
740 memcpy(req_task->io_ports, &args.tf_array[6], HDIO_DRIVE_TASK_HDR_SIZE);
Bartlomiej Zolnierkiewicz866e2ec2008-01-25 22:17:14 +0100741
742 if ((args.tf_flags & IDE_TFLAG_FLAGGED_SET_IN_FLAGS) &&
743 req_task->in_flags.all == 0) {
744 req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS;
745 if (drive->addressing == 1)
746 req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
747 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
749 if (copy_to_user(buf, req_task, tasksize)) {
750 err = -EFAULT;
751 goto abort;
752 }
753 if (taskout) {
754 int outtotal = tasksize;
755 if (copy_to_user(buf + outtotal, outbuf, taskout)) {
756 err = -EFAULT;
757 goto abort;
758 }
759 }
760 if (taskin) {
761 int intotal = tasksize + taskout;
762 if (copy_to_user(buf + intotal, inbuf, taskin)) {
763 err = -EFAULT;
764 goto abort;
765 }
766 }
767abort:
768 kfree(req_task);
Jesper Juhl6044ec82005-11-07 01:01:32 -0800769 kfree(outbuf);
770 kfree(inbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
772// printk("IDE Taskfile ioctl ended. rc = %i\n", err);
773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 return err;
775}
Bartlomiej Zolnierkiewicz26a5b042007-11-05 21:42:27 +0100776#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
779{
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100780 u8 *buf = NULL;
781 int bufsize = 0, err = 0;
782 u8 args[4], xfer_rate = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 ide_task_t tfargs;
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100784 struct ide_taskfile *tf = &tfargs.tf;
Bartlomiej Zolnierkiewicz5efe7c52008-02-02 19:56:46 +0100785 struct hd_driveid *id = drive->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
787 if (NULL == (void *) arg) {
788 struct request rq;
Bartlomiej Zolnierkiewicz145b75e2008-01-26 20:13:11 +0100789
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 ide_init_drive_cmd(&rq);
Bartlomiej Zolnierkiewicz852738f2008-01-26 20:13:12 +0100791 rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
Bartlomiej Zolnierkiewicz145b75e2008-01-26 20:13:11 +0100792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 return ide_do_drive_cmd(drive, &rq, ide_wait);
794 }
795
796 if (copy_from_user(args, (void __user *)arg, 4))
797 return -EFAULT;
798
799 memset(&tfargs, 0, sizeof(ide_task_t));
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100800 tf->feature = args[2];
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100801 if (args[0] == WIN_SMART) {
802 tf->nsect = args[3];
803 tf->lbal = args[1];
804 tf->lbam = 0x4f;
805 tf->lbah = 0xc2;
806 tfargs.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_IN_NSECT;
807 } else {
808 tf->nsect = args[1];
809 tfargs.tf_flags = IDE_TFLAG_OUT_FEATURE |
810 IDE_TFLAG_OUT_NSECT | IDE_TFLAG_IN_NSECT;
811 }
Bartlomiej Zolnierkiewicz650d8412008-01-25 22:17:06 +0100812 tf->command = args[0];
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100813 tfargs.data_phase = args[3] ? TASKFILE_IN : TASKFILE_NO_DATA;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 if (args[3]) {
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100816 tfargs.tf_flags |= IDE_TFLAG_IO_16BIT;
817 bufsize = SECTOR_WORDS * 4 * args[3];
818 buf = kzalloc(bufsize, GFP_KERNEL);
819 if (buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 }
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100822
Bartlomiej Zolnierkiewicz5efe7c52008-02-02 19:56:46 +0100823 if (tf->command == WIN_SETFEATURES &&
824 tf->feature == SETFEATURES_XFER &&
825 tf->nsect >= XFER_SW_DMA_0 &&
826 (id->dma_ultra || id->dma_mword || id->dma_1word)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 xfer_rate = args[1];
Bartlomiej Zolnierkiewiczaf10f772008-02-02 19:56:46 +0100828 if (tf->nsect > XFER_UDMA_2 && !eighty_ninty_three(drive)) {
829 printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot "
830 "be set\n", drive->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 goto abort;
Bartlomiej Zolnierkiewiczaf10f772008-02-02 19:56:46 +0100832 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 }
834
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100835 err = ide_raw_taskfile(drive, &tfargs, buf, args[3]);
836
837 args[0] = tf->status;
838 args[1] = tf->error;
839 args[2] = tf->nsect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
841 if (!err && xfer_rate) {
842 /* active-retuning-calls future */
843 ide_set_xfer_rate(drive, xfer_rate);
844 ide_driveid_update(drive);
845 }
846abort:
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100847 if (copy_to_user((void __user *)arg, &args, 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 err = -EFAULT;
Bartlomiej Zolnierkiewicz5a9e77a2008-01-26 20:13:13 +0100849 if (buf) {
850 if (copy_to_user((void __user *)(arg + 4), buf, bufsize))
851 err = -EFAULT;
852 kfree(buf);
853 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 return err;
855}
856
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
858{
859 void __user *p = (void __user *)arg;
860 int err = 0;
Bartlomiej Zolnierkiewicz14b89ef2008-01-25 22:17:11 +0100861 u8 args[7];
862 ide_task_t task;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
864 if (copy_from_user(args, p, 7))
865 return -EFAULT;
Bartlomiej Zolnierkiewicz14b89ef2008-01-25 22:17:11 +0100866
867 memset(&task, 0, sizeof(task));
868 memcpy(&task.tf_array[7], &args[1], 6);
869 task.tf.command = args[0];
Bartlomiej Zolnierkiewicz657cc1a2008-01-26 20:13:10 +0100870 task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
Bartlomiej Zolnierkiewicz14b89ef2008-01-25 22:17:11 +0100871
872 err = ide_no_data_taskfile(drive, &task);
873
874 args[0] = task.tf.command;
875 memcpy(&args[1], &task.tf_array[7], 6);
876
877 if (copy_to_user(p, args, 7))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 err = -EFAULT;
Bartlomiej Zolnierkiewicz14b89ef2008-01-25 22:17:11 +0100879
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 return err;
881}