diff options
author | Tejun Heo <htejun@gmail.com> | 2006-11-14 08:47:10 -0500 |
---|---|---|
committer | Tejun Heo <htejun@gmail.com> | 2006-12-03 03:56:24 -0500 |
commit | bd056d7eeb55d696556ee4f5ee2acb67b2d16df4 (patch) | |
tree | f6ca604abc43d73d42354d977f1bcedd262dbcda /drivers/ata/libata-core.c | |
parent | 2432697ba0ce312d60be5009ffe1fa054a761bb9 (diff) |
[PATCH] libata: separate out rw ATA taskfile building into ata_build_rw_tf()
Separate out rw ATA taskfile building from ata_scsi_rw_xlat() into
ata_build_rw_tf(). This will be used to improve media error handling.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 133 |
1 files changed, 128 insertions, 5 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 0a5103b707c6..4ac53ef0e4b0 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
@@ -199,7 +199,8 @@ static const u8 ata_rw_cmds[] = { | |||
199 | 199 | ||
200 | /** | 200 | /** |
201 | * ata_rwcmd_protocol - set taskfile r/w commands and protocol | 201 | * ata_rwcmd_protocol - set taskfile r/w commands and protocol |
202 | * @qc: command to examine and configure | 202 | * @tf: command to examine and configure |
203 | * @dev: device tf belongs to | ||
203 | * | 204 | * |
204 | * Examine the device configuration and tf->flags to calculate | 205 | * Examine the device configuration and tf->flags to calculate |
205 | * the proper read/write commands and protocol to use. | 206 | * the proper read/write commands and protocol to use. |
@@ -207,10 +208,8 @@ static const u8 ata_rw_cmds[] = { | |||
207 | * LOCKING: | 208 | * LOCKING: |
208 | * caller. | 209 | * caller. |
209 | */ | 210 | */ |
210 | int ata_rwcmd_protocol(struct ata_queued_cmd *qc) | 211 | static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev) |
211 | { | 212 | { |
212 | struct ata_taskfile *tf = &qc->tf; | ||
213 | struct ata_device *dev = qc->dev; | ||
214 | u8 cmd; | 213 | u8 cmd; |
215 | 214 | ||
216 | int index, fua, lba48, write; | 215 | int index, fua, lba48, write; |
@@ -222,7 +221,7 @@ int ata_rwcmd_protocol(struct ata_queued_cmd *qc) | |||
222 | if (dev->flags & ATA_DFLAG_PIO) { | 221 | if (dev->flags & ATA_DFLAG_PIO) { |
223 | tf->protocol = ATA_PROT_PIO; | 222 | tf->protocol = ATA_PROT_PIO; |
224 | index = dev->multi_count ? 0 : 8; | 223 | index = dev->multi_count ? 0 : 8; |
225 | } else if (lba48 && (qc->ap->flags & ATA_FLAG_PIO_LBA48)) { | 224 | } else if (lba48 && (dev->ap->flags & ATA_FLAG_PIO_LBA48)) { |
226 | /* Unable to use DMA due to host limitation */ | 225 | /* Unable to use DMA due to host limitation */ |
227 | tf->protocol = ATA_PROT_PIO; | 226 | tf->protocol = ATA_PROT_PIO; |
228 | index = dev->multi_count ? 0 : 8; | 227 | index = dev->multi_count ? 0 : 8; |
@@ -283,6 +282,130 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) | |||
283 | } | 282 | } |
284 | 283 | ||
285 | /** | 284 | /** |
285 | * ata_build_rw_tf - Build ATA taskfile for given read/write request | ||
286 | * @tf: Target ATA taskfile | ||
287 | * @dev: ATA device @tf belongs to | ||
288 | * @block: Block address | ||
289 | * @n_block: Number of blocks | ||
290 | * @tf_flags: RW/FUA etc... | ||
291 | * @tag: tag | ||
292 | * | ||
293 | * LOCKING: | ||
294 | * None. | ||
295 | * | ||
296 | * Build ATA taskfile @tf for read/write request described by | ||
297 | * @block, @n_block, @tf_flags and @tag on @dev. | ||
298 | * | ||
299 | * RETURNS: | ||
300 | * | ||
301 | * 0 on success, -ERANGE if the request is too large for @dev, | ||
302 | * -EINVAL if the request is invalid. | ||
303 | */ | ||
304 | int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, | ||
305 | u64 block, u32 n_block, unsigned int tf_flags, | ||
306 | unsigned int tag) | ||
307 | { | ||
308 | tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; | ||
309 | tf->flags |= tf_flags; | ||
310 | |||
311 | if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ_OFF | | ||
312 | ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) { | ||
313 | /* yay, NCQ */ | ||
314 | if (!lba_48_ok(block, n_block)) | ||
315 | return -ERANGE; | ||
316 | |||
317 | tf->protocol = ATA_PROT_NCQ; | ||
318 | tf->flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48; | ||
319 | |||
320 | if (tf->flags & ATA_TFLAG_WRITE) | ||
321 | tf->command = ATA_CMD_FPDMA_WRITE; | ||
322 | else | ||
323 | tf->command = ATA_CMD_FPDMA_READ; | ||
324 | |||
325 | tf->nsect = tag << 3; | ||
326 | tf->hob_feature = (n_block >> 8) & 0xff; | ||
327 | tf->feature = n_block & 0xff; | ||
328 | |||
329 | tf->hob_lbah = (block >> 40) & 0xff; | ||
330 | tf->hob_lbam = (block >> 32) & 0xff; | ||
331 | tf->hob_lbal = (block >> 24) & 0xff; | ||
332 | tf->lbah = (block >> 16) & 0xff; | ||
333 | tf->lbam = (block >> 8) & 0xff; | ||
334 | tf->lbal = block & 0xff; | ||
335 | |||
336 | tf->device = 1 << 6; | ||
337 | if (tf->flags & ATA_TFLAG_FUA) | ||
338 | tf->device |= 1 << 7; | ||
339 | } else if (dev->flags & ATA_DFLAG_LBA) { | ||
340 | tf->flags |= ATA_TFLAG_LBA; | ||
341 | |||
342 | if (lba_28_ok(block, n_block)) { | ||
343 | /* use LBA28 */ | ||
344 | tf->device |= (block >> 24) & 0xf; | ||
345 | } else if (lba_48_ok(block, n_block)) { | ||
346 | if (!(dev->flags & ATA_DFLAG_LBA48)) | ||
347 | return -ERANGE; | ||
348 | |||
349 | /* use LBA48 */ | ||
350 | tf->flags |= ATA_TFLAG_LBA48; | ||
351 | |||
352 | tf->hob_nsect = (n_block >> 8) & 0xff; | ||
353 | |||
354 | tf->hob_lbah = (block >> 40) & 0xff; | ||
355 | tf->hob_lbam = (block >> 32) & 0xff; | ||
356 | tf->hob_lbal = (block >> 24) & 0xff; | ||
357 | } else | ||
358 | /* request too large even for LBA48 */ | ||
359 | return -ERANGE; | ||
360 | |||
361 | if (unlikely(ata_rwcmd_protocol(tf, dev) < 0)) | ||
362 | return -EINVAL; | ||
363 | |||
364 | tf->nsect = n_block & 0xff; | ||
365 | |||
366 | tf->lbah = (block >> 16) & 0xff; | ||
367 | tf->lbam = (block >> 8) & 0xff; | ||
368 | tf->lbal = block & 0xff; | ||
369 | |||
370 | tf->device |= ATA_LBA; | ||
371 | } else { | ||
372 | /* CHS */ | ||
373 | u32 sect, head, cyl, track; | ||
374 | |||
375 | /* The request -may- be too large for CHS addressing. */ | ||
376 | if (!lba_28_ok(block, n_block)) | ||
377 | return -ERANGE; | ||
378 | |||
379 | if (unlikely(ata_rwcmd_protocol(tf, dev) < 0)) | ||
380 | return -EINVAL; | ||
381 | |||
382 | /* Convert LBA to CHS */ | ||
383 | track = (u32)block / dev->sectors; | ||
384 | cyl = track / dev->heads; | ||
385 | head = track % dev->heads; | ||
386 | sect = (u32)block % dev->sectors + 1; | ||
387 | |||
388 | DPRINTK("block %u track %u cyl %u head %u sect %u\n", | ||
389 | (u32)block, track, cyl, head, sect); | ||
390 | |||
391 | /* Check whether the converted CHS can fit. | ||
392 | Cylinder: 0-65535 | ||
393 | Head: 0-15 | ||
394 | Sector: 1-255*/ | ||
395 | if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) | ||
396 | return -ERANGE; | ||
397 | |||
398 | tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */ | ||
399 | tf->lbal = sect; | ||
400 | tf->lbam = cyl; | ||
401 | tf->lbah = cyl >> 8; | ||
402 | tf->device |= head; | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | /** | ||
286 | * ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask | 409 | * ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask |
287 | * @pio_mask: pio_mask | 410 | * @pio_mask: pio_mask |
288 | * @mwdma_mask: mwdma_mask | 411 | * @mwdma_mask: mwdma_mask |