mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-26 16:30:55 +07:00
3b2a5c7149
* Filter out "default" transfer mode values (0x00 - default PIO mode, 0x01 - default PIO mode w/ IORDY disabled) in write handler for obsoleted /proc/ide/hd?/settings:current_speed setting. Allowing "default" transfer mode values is a dangerous thing to do as we don't support programming controller to the "default" transfer mode and devices often use different values for the default and maximum PIO mode (i.e. PIO2 default and PIO4 maximum) so the controller will stay programmed for higher PIO mode while device will use the lower PIO mode. There is no functionality loss as by using special IOCTLs device can still be programmed to "default" transfer modes (it is only useful for debugging/testing purposes anyway). * Remove no longer needed IDE_HFLAG_ABUSE_SET_DMA_MODE host flag, it was previously used by few host drivers to program the controller to PIO0 timings for "default" transfer mode == 0x01 (although some host drivers would program invalid PIO timings instead). * Cleanup ide_set_xfer_rate() and add BUG_ON(). Suggested-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
415 lines
10 KiB
C
415 lines
10 KiB
C
#include <linux/types.h>
|
|
#include <linux/string.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/hdreg.h>
|
|
#include <linux/ide.h>
|
|
#include <linux/bitops.h>
|
|
|
|
static const char *udma_str[] =
|
|
{ "UDMA/16", "UDMA/25", "UDMA/33", "UDMA/44",
|
|
"UDMA/66", "UDMA/100", "UDMA/133", "UDMA7" };
|
|
static const char *mwdma_str[] =
|
|
{ "MWDMA0", "MWDMA1", "MWDMA2" };
|
|
static const char *swdma_str[] =
|
|
{ "SWDMA0", "SWDMA1", "SWDMA2" };
|
|
static const char *pio_str[] =
|
|
{ "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5" };
|
|
|
|
/**
|
|
* ide_xfer_verbose - return IDE mode names
|
|
* @mode: transfer mode
|
|
*
|
|
* Returns a constant string giving the name of the mode
|
|
* requested.
|
|
*/
|
|
|
|
const char *ide_xfer_verbose(u8 mode)
|
|
{
|
|
const char *s;
|
|
u8 i = mode & 0xf;
|
|
|
|
if (mode >= XFER_UDMA_0 && mode <= XFER_UDMA_7)
|
|
s = udma_str[i];
|
|
else if (mode >= XFER_MW_DMA_0 && mode <= XFER_MW_DMA_2)
|
|
s = mwdma_str[i];
|
|
else if (mode >= XFER_SW_DMA_0 && mode <= XFER_SW_DMA_2)
|
|
s = swdma_str[i];
|
|
else if (mode >= XFER_PIO_0 && mode <= XFER_PIO_5)
|
|
s = pio_str[i & 0x7];
|
|
else if (mode == XFER_PIO_SLOW)
|
|
s = "PIO SLOW";
|
|
else
|
|
s = "XFER ERROR";
|
|
|
|
return s;
|
|
}
|
|
|
|
EXPORT_SYMBOL(ide_xfer_verbose);
|
|
|
|
/**
|
|
* ide_rate_filter - filter transfer mode
|
|
* @drive: IDE device
|
|
* @speed: desired speed
|
|
*
|
|
* Given the available transfer modes this function returns
|
|
* the best available speed at or below the speed requested.
|
|
*
|
|
* TODO: check device PIO capabilities
|
|
*/
|
|
|
|
static u8 ide_rate_filter(ide_drive_t *drive, u8 speed)
|
|
{
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
u8 mode = ide_find_dma_mode(drive, speed);
|
|
|
|
if (mode == 0) {
|
|
if (hwif->pio_mask)
|
|
mode = fls(hwif->pio_mask) - 1 + XFER_PIO_0;
|
|
else
|
|
mode = XFER_PIO_4;
|
|
}
|
|
|
|
/* printk("%s: mode 0x%02x, speed 0x%02x\n", __func__, mode, speed); */
|
|
|
|
return min(speed, mode);
|
|
}
|
|
|
|
/**
|
|
* ide_get_best_pio_mode - get PIO mode from drive
|
|
* @drive: drive to consider
|
|
* @mode_wanted: preferred mode
|
|
* @max_mode: highest allowed mode
|
|
*
|
|
* This routine returns the recommended PIO settings for a given drive,
|
|
* based on the drive->id information and the ide_pio_blacklist[].
|
|
*
|
|
* Drive PIO mode is auto-selected if 255 is passed as mode_wanted.
|
|
* This is used by most chipset support modules when "auto-tuning".
|
|
*/
|
|
|
|
u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
|
|
{
|
|
int pio_mode;
|
|
struct hd_driveid* id = drive->id;
|
|
int overridden = 0;
|
|
|
|
if (mode_wanted != 255)
|
|
return min_t(u8, mode_wanted, max_mode);
|
|
|
|
if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0 &&
|
|
(pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
|
|
printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name);
|
|
} else {
|
|
pio_mode = id->tPIO;
|
|
if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
|
|
pio_mode = 2;
|
|
overridden = 1;
|
|
}
|
|
if (id->field_valid & 2) { /* drive implements ATA2? */
|
|
if (id->capability & 8) { /* IORDY supported? */
|
|
if (id->eide_pio_modes & 7) {
|
|
overridden = 0;
|
|
if (id->eide_pio_modes & 4)
|
|
pio_mode = 5;
|
|
else if (id->eide_pio_modes & 2)
|
|
pio_mode = 4;
|
|
else
|
|
pio_mode = 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (overridden)
|
|
printk(KERN_INFO "%s: tPIO > 2, assuming tPIO = 2\n",
|
|
drive->name);
|
|
}
|
|
|
|
if (pio_mode > max_mode)
|
|
pio_mode = max_mode;
|
|
|
|
return pio_mode;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_get_best_pio_mode);
|
|
|
|
/* req_pio == "255" for auto-tune */
|
|
void ide_set_pio(ide_drive_t *drive, u8 req_pio)
|
|
{
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
const struct ide_port_ops *port_ops = hwif->port_ops;
|
|
u8 host_pio, pio;
|
|
|
|
if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
|
|
(hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
|
|
return;
|
|
|
|
BUG_ON(hwif->pio_mask == 0x00);
|
|
|
|
host_pio = fls(hwif->pio_mask) - 1;
|
|
|
|
pio = ide_get_best_pio_mode(drive, req_pio, host_pio);
|
|
|
|
/*
|
|
* TODO:
|
|
* - report device max PIO mode
|
|
* - check req_pio != 255 against device max PIO mode
|
|
*/
|
|
printk(KERN_DEBUG "%s: host max PIO%d wanted PIO%d%s selected PIO%d\n",
|
|
drive->name, host_pio, req_pio,
|
|
req_pio == 255 ? "(auto-tune)" : "", pio);
|
|
|
|
(void)ide_set_pio_mode(drive, XFER_PIO_0 + pio);
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_set_pio);
|
|
|
|
/**
|
|
* ide_toggle_bounce - handle bounce buffering
|
|
* @drive: drive to update
|
|
* @on: on/off boolean
|
|
*
|
|
* Enable or disable bounce buffering for the device. Drives move
|
|
* between PIO and DMA and that changes the rules we need.
|
|
*/
|
|
|
|
void ide_toggle_bounce(ide_drive_t *drive, int on)
|
|
{
|
|
u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */
|
|
|
|
if (!PCI_DMA_BUS_IS_PHYS) {
|
|
addr = BLK_BOUNCE_ANY;
|
|
} else if (on && drive->media == ide_disk) {
|
|
struct device *dev = drive->hwif->dev;
|
|
|
|
if (dev && dev->dma_mask)
|
|
addr = *dev->dma_mask;
|
|
}
|
|
|
|
if (drive->queue)
|
|
blk_queue_bounce_limit(drive->queue, addr);
|
|
}
|
|
|
|
int ide_set_pio_mode(ide_drive_t *drive, const u8 mode)
|
|
{
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
const struct ide_port_ops *port_ops = hwif->port_ops;
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
|
|
return 0;
|
|
|
|
if (port_ops == NULL || port_ops->set_pio_mode == NULL)
|
|
return -1;
|
|
|
|
/*
|
|
* TODO: temporary hack for some legacy host drivers that didn't
|
|
* set transfer mode on the device in ->set_pio_mode method...
|
|
*/
|
|
if (port_ops->set_dma_mode == NULL) {
|
|
port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
|
|
return 0;
|
|
}
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
|
|
if (ide_config_drive_speed(drive, mode))
|
|
return -1;
|
|
port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
|
|
return 0;
|
|
} else {
|
|
port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
|
|
return ide_config_drive_speed(drive, mode);
|
|
}
|
|
}
|
|
|
|
int ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
|
|
{
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
const struct ide_port_ops *port_ops = hwif->port_ops;
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
|
|
return 0;
|
|
|
|
if (port_ops == NULL || port_ops->set_dma_mode == NULL)
|
|
return -1;
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
|
|
if (ide_config_drive_speed(drive, mode))
|
|
return -1;
|
|
port_ops->set_dma_mode(drive, mode);
|
|
return 0;
|
|
} else {
|
|
port_ops->set_dma_mode(drive, mode);
|
|
return ide_config_drive_speed(drive, mode);
|
|
}
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_set_dma_mode);
|
|
|
|
/**
|
|
* ide_set_xfer_rate - set transfer rate
|
|
* @drive: drive to set
|
|
* @rate: speed to attempt to set
|
|
*
|
|
* General helper for setting the speed of an IDE device. This
|
|
* function knows about user enforced limits from the configuration
|
|
* which ->set_pio_mode/->set_dma_mode does not.
|
|
*/
|
|
|
|
int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
|
|
{
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
const struct ide_port_ops *port_ops = hwif->port_ops;
|
|
|
|
if (port_ops == NULL || port_ops->set_dma_mode == NULL ||
|
|
(hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
|
|
return -1;
|
|
|
|
rate = ide_rate_filter(drive, rate);
|
|
|
|
BUG_ON(rate < XFER_PIO_0);
|
|
|
|
if (rate >= XFER_PIO_0 && rate <= XFER_PIO_5)
|
|
return ide_set_pio_mode(drive, rate);
|
|
|
|
return ide_set_dma_mode(drive, rate);
|
|
}
|
|
|
|
static void ide_dump_opcode(ide_drive_t *drive)
|
|
{
|
|
struct request *rq;
|
|
ide_task_t *task = NULL;
|
|
|
|
spin_lock(&ide_lock);
|
|
rq = NULL;
|
|
if (HWGROUP(drive))
|
|
rq = HWGROUP(drive)->rq;
|
|
spin_unlock(&ide_lock);
|
|
if (!rq)
|
|
return;
|
|
|
|
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
|
|
task = rq->special;
|
|
|
|
printk("ide: failed opcode was: ");
|
|
if (task == NULL)
|
|
printk(KERN_CONT "unknown\n");
|
|
else
|
|
printk(KERN_CONT "0x%02x\n", task->tf.command);
|
|
}
|
|
|
|
u64 ide_get_lba_addr(struct ide_taskfile *tf, int lba48)
|
|
{
|
|
u32 high, low;
|
|
|
|
if (lba48)
|
|
high = (tf->hob_lbah << 16) | (tf->hob_lbam << 8) |
|
|
tf->hob_lbal;
|
|
else
|
|
high = tf->device & 0xf;
|
|
low = (tf->lbah << 16) | (tf->lbam << 8) | tf->lbal;
|
|
|
|
return ((u64)high << 24) | low;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ide_get_lba_addr);
|
|
|
|
static void ide_dump_sector(ide_drive_t *drive)
|
|
{
|
|
ide_task_t task;
|
|
struct ide_taskfile *tf = &task.tf;
|
|
int lba48 = (drive->addressing == 1) ? 1 : 0;
|
|
|
|
memset(&task, 0, sizeof(task));
|
|
if (lba48)
|
|
task.tf_flags = IDE_TFLAG_IN_LBA | IDE_TFLAG_IN_HOB_LBA |
|
|
IDE_TFLAG_LBA48;
|
|
else
|
|
task.tf_flags = IDE_TFLAG_IN_LBA | IDE_TFLAG_IN_DEVICE;
|
|
|
|
drive->hwif->tf_read(drive, &task);
|
|
|
|
if (lba48 || (tf->device & ATA_LBA))
|
|
printk(", LBAsect=%llu",
|
|
(unsigned long long)ide_get_lba_addr(tf, lba48));
|
|
else
|
|
printk(", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam,
|
|
tf->device & 0xf, tf->lbal);
|
|
}
|
|
|
|
static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
|
|
{
|
|
printk("{ ");
|
|
if (err & ABRT_ERR) printk("DriveStatusError ");
|
|
if (err & ICRC_ERR)
|
|
printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
|
|
if (err & ECC_ERR) printk("UncorrectableError ");
|
|
if (err & ID_ERR) printk("SectorIdNotFound ");
|
|
if (err & TRK0_ERR) printk("TrackZeroNotFound ");
|
|
if (err & MARK_ERR) printk("AddrMarkNotFound ");
|
|
printk("}");
|
|
if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR ||
|
|
(err & (ECC_ERR|ID_ERR|MARK_ERR))) {
|
|
ide_dump_sector(drive);
|
|
if (HWGROUP(drive) && HWGROUP(drive)->rq)
|
|
printk(", sector=%llu",
|
|
(unsigned long long)HWGROUP(drive)->rq->sector);
|
|
}
|
|
printk("\n");
|
|
}
|
|
|
|
static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
|
|
{
|
|
printk("{ ");
|
|
if (err & ILI_ERR) printk("IllegalLengthIndication ");
|
|
if (err & EOM_ERR) printk("EndOfMedia ");
|
|
if (err & ABRT_ERR) printk("AbortedCommand ");
|
|
if (err & MCR_ERR) printk("MediaChangeRequested ");
|
|
if (err & LFS_ERR) printk("LastFailedSense=0x%02x ",
|
|
(err & LFS_ERR) >> 4);
|
|
printk("}\n");
|
|
}
|
|
|
|
/**
|
|
* ide_dump_status - translate ATA/ATAPI error
|
|
* @drive: drive that status applies to
|
|
* @msg: text message to print
|
|
* @stat: status byte to decode
|
|
*
|
|
* Error reporting, in human readable form (luxurious, but a memory hog).
|
|
* Combines the drive name, message and status byte to provide a
|
|
* user understandable explanation of the device error.
|
|
*/
|
|
|
|
u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
|
|
{
|
|
unsigned long flags;
|
|
u8 err = 0;
|
|
|
|
local_irq_save(flags);
|
|
printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
|
|
if (stat & BUSY_STAT)
|
|
printk("Busy ");
|
|
else {
|
|
if (stat & READY_STAT) printk("DriveReady ");
|
|
if (stat & WRERR_STAT) printk("DeviceFault ");
|
|
if (stat & SEEK_STAT) printk("SeekComplete ");
|
|
if (stat & DRQ_STAT) printk("DataRequest ");
|
|
if (stat & ECC_STAT) printk("CorrectedError ");
|
|
if (stat & INDEX_STAT) printk("Index ");
|
|
if (stat & ERR_STAT) printk("Error ");
|
|
}
|
|
printk("}\n");
|
|
if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
|
|
err = ide_read_error(drive);
|
|
printk("%s: %s: error=0x%02x ", drive->name, msg, err);
|
|
if (drive->media == ide_disk)
|
|
ide_dump_ata_error(drive, err);
|
|
else
|
|
ide_dump_atapi_error(drive, err);
|
|
}
|
|
ide_dump_opcode(drive);
|
|
local_irq_restore(flags);
|
|
return err;
|
|
}
|
|
|
|
EXPORT_SYMBOL(ide_dump_status);
|