blob: 8510e66c199c6c9913b4aa48e516d4189f130495 [file] [log] [blame]
/*
* linux/drivers/block/ide-features.c Version 0.04 June 9, 2000
*
* Copyright (C) 1999-2000 Linus Torvalds & authors (see below)
*
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
*
* Extracts if ide.c to address the evolving transfer rate code for
* the SETFEATURES_XFER callouts. Various parts of any given function
* are credited to previous ATA-IDE maintainers.
*
* Auto-CRC downgrade for Ultra DMA(ing)
*
* May be copied or modified under the terms of the GNU General Public License
*/
#include <linux/config.h>
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/genhd.h>
#include <linux/blkpg.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/bitops.h>
/*
* A Verbose noise maker for debugging on the attempted transfer rates.
*/
char *ide_xfer_verbose (byte xfer_rate)
{
switch(xfer_rate) {
case XFER_UDMA_7: return("UDMA 7");
case XFER_UDMA_6: return("UDMA 6");
case XFER_UDMA_5: return("UDMA 5");
case XFER_UDMA_4: return("UDMA 4");
case XFER_UDMA_3: return("UDMA 3");
case XFER_UDMA_2: return("UDMA 2");
case XFER_UDMA_1: return("UDMA 1");
case XFER_UDMA_0: return("UDMA 0");
case XFER_MW_DMA_2: return("MW DMA 2");
case XFER_MW_DMA_1: return("MW DMA 1");
case XFER_MW_DMA_0: return("MW DMA 0");
case XFER_SW_DMA_2: return("SW DMA 2");
case XFER_SW_DMA_1: return("SW DMA 1");
case XFER_SW_DMA_0: return("SW DMA 0");
case XFER_PIO_4: return("PIO 4");
case XFER_PIO_3: return("PIO 3");
case XFER_PIO_2: return("PIO 2");
case XFER_PIO_1: return("PIO 1");
case XFER_PIO_0: return("PIO 0");
case XFER_PIO_SLOW: return("PIO SLOW");
default: return("XFER ERROR");
}
}
/*
*
*/
char *ide_media_verbose (ide_drive_t *drive)
{
switch (drive->media) {
case ide_scsi: return("scsi ");
case ide_disk: return("disk ");
case ide_optical: return("optical");
case ide_cdrom: return("cdrom ");
case ide_tape: return("tape ");
case ide_floppy: return("floppy ");
default: return("???????");
}
}
/*
* A Verbose noise maker for debugging on the attempted dmaing calls.
*/
char *ide_dmafunc_verbose (ide_dma_action_t dmafunc)
{
switch (dmafunc) {
case ide_dma_read: return("ide_dma_read");
case ide_dma_write: return("ide_dma_write");
case ide_dma_begin: return("ide_dma_begin");
case ide_dma_end: return("ide_dma_end:");
case ide_dma_check: return("ide_dma_check");
case ide_dma_on: return("ide_dma_on");
case ide_dma_off: return("ide_dma_off");
case ide_dma_off_quietly: return("ide_dma_off_quietly");
case ide_dma_test_irq: return("ide_dma_test_irq");
case ide_dma_bad_drive: return("ide_dma_bad_drive");
case ide_dma_good_drive: return("ide_dma_good_drive");
case ide_dma_verbose: return("ide_dma_verbose");
case ide_dma_retune: return("ide_dma_retune");
case ide_dma_lostirq: return("ide_dma_lostirq");
case ide_dma_timeout: return("ide_dma_timeout");
default: return("unknown");
}
}
/*
*
*/
byte ide_auto_reduce_xfer (ide_drive_t *drive)
{
if (!drive->crc_count)
return drive->current_speed;
drive->crc_count = 0;
switch(drive->current_speed) {
case XFER_UDMA_7: return XFER_UDMA_6;
case XFER_UDMA_6: return XFER_UDMA_5;
case XFER_UDMA_5: return XFER_UDMA_4;
case XFER_UDMA_4: return XFER_UDMA_3;
case XFER_UDMA_3: return XFER_UDMA_2;
case XFER_UDMA_2: return XFER_UDMA_1;
case XFER_UDMA_1: return XFER_UDMA_0;
/*
* OOPS we do not goto non Ultra DMA modes
* without iCRC's available we force
* the system to PIO and make the user
* invoke the ATA-1 ATA-2 DMA modes.
*/
case XFER_UDMA_0:
default: return XFER_PIO_4;
}
}
/*
* Update the
*/
int ide_driveid_update (ide_drive_t *drive)
{
/*
* Re-read drive->id for possible DMA mode
* change (copied from ide-probe.c)
*/
struct hd_driveid *id;
unsigned long timeout, flags;
SELECT_MASK(HWIF(drive), drive, 1);
if (IDE_CONTROL_REG)
OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
ide_delay_50ms();
OUT_BYTE(WIN_IDENTIFY, IDE_COMMAND_REG);
timeout = jiffies + WAIT_WORSTCASE;
do {
if (0 < (signed long)(jiffies - timeout)) {
SELECT_MASK(HWIF(drive), drive, 0);
return 0; /* drive timed-out */
}
ide_delay_50ms(); /* give drive a breather */
} while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT);
ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */
if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
SELECT_MASK(HWIF(drive), drive, 0);
printk("%s: CHECK for good STATUS\n", drive->name);
return 0;
}
__save_flags(flags); /* local CPU only */
__cli(); /* local CPU only; some systems need this */
SELECT_MASK(HWIF(drive), drive, 0);
id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC);
if (!id) {
__restore_flags(flags); /* local CPU only */
return 0;
}
ata_input_data(drive, id, SECTOR_WORDS);
(void) GET_STAT(); /* clear drive IRQ */
ide__sti(); /* local CPU only */
__restore_flags(flags); /* local CPU only */
ide_fix_driveid(id);
if (id) {
drive->id->dma_ultra = id->dma_ultra;
drive->id->dma_mword = id->dma_mword;
drive->id->dma_1word = id->dma_1word;
/* anything more ? */
kfree(id);
}
return 1;
}
/*
* Verify that we are doing an approved SETFEATURES_XFER with respect
* to the hardware being able to support request. Since some hardware
* can improperly report capabilties, we check to see if the host adapter
* in combination with the device (usually a disk) properly detect
* and acknowledge each end of the ribbon.
*/
int ide_ata66_check (ide_drive_t *drive, ide_task_t *args)
{
if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) &&
(args->tfRegister[IDE_SECTOR_OFFSET] > XFER_UDMA_2) &&
(args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER)) {
if (!HWIF(drive)->udma_four) {
printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", HWIF(drive)->name);
return 1;
}
#ifndef CONFIG_IDEDMA_IVB
if ((drive->id->hw_config & 0x6000) == 0) {
#else /* !CONFIG_IDEDMA_IVB */
if (((drive->id->hw_config & 0x2000) == 0) ||
((drive->id->hw_config & 0x4000) == 0)) {
#endif /* CONFIG_IDEDMA_IVB */
printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", drive->name);
return 1;
}
}
return 0;
}
/*
* Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER.
* 1 : Safe to update drive->id DMA registers.
* 0 : OOPs not allowed.
*/
int set_transfer (ide_drive_t *drive, ide_task_t *args)
{
if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) &&
(args->tfRegister[IDE_SECTOR_OFFSET] >= XFER_SW_DMA_0) &&
(args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER) &&
(drive->id->dma_ultra ||
drive->id->dma_mword ||
drive->id->dma_1word))
return 1;
return 0;
}
/*
* All hosts that use the 80c ribbon mus use!
*/
byte eighty_ninty_three (ide_drive_t *drive)
{
return ((byte) ((HWIF(drive)->udma_four) &&
#ifndef CONFIG_IDEDMA_IVB
(drive->id->hw_config & 0x4000) &&
#endif /* CONFIG_IDEDMA_IVB */
(drive->id->hw_config & 0x6000)) ? 1 : 0);
}
/*
* Similar to ide_wait_stat(), except it never calls ide_error internally.
* This is a kludge to handle the new ide_config_drive_speed() function,
* and should not otherwise be used anywhere. Eventually, the tuneproc's
* should be updated to return ide_startstop_t, in which case we can get
* rid of this abomination again. :) -ml
*
* It is gone..........
*
* const char *msg == consider adding for verbose errors.
*/
int ide_config_drive_speed (ide_drive_t *drive, byte speed)
{
ide_hwif_t *hwif = HWIF(drive);
int i, error = 1;
byte stat;
#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
byte unit = (drive->select.b.unit & 0x01);
outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */
/*
* Don't use ide_wait_cmd here - it will
* attempt to set_geometry and recalibrate,
* but for some reason these don't work at
* this point (lost interrupt).
*/
/*
* Select the drive, and issue the SETFEATURES command
*/
disable_irq(hwif->irq); /* disable_irq_nosync ?? */
udelay(1);
SELECT_DRIVE(HWIF(drive), drive);
SELECT_MASK(HWIF(drive), drive, 0);
udelay(1);
if (IDE_CONTROL_REG)
OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
OUT_BYTE(speed, IDE_NSECTOR_REG);
OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
if ((IDE_CONTROL_REG) && (drive->quirk_list == 2))
OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
udelay(1);
/*
* Wait for drive to become non-BUSY
*/
if ((stat = GET_STAT()) & BUSY_STAT) {
unsigned long flags, timeout;
__save_flags(flags); /* local CPU only */
ide__sti(); /* local CPU only -- for jiffies */
timeout = jiffies + WAIT_CMD;
while ((stat = GET_STAT()) & BUSY_STAT) {
if (0 < (signed long)(jiffies - timeout))
break;
}
__restore_flags(flags); /* local CPU only */
}
/*
* Allow status to settle, then read it again.
* A few rare drives vastly violate the 400ns spec here,
* so we'll wait up to 10usec for a "good" status
* rather than expensively fail things immediately.
* This fix courtesy of Matthew Faupel & Niccolo Rigacci.
*/
for (i = 0; i < 10; i++) {
udelay(1);
if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) {
error = 0;
break;
}
}
SELECT_MASK(HWIF(drive), drive, 0);
enable_irq(hwif->irq);
if (error) {
(void) ide_dump_status(drive, "set_drive_speed_status", stat);
return error;
}
drive->id->dma_ultra &= ~0xFF00;
drive->id->dma_mword &= ~0x0F00;
drive->id->dma_1word &= ~0x0F00;
#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
if (speed > XFER_PIO_4) {
outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2);
} else {
outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
}
#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */
switch(speed) {
case XFER_UDMA_7: drive->id->dma_ultra |= 0x8080; break;
case XFER_UDMA_6: drive->id->dma_ultra |= 0x4040; break;
case XFER_UDMA_5: drive->id->dma_ultra |= 0x2020; break;
case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break;
case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break;
case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break;
case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break;
case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break;
case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
default: break;
}
return error;
}
EXPORT_SYMBOL(ide_auto_reduce_xfer);
EXPORT_SYMBOL(ide_driveid_update);
EXPORT_SYMBOL(ide_ata66_check);
EXPORT_SYMBOL(set_transfer);
EXPORT_SYMBOL(eighty_ninty_three);
EXPORT_SYMBOL(ide_config_drive_speed);