Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e9b4e53 authored by Bartlomiej Zolnierkiewicz's avatar Bartlomiej Zolnierkiewicz
Browse files

it821x: PIO mode setup fixes



* limit max PIO mode to PIO4, this driver doesn't support PIO5 and attempt
  to setup PIO5 by it821x_tuneproc() could result in incorrect PIO timings
  + incorrect base clock being set for controller in the passthrough mode

* move code limiting max PIO according to the pair device capabilities from
  config_it821x_chipset_for_pio() to it821x_tuneproc() so the check is also
  applied for mode change requests coming through ->tuneproc and ->speedproc
  interfaces

* set device speed in it821x_tuneproc()

* in it821x_tune_chipset() call it821x_tuneproc() also if the controller is
  in the smart mode (so the check for pair device max PIO is done)

* rename it821x_tuneproc() to it821x_tune_pio(), then add it821x_tuneproc()
  wrapper which does the max PIO mode check;  it worked by the pure luck
  previously, pio[4] and pio_want[4] arrays were used with index == 255
  so random PIO timings and base clock were set for the controller in the
  passthrough mode, thankfully PIO timings and base clock were corrected
  later by config_it821x_chipset_for_pio() call (but it was not called for
  PIO-only devices during resume and for user requested PIO autotuning)

* remove config_it821x_chipset_for_pio() call from config_chipset_for_dma()
  as the driver sets ->autotune to 1 and ->tuneproc does the proper job now

* convert the last user of config_it821x_chipset_for_pio() to use
  it821x_tuneproc(drive, 255) and remove no longer needed function

While at it:

* fix few comments

* bump driver version

Signed-off-by: default avatarBartlomiej Zolnierkiewicz <bzolnier@gmail.com>
parent 247b03f8
Loading
Loading
Loading
Loading
+58 −68
Original line number Diff line number Diff line

/*
 * linux/drivers/ide/pci/it821x.c		Version 0.09	December 2004
 * linux/drivers/ide/pci/it821x.c		Version 0.10	Mar 10 2007
 *
 * Copyright (C) 2004		Red Hat <alan@redhat.com>
 * Copyright (C) 2007		Bartlomiej Zolnierkiewicz
 *
 *  May be copied or modified under the terms of the GNU General Public License
 *  Based in part on the ITE vendor provided SCSI driver.
@@ -104,6 +105,7 @@ static int it8212_noraid;
/**
 *	it821x_program	-	program the PIO/MWDMA registers
 *	@drive: drive to tune
 *	@timing: timing info
 *
 *	Program the PIO/MWDMA timing for this channel according to the
 *	current clock.
@@ -127,6 +129,7 @@ static void it821x_program(ide_drive_t *drive, u16 timing)
/**
 *	it821x_program_udma	-	program the UDMA registers
 *	@drive: drive to tune
 *	@timing: timing info
 *
 *	Program the UDMA timing for this drive according to the
 *	current clock.
@@ -153,10 +156,9 @@ static void it821x_program_udma(ide_drive_t *drive, u16 timing)
	}
}


/**
 *	it821x_clock_strategy
 *	@hwif: hardware interface
 *	@drive: drive to set up
 *
 *	Select between the 50 and 66Mhz base clocks to get the best
 *	results for this interface.
@@ -182,7 +184,10 @@ static void it821x_clock_strategy(ide_drive_t *drive)
		altclock = itdev->want[0][1];
	}

	/* Master doesn't care does the slave ? */
	/*
	 * if both clocks can be used for the mode with the higher priority
	 * use the clock needed by the mode with the lower priority
	 */
	if (clock == ATA_ANY)
		clock = altclock;

@@ -240,37 +245,56 @@ static u8 it821x_ratemask (ide_drive_t *drive)
}

/**
 *	it821x_tuneproc	-	tune a drive
 *	it821x_tunepio	-	tune a drive
 *	@drive: drive to tune
 *	@mode_wanted: the target operating mode
 *
 *	Load the timing settings for this device mode into the
 *	controller. By the time we are called the mode has been
 *	modified as neccessary to handle the absence of seperate
 *	master/slave timers for MWDMA/PIO.
 *	@pio: the desired PIO mode
 *
 *	This code is only used in pass through mode.
 *	Try to tune the drive/host to the desired PIO mode taking into
 *	the consideration the maximum PIO mode supported by the other
 *	device on the cable.
 */

static void it821x_tuneproc (ide_drive_t *drive, byte mode_wanted)
static int it821x_tunepio(ide_drive_t *drive, u8 set_pio)
{
	ide_hwif_t *hwif	= drive->hwif;
	struct it821x_dev *itdev = ide_get_hwifdata(hwif);
	int unit = drive->select.b.unit;
	ide_drive_t *pair = &hwif->drives[1 - unit];

	/* Spec says 89 ref driver uses 88 */
	static u16 pio[]	= { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 };
	static u8 pio_want[]    = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY };

	/*
	 * Compute the best PIO mode we can for a given device. We must
	 * pick a speed that does not cause problems with the other device
	 * on the cable.
	 */
	if (pair) {
		u8 pair_pio = ide_get_best_pio_mode(pair, 255, 4, NULL);
		/* trim PIO to the slowest of the master/slave */
		if (pair_pio < set_pio)
			set_pio = pair_pio;
	}

	if (itdev->smart)
		return;
		goto set_drive_speed;

	/* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */
	itdev->want[unit][1] = pio_want[mode_wanted];
	itdev->want[unit][1] = pio_want[set_pio];
	itdev->want[unit][0] = 1;	/* PIO is lowest priority */
	itdev->pio[unit] = pio[mode_wanted];
	itdev->pio[unit] = pio[set_pio];
	it821x_clock_strategy(drive);
	it821x_program(drive, itdev->pio[unit]);

set_drive_speed:
	return ide_config_drive_speed(drive, XFER_PIO_0 + set_pio);
}

static void it821x_tuneproc(ide_drive_t *drive, u8 pio)
{
	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
	(void)it821x_tunepio(drive, pio);
}

/**
@@ -353,40 +377,6 @@ static void it821x_tune_udma (ide_drive_t *drive, byte mode_wanted)

}

/**
 *	config_it821x_chipset_for_pio	-	set drive timings
 *	@drive: drive to tune
 *	@speed we want
 *
 *	Compute the best pio mode we can for a given device. We must
 *	pick a speed that does not cause problems with the other device
 *	on the cable.
 */

static void config_it821x_chipset_for_pio (ide_drive_t *drive, byte set_speed)
{
	u8 unit = drive->select.b.unit;
	ide_hwif_t *hwif = drive->hwif;
	ide_drive_t *pair = &hwif->drives[1-unit];
	u8 speed = 0, set_pio	= ide_get_best_pio_mode(drive, 255, 5, NULL);
	u8 pair_pio;

	/* We have to deal with this mess in pairs */
	if(pair != NULL) {
		pair_pio = ide_get_best_pio_mode(pair, 255, 5, NULL);
		/* Trim PIO to the slowest of the master/slave */
		if(pair_pio < set_pio)
			set_pio = pair_pio;
	}
	it821x_tuneproc(drive, set_pio);
	speed = XFER_PIO_0 + set_pio;
	/* XXX - We trim to the lowest of the pair so the other drive
	   will always be fine at this point until we do hotplug passthru */

	if (set_speed)
		(void) ide_config_drive_speed(drive, speed);
}

/**
 *	it821x_dma_read	-	DMA hook
 *	@drive: drive for DMA
@@ -450,15 +440,17 @@ static int it821x_tune_chipset (ide_drive_t *drive, byte xferspeed)
	struct it821x_dev *itdev = ide_get_hwifdata(hwif);
	u8 speed		= ide_rate_filter(it821x_ratemask(drive), xferspeed);

	if(!itdev->smart) {
	switch (speed) {
	case XFER_PIO_4:
	case XFER_PIO_3:
	case XFER_PIO_2:
	case XFER_PIO_1:
	case XFER_PIO_0:
				it821x_tuneproc(drive, (speed - XFER_PIO_0));
				break;
		return it821x_tunepio(drive, speed - XFER_PIO_0);
	}

	if (itdev->smart == 0) {
		switch (speed) {
			/* MWDMA tuning is really hard because our MWDMA and PIO
			   timings are kept in the same place. We can switch in the
			   host dma on/off callbacks */
@@ -498,16 +490,14 @@ static int config_chipset_for_dma (ide_drive_t *drive)
{
	u8 speed	= ide_dma_speed(drive, it821x_ratemask(drive));

	if (speed) {
		config_it821x_chipset_for_pio(drive, 0);
	if (speed == 0)
		return 0;

	it821x_tune_chipset(drive, speed);

	return ide_dma_enable(drive);
}

	return 0;
}

/**
 *	it821x_configure_drive_for_dma	-	set up for DMA transfers
 *	@drive: drive we are going to set up
@@ -523,7 +513,7 @@ static int it821x_config_drive_for_dma (ide_drive_t *drive)
	if (ide_use_dma(drive) && config_chipset_for_dma(drive))
		return 0;

	config_it821x_chipset_for_pio(drive, 1);
	it821x_tuneproc(drive, 255);

	return -1;
}