Files
linux-apfs/drivers/ide/ide-dma.c
T

502 lines
12 KiB
C
Raw Normal View History

2005-04-16 15:20:36 -07:00
/*
* IDE DMA support (including IDE PCI BM-DMA).
*
2008-02-01 23:09:33 +01:00
* Copyright (C) 1995-1998 Mark Lord
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
* Copyright (C) 2004, 2007 Bartlomiej Zolnierkiewicz
*
2005-04-16 15:20:36 -07:00
* May be copied or modified under the terms of the GNU General Public License
*
* DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
2005-04-16 15:20:36 -07:00
*/
/*
* Special Thanks to Mark for his Six years of work.
*/
/*
* Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
* fixing the problem with the BIOS on some Acer motherboards.
*
* Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
* "TX" chipset compatibility and for providing patches for the "TX" chipset.
*
* Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
* at generic DMA -- his patches were referred to when preparing this code.
*
* Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
* for supplying a Promise UDMA board & WD UDMA drive for this work!
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/scatterlist.h>
2008-02-01 23:09:32 +01:00
#include <linux/dma-mapping.h>
2005-04-16 15:20:36 -07:00
2008-10-13 21:39:47 +02:00
static const struct drive_list_entry drive_whitelist[] = {
{ "Micropolis 2112A" , NULL },
{ "CONNER CTMA 4000" , NULL },
{ "CONNER CTT8000-A" , NULL },
{ "ST34342A" , NULL },
2005-04-16 15:20:36 -07:00
{ NULL , NULL }
};
2008-10-13 21:39:47 +02:00
static const struct drive_list_entry drive_blacklist[] = {
{ "WDC AC11000H" , NULL },
{ "WDC AC22100H" , NULL },
{ "WDC AC32500H" , NULL },
{ "WDC AC33100H" , NULL },
{ "WDC AC31600H" , NULL },
2005-04-16 15:20:36 -07:00
{ "WDC AC32100H" , "24.09P07" },
{ "WDC AC23200L" , "21.10N21" },
{ "Compaq CRD-8241B" , NULL },
{ "CRD-8400B" , NULL },
{ "CRD-8480B", NULL },
{ "CRD-8482B", NULL },
{ "CRD-84" , NULL },
{ "SanDisk SDP3B" , NULL },
{ "SanDisk SDP3B-64" , NULL },
{ "SANYO CD-ROM CRD" , NULL },
{ "HITACHI CDR-8" , NULL },
{ "HITACHI CDR-8335" , NULL },
{ "HITACHI CDR-8435" , NULL },
{ "Toshiba CD-ROM XM-6202B" , NULL },
{ "TOSHIBA CD-ROM XM-1702BC", NULL },
{ "CD-532E-A" , NULL },
{ "E-IDE CD-ROM CR-840", NULL },
{ "CD-ROM Drive/F5A", NULL },
{ "WPI CDD-820", NULL },
{ "SAMSUNG CD-ROM SC-148C", NULL },
{ "SAMSUNG CD-ROM SC", NULL },
{ "ATAPI CD-ROM DRIVE 40X MAXIMUM", NULL },
{ "_NEC DV5800A", NULL },
{ "SAMSUNG CD-ROM SN-124", "N001" },
{ "Seagate STT20000A", NULL },
{ "CD-ROM CDR_U200", "1.09" },
2005-04-16 15:20:36 -07:00
{ NULL , NULL }
};
/**
* ide_dma_intr - IDE DMA interrupt handler
* @drive: the drive the interrupt is for
*
2008-10-13 21:39:47 +02:00
* Handle an interrupt completing a read/write DMA transfer on an
2005-04-16 15:20:36 -07:00
* IDE device
*/
2008-10-13 21:39:47 +02:00
ide_startstop_t ide_dma_intr(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
2008-07-23 19:55:52 +02:00
ide_hwif_t *hwif = drive->hwif;
2005-04-16 15:20:36 -07:00
u8 stat = 0, dma_stat = 0;
2008-07-23 19:55:52 +02:00
dma_stat = hwif->dma_ops->dma_end(drive);
2008-07-23 19:55:56 +02:00
stat = hwif->tp_ops->read_status(hwif);
if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | ATA_DRQ)) {
2005-04-16 15:20:36 -07:00
if (!dma_stat) {
struct request *rq = hwif->rq;
2005-04-16 15:20:36 -07:00
2008-01-26 20:13:11 +01:00
task_end_request(drive, rq, stat);
2005-04-16 15:20:36 -07:00
return ide_stopped;
}
2008-10-13 21:39:47 +02:00
printk(KERN_ERR "%s: %s: bad DMA status (0x%02x)\n",
drive->name, __func__, dma_stat);
2005-04-16 15:20:36 -07:00
}
return ide_error(drive, "dma_intr", stat);
}
EXPORT_SYMBOL_GPL(ide_dma_intr);
2008-10-13 21:39:47 +02:00
int ide_dma_good_drive(ide_drive_t *drive)
2007-10-13 17:47:50 +02:00
{
return ide_in_drive_list(drive->id, drive_whitelist);
}
2005-04-16 15:20:36 -07:00
/**
* ide_build_sglist - map IDE scatter gather for DMA I/O
* @drive: the drive to build the DMA table for
* @rq: the request holding the sg list
*
2008-02-01 23:09:32 +01:00
* Perform the DMA mapping magic necessary to access the source or
* target buffers of a request via DMA. The lower layers of the
2005-04-16 15:20:36 -07:00
* kernel provide the necessary cache management so that we can
2008-02-01 23:09:32 +01:00
* operate in a portable fashion.
2005-04-16 15:20:36 -07:00
*/
int ide_build_sglist(ide_drive_t *drive, struct request *rq)
{
2008-10-13 21:39:47 +02:00
ide_hwif_t *hwif = drive->hwif;
2005-04-16 15:20:36 -07:00
struct scatterlist *sg = hwif->sg_table;
ide_map_sg(drive, rq);
if (rq_data_dir(rq) == READ)
2008-02-01 23:09:32 +01:00
hwif->sg_dma_direction = DMA_FROM_DEVICE;
2005-04-16 15:20:36 -07:00
else
2008-02-01 23:09:32 +01:00
hwif->sg_dma_direction = DMA_TO_DEVICE;
2005-04-16 15:20:36 -07:00
2008-02-01 23:09:32 +01:00
return dma_map_sg(hwif->dev, sg, hwif->sg_nents,
hwif->sg_dma_direction);
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL_GPL(ide_build_sglist);
/**
* ide_destroy_dmatable - clean up DMA mapping
* @drive: The drive to unmap
*
* Teardown mappings after DMA has completed. This must be called
* after the completion of each use of ide_build_dmatable and before
* the next use of ide_build_dmatable. Failure to do so will cause
* an oops as only one mapping can be live for each target at a given
* time.
*/
2008-10-13 21:39:47 +02:00
void ide_destroy_dmatable(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
ide_hwif_t *hwif = drive->hwif;
2005-04-16 15:20:36 -07:00
2008-02-01 23:09:32 +01:00
dma_unmap_sg(hwif->dev, hwif->sg_table, hwif->sg_nents,
hwif->sg_dma_direction);
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL_GPL(ide_destroy_dmatable);
/**
* ide_dma_off_quietly - Generic DMA kill
2005-04-16 15:20:36 -07:00
* @drive: drive to control
*
2008-10-13 21:39:47 +02:00
* Turn off the current DMA on this IDE controller.
2005-04-16 15:20:36 -07:00
*/
void ide_dma_off_quietly(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
2008-10-13 21:39:36 +02:00
drive->dev_flags &= ~IDE_DFLAG_USING_DMA;
2005-04-16 15:20:36 -07:00
ide_toggle_bounce(drive, 0);
2008-04-26 22:25:24 +02:00
drive->hwif->dma_ops->dma_host_set(drive, 0);
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL(ide_dma_off_quietly);
2005-04-16 15:20:36 -07:00
/**
* ide_dma_off - disable DMA on a device
2005-04-16 15:20:36 -07:00
* @drive: drive to disable DMA on
*
* Disable IDE DMA for a device on this IDE controller.
* Inform the user that DMA has been disabled.
*/
void ide_dma_off(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
printk(KERN_INFO "%s: DMA disabled\n", drive->name);
ide_dma_off_quietly(drive);
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL(ide_dma_off);
2005-04-16 15:20:36 -07:00
/**
* ide_dma_on - Enable DMA on a device
2005-04-16 15:20:36 -07:00
* @drive: drive to enable DMA on
*
* Enable IDE DMA for a device on this IDE controller.
*/
void ide_dma_on(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
2008-10-13 21:39:36 +02:00
drive->dev_flags |= IDE_DFLAG_USING_DMA;
2005-04-16 15:20:36 -07:00
ide_toggle_bounce(drive, 1);
2008-04-26 22:25:24 +02:00
drive->hwif->dma_ops->dma_host_set(drive, 1);
2005-04-16 15:20:36 -07:00
}
2008-10-13 21:39:47 +02:00
int __ide_dma_bad_drive(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
2008-10-10 22:39:19 +02:00
u16 *id = drive->id;
2005-04-16 15:20:36 -07:00
2005-12-15 02:16:18 +01:00
int blacklist = ide_in_drive_list(id, drive_blacklist);
2005-04-16 15:20:36 -07:00
if (blacklist) {
printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n",
2008-10-10 22:39:19 +02:00
drive->name, (char *)&id[ATA_ID_PROD]);
2005-04-16 15:20:36 -07:00
return blacklist;
}
return 0;
}
EXPORT_SYMBOL(__ide_dma_bad_drive);
static const u8 xfer_mode_bases[] = {
XFER_UDMA_0,
XFER_MW_DMA_0,
XFER_SW_DMA_0,
};
static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode)
{
2008-10-10 22:39:19 +02:00
u16 *id = drive->id;
ide_hwif_t *hwif = drive->hwif;
2008-04-26 22:25:14 +02:00
const struct ide_port_ops *port_ops = hwif->port_ops;
unsigned int mask = 0;
2008-10-13 21:39:47 +02:00
switch (base) {
case XFER_UDMA_0:
2008-10-10 22:39:19 +02:00
if ((id[ATA_ID_FIELD_VALID] & 4) == 0)
break;
2008-04-26 22:25:14 +02:00
if (port_ops && port_ops->udma_filter)
mask = port_ops->udma_filter(drive);
else
mask = hwif->ultra_mask;
2008-10-10 22:39:19 +02:00
mask &= id[ATA_ID_UDMA_MODES];
/*
* avoid false cable warning from eighty_ninty_three()
*/
if (req_mode > XFER_UDMA_2) {
if ((mask & 0x78) && (eighty_ninty_three(drive) == 0))
mask &= 0x07;
}
break;
case XFER_MW_DMA_0:
2008-10-10 22:39:19 +02:00
if ((id[ATA_ID_FIELD_VALID] & 2) == 0)
break;
2008-04-26 22:25:14 +02:00
if (port_ops && port_ops->mdma_filter)
mask = port_ops->mdma_filter(drive);
else
mask = hwif->mwdma_mask;
2008-10-10 22:39:19 +02:00
mask &= id[ATA_ID_MWDMA_MODES];
break;
case XFER_SW_DMA_0:
2008-10-10 22:39:19 +02:00
if (id[ATA_ID_FIELD_VALID] & 2) {
mask = id[ATA_ID_SWDMA_MODES] & hwif->swdma_mask;
2008-10-10 22:39:19 +02:00
} else if (id[ATA_ID_OLD_DMA_MODES] >> 8) {
u8 mode = id[ATA_ID_OLD_DMA_MODES] >> 8;
2007-07-09 23:17:57 +02:00
/*
* if the mode is valid convert it to the mask
* (the maximum allowed mode is XFER_SW_DMA_2)
*/
if (mode <= 2)
mask = ((2 << mode) - 1) & hwif->swdma_mask;
}
break;
default:
BUG();
break;
}
return mask;
}
/**
* ide_find_dma_mode - compute DMA speed
* @drive: IDE device
* @req_mode: requested mode
*
* Checks the drive/host capabilities and finds the speed to use for
* the DMA transfer. The speed is then limited by the requested mode.
*
* Returns 0 if the drive/host combination is incapable of DMA transfers
* or if the requested mode is not a DMA mode.
*/
u8 ide_find_dma_mode(ide_drive_t *drive, u8 req_mode)
{
ide_hwif_t *hwif = drive->hwif;
unsigned int mask;
int x, i;
u8 mode = 0;
2007-10-19 00:30:06 +02:00
if (drive->media != ide_disk) {
if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA)
return 0;
}
for (i = 0; i < ARRAY_SIZE(xfer_mode_bases); i++) {
if (req_mode < xfer_mode_bases[i])
continue;
mask = ide_get_mode_mask(drive, xfer_mode_bases[i], req_mode);
x = fls(mask) - 1;
if (x >= 0) {
mode = xfer_mode_bases[i] + x;
break;
}
}
2007-10-13 17:47:50 +02:00
if (hwif->chipset == ide_acorn && mode == 0) {
/*
* is this correct?
*/
2008-10-10 22:39:19 +02:00
if (ide_dma_good_drive(drive) &&
drive->id[ATA_ID_EIDE_DMA_TIME] < 150)
2007-10-13 17:47:50 +02:00
mode = XFER_MW_DMA_1;
}
mode = min(mode, req_mode);
printk(KERN_INFO "%s: %s mode selected\n", drive->name,
mode ? ide_xfer_verbose(mode) : "no DMA");
return mode;
}
EXPORT_SYMBOL_GPL(ide_find_dma_mode);
2007-10-16 22:29:55 +02:00
static int ide_tune_dma(ide_drive_t *drive)
2007-05-10 00:01:09 +02:00
{
2008-01-26 20:13:00 +01:00
ide_hwif_t *hwif = drive->hwif;
2007-05-10 00:01:09 +02:00
u8 speed;
2008-10-13 21:39:36 +02:00
if (ata_id_has_dma(drive->id) == 0 ||
(drive->dev_flags & IDE_DFLAG_NODMA))
2007-05-16 00:51:46 +02:00
return 0;
/* consult the list of known "bad" drives */
if (__ide_dma_bad_drive(drive))
2007-05-10 00:01:09 +02:00
return 0;
if (ide_id_dma_bug(drive))
return 0;
2008-01-26 20:13:00 +01:00
if (hwif->host_flags & IDE_HFLAG_TRUST_BIOS_FOR_DMA)
2007-10-16 22:29:55 +02:00
return config_drive_for_dma(drive);
2007-05-10 00:01:09 +02:00
speed = ide_max_dma_mode(drive);
2008-04-26 17:36:38 +02:00
if (!speed)
return 0;
2007-05-10 00:01:09 +02:00
if (ide_set_dma_mode(drive, speed))
2007-05-16 00:51:46 +02:00
return 0;
2007-05-10 00:01:09 +02:00
2007-05-16 00:51:46 +02:00
return 1;
2007-05-10 00:01:09 +02:00
}
2007-10-16 22:29:55 +02:00
static int ide_dma_check(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
2008-07-23 19:55:55 +02:00
if (ide_tune_dma(drive))
2007-10-16 22:29:55 +02:00
return 0;
/* TODO: always do PIO fallback */
if (hwif->host_flags & IDE_HFLAG_TRUST_BIOS_FOR_DMA)
return -1;
ide_set_max_pio(drive);
2008-07-23 19:55:55 +02:00
return -1;
2007-10-16 22:29:55 +02:00
}
int ide_id_dma_bug(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
2008-10-10 22:39:19 +02:00
u16 *id = drive->id;
2005-04-16 15:20:36 -07:00
2008-10-10 22:39:19 +02:00
if (id[ATA_ID_FIELD_VALID] & 4) {
if ((id[ATA_ID_UDMA_MODES] >> 8) &&
(id[ATA_ID_MWDMA_MODES] >> 8))
goto err_out;
2008-10-10 22:39:19 +02:00
} else if (id[ATA_ID_FIELD_VALID] & 2) {
if ((id[ATA_ID_MWDMA_MODES] >> 8) &&
(id[ATA_ID_SWDMA_MODES] >> 8))
goto err_out;
2005-04-16 15:20:36 -07:00
}
return 0;
err_out:
printk(KERN_ERR "%s: bad DMA info in identify block\n", drive->name);
return 1;
2005-04-16 15:20:36 -07:00
}
2007-02-17 02:40:26 +01:00
int ide_set_dma(ide_drive_t *drive)
{
int rc;
2008-01-25 22:17:19 +01:00
/*
* Force DMAing for the beginning of the check.
* Some chipsets appear to do interesting
* things, if not checked and cleared.
* PARANOIA!!!
*/
ide_dma_off_quietly(drive);
2008-01-25 22:17:19 +01:00
2007-10-16 22:29:55 +02:00
rc = ide_dma_check(drive);
2008-01-25 22:17:19 +01:00
if (rc)
return rc;
2007-02-17 02:40:26 +01:00
ide_dma_on(drive);
return 0;
2007-02-17 02:40:26 +01:00
}
2008-02-02 19:56:47 +01:00
void ide_check_dma_crc(ide_drive_t *drive)
{
u8 mode;
ide_dma_off_quietly(drive);
drive->crc_count = 0;
mode = drive->current_speed;
/*
* Don't try non Ultra-DMA modes without iCRC's. Force the
* device to PIO and make the user enable SWDMA/MWDMA modes.
*/
if (mode > XFER_UDMA_0 && mode <= XFER_UDMA_7)
mode--;
else
mode = XFER_PIO_4;
ide_set_xfer_rate(drive, mode);
if (drive->current_speed >= XFER_SW_DMA_0)
ide_dma_on(drive);
}
void ide_dma_lost_irq(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
printk(KERN_ERR "%s: DMA interrupt recovery\n", drive->name);
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL_GPL(ide_dma_lost_irq);
2005-04-16 15:20:36 -07:00
void ide_dma_timeout(ide_drive_t *drive)
2005-04-16 15:20:36 -07:00
{
2008-10-13 21:39:47 +02:00
ide_hwif_t *hwif = drive->hwif;
2005-04-16 15:20:36 -07:00
printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name);
2008-04-26 22:25:24 +02:00
if (hwif->dma_ops->dma_test_irq(drive))
return;
ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif));
2008-04-26 22:25:24 +02:00
hwif->dma_ops->dma_end(drive);
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL_GPL(ide_dma_timeout);
2005-04-16 15:20:36 -07:00
void ide_release_dma_engine(ide_hwif_t *hwif)
2005-04-16 15:20:36 -07:00
{
if (hwif->dmatable_cpu) {
2008-10-13 21:39:47 +02:00
int prd_size = hwif->prd_max_nents * hwif->prd_ent_size;
2008-10-13 21:39:47 +02:00
dma_free_coherent(hwif->dev, prd_size,
hwif->dmatable_cpu, hwif->dmatable_dma);
2005-04-16 15:20:36 -07:00
hwif->dmatable_cpu = NULL;
}
}
2008-10-13 21:39:47 +02:00
EXPORT_SYMBOL_GPL(ide_release_dma_engine);
2005-04-16 15:20:36 -07:00
2008-04-26 22:25:21 +02:00
int ide_allocate_dma_engine(ide_hwif_t *hwif)
2005-04-16 15:20:36 -07:00
{
2008-10-13 21:39:47 +02:00
int prd_size;
2008-10-13 21:39:47 +02:00
if (hwif->prd_max_nents == 0)
hwif->prd_max_nents = PRD_ENTRIES;
if (hwif->prd_ent_size == 0)
hwif->prd_ent_size = PRD_BYTES;
2005-04-16 15:20:36 -07:00
2008-10-13 21:39:47 +02:00
prd_size = hwif->prd_max_nents * hwif->prd_ent_size;
2005-04-16 15:20:36 -07:00
2008-10-13 21:39:47 +02:00
hwif->dmatable_cpu = dma_alloc_coherent(hwif->dev, prd_size,
&hwif->dmatable_dma,
GFP_ATOMIC);
if (hwif->dmatable_cpu == NULL) {
printk(KERN_ERR "%s: unable to allocate PRD table\n",
hwif->name);
2008-10-13 21:39:47 +02:00
return -ENOMEM;
}
2005-04-16 15:20:36 -07:00
2008-10-13 21:39:47 +02:00
return 0;
2005-04-16 15:20:36 -07:00
}
2008-04-26 22:25:21 +02:00
EXPORT_SYMBOL_GPL(ide_allocate_dma_engine);