ht6560b.c 10.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
/*
 *  Copyright (C) 1995-2000  Linus Torvalds & author (see below)
 */

/*
 *
 *  Version 0.01        Initial version hacked out of ide.c
 *
 *  Version 0.02        Added support for PIO modes, auto-tune
 *
 *  Version 0.03        Some cleanups
 *
 *  Version 0.05        PIO mode cycle timings auto-tune using bus-speed
 *
 *  Version 0.06        Prefetch mode now defaults no OFF. To set
 *                      prefetch mode OFF/ON use "hdparm -p8/-p9".
 *                      Unmask irq is disabled when prefetch mode
 *                      is enabled.
 *
 *  Version 0.07        Trying to fix CD-ROM detection problem.
 *                      "Prefetch" mode bit OFF for ide disks and
 *                      ON for anything else.
 *
 *
 *  HT-6560B EIDE-controller support
 *  To activate controller support use kernel parameter "ide0=ht6560b".
 *  Use hdparm utility to enable PIO mode support.
 *
 *  Author:    Mikko Ala-Fossi            <maf@iki.fi>
 *             Jan Evert van Grootheest   <janevert@iae.nl>
 *
 *  Try:  http://www.maf.iki.fi/~maf/ht6560b/
 */

#define HT6560B_VERSION "v0.07"

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>

#include <asm/io.h>

/* #define DEBUG */  /* remove comments for DEBUG messages */

/*
 * The special i/o-port that HT-6560B uses to configuration:
 *    bit0 (0x01): "1" selects secondary interface
 *    bit2 (0x04): "1" enables FIFO function
 *    bit5 (0x20): "1" enables prefetched data read function  (???)
 *
 * The special i/o-port that HT-6560A uses to configuration:
 *    bit0 (0x01): "1" selects secondary interface
 *    bit1 (0x02): "1" enables prefetched data read function
 *    bit2 (0x04): "0" enables multi-master system	      (?)
 *    bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time	      (?)
 */
#define HT_CONFIG_PORT	  0x3e6
#define HT_CONFIG(drivea) (u8)(((drivea)->drive_data & 0xff00) >> 8)
/*
 * FIFO + PREFETCH (both a/b-model)
 */
#define HT_CONFIG_DEFAULT 0x1c /* no prefetch */
/* #define HT_CONFIG_DEFAULT 0x3c */ /* with prefetch */
#define HT_SECONDARY_IF	  0x01
#define HT_PREFETCH_MODE  0x20

/*
 * ht6560b Timing values:
 *
 * I reviewed some assembler source listings of htide drivers and found
 * out how they setup those cycle time interfacing values, as they at Holtek
 * call them. IDESETUP.COM that is supplied with the drivers figures out
 * optimal values and fetches those values to drivers. I found out that
 * they use IDE_SELECT_REG to fetch timings to the ide board right after
 * interface switching. After that it was quite easy to add code to
 * ht6560b.c.
 *
 * IDESETUP.COM gave me values 0x24, 0x45, 0xaa, 0xff that worked fine
 * for hda and hdc. But hdb needed higher values to work, so I guess
 * that sometimes it is necessary to give higher value than IDESETUP
 * gives.   [see cmd640.c for an extreme example of this. -ml]
 *
 * Perhaps I should explain something about these timing values:
 * The higher nibble of value is the Recovery Time  (rt) and the lower nibble
 * of the value is the Active Time  (at). Minimum value 2 is the fastest and
 * the maximum value 15 is the slowest. Default values should be 15 for both.
 * So 0x24 means 2 for rt and 4 for at. Each of the drives should have
 * both values, and IDESETUP gives automatically rt=15 st=15 for CDROMs or
 * similar. If value is too small there will be all sorts of failures.
 *
 * Timing byte consists of
 *	High nibble:  Recovery Cycle Time  (rt)
 *	     The valid values range from 2 to 15. The default is 15.
 *
 *	Low nibble:   Active Cycle Time	   (at)
 *	     The valid values range from 2 to 15. The default is 15.
 *
 * You can obtain optimized timing values by running Holtek IDESETUP.COM
 * for DOS. DOS drivers get their timing values from command line, where
 * the first value is the Recovery Time and the second value is the
 * Active Time for each drive. Smaller value gives higher speed.
 * In case of failures you should probably fall back to a higher value.
 */
#define HT_TIMING(drivea) (u8)((drivea)->drive_data & 0x00ff)
#define HT_TIMING_DEFAULT 0xff

/*
 * This routine handles interface switching for the peculiar hardware design
 * on the F.G.I./Holtek HT-6560B VLB IDE interface.
 * The HT-6560B can only enable one IDE port at a time, and requires a
 * silly sequence (below) whenever we switch between primary and secondary.
 */

/*
 * This routine is invoked from ide.c to prepare for access to a given drive.
 */
static void ht6560b_selectproc (ide_drive_t *drive)
{
	unsigned long flags;
	static u8 current_select = 0;
	static u8 current_timing = 0;
	u8 select, timing;
	
	local_irq_save(flags);
	
	select = HT_CONFIG(drive);
	timing = HT_TIMING(drive);
	
	if (select != current_select || timing != current_timing) {
		current_select = select;
		current_timing = timing;
		if (drive->media != ide_disk || !drive->present)
			select |= HT_PREFETCH_MODE;
142 143 144 145 146
		(void)inb(HT_CONFIG_PORT);
		(void)inb(HT_CONFIG_PORT);
		(void)inb(HT_CONFIG_PORT);
		(void)inb(HT_CONFIG_PORT);
		outb(select, HT_CONFIG_PORT);
Linus Torvalds's avatar
Linus Torvalds committed
147 148 149
		/*
		 * Set timing for this drive:
		 */
150 151
		outb(timing, IDE_SELECT_REG);
		(void)inb(IDE_STATUS_REG);
Linus Torvalds's avatar
Linus Torvalds committed
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
#ifdef DEBUG
		printk("ht6560b: %s: select=%#x timing=%#x\n",
			drive->name, select, timing);
#endif
	}
	local_irq_restore(flags);
}

/*
 * Autodetection and initialization of ht6560b
 */
static int __init try_to_init_ht6560b(void)
{
	u8 orig_value;
	int i;
	
	/* Autodetect ht6560b */
	if ((orig_value = inb(HT_CONFIG_PORT)) == 0xff)
		return 0;
	
	for (i=3;i>0;i--) {
		outb(0x00, HT_CONFIG_PORT);
		if (!( (~inb(HT_CONFIG_PORT)) & 0x3f )) {
			outb(orig_value, HT_CONFIG_PORT);
			return 0;
		}
	}
	outb(0x00, HT_CONFIG_PORT);
	if ((~inb(HT_CONFIG_PORT))& 0x3f) {
		outb(orig_value, HT_CONFIG_PORT);
		return 0;
	}
	/*
	 * Ht6560b autodetected
	 */
	outb(HT_CONFIG_DEFAULT, HT_CONFIG_PORT);
	outb(HT_TIMING_DEFAULT, 0x1f6);  /* IDE_SELECT_REG */
	(void) inb(0x1f7);               /* IDE_STATUS_REG */
	
	printk("\nht6560b " HT6560B_VERSION
	       ": chipset detected and initialized"
#ifdef DEBUG
	       " with debug enabled"
#endif
		);
	return 1;
}

200
static u8 ht_pio2timings(ide_drive_t *drive, const u8 pio)
Linus Torvalds's avatar
Linus Torvalds committed
201 202 203 204 205 206
{
	int active_time, recovery_time;
	int active_cycles, recovery_cycles;
	int bus_speed = system_bus_clock();
	
        if (pio) {
207 208 209 210
		unsigned int cycle_time;

		cycle_time = ide_pio_cycle_time(drive, pio);

Linus Torvalds's avatar
Linus Torvalds committed
211 212 213 214 215 216
		/*
		 *  Just like opti621.c we try to calculate the
		 *  actual cycle time for recovery and activity
		 *  according system bus speed.
		 */
		active_time = ide_pio_timings[pio].active_time;
217
		recovery_time = cycle_time
Linus Torvalds's avatar
Linus Torvalds committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
			- active_time
			- ide_pio_timings[pio].setup_time;
		/*
		 *  Cycle times should be Vesa bus cycles
		 */
		active_cycles   = (active_time   * bus_speed + 999) / 1000;
		recovery_cycles = (recovery_time * bus_speed + 999) / 1000;
		/*
		 *  Upper and lower limits
		 */
		if (active_cycles   < 2)  active_cycles   = 2;
		if (recovery_cycles < 2)  recovery_cycles = 2;
		if (active_cycles   > 15) active_cycles   = 15;
		if (recovery_cycles > 15) recovery_cycles = 0;  /* 0==16 */
		
#ifdef DEBUG
		printk("ht6560b: drive %s setting pio=%d recovery=%d (%dns) active=%d (%dns)\n", drive->name, pio, recovery_cycles, recovery_time, active_cycles, active_time);
#endif
		
		return (u8)((recovery_cycles << 4) | active_cycles);
	} else {
		
#ifdef DEBUG
		printk("ht6560b: drive %s setting pio=0\n", drive->name);
#endif
		
		return HT_TIMING_DEFAULT;    /* default setting */
	}
}

248 249
static DEFINE_SPINLOCK(ht6560b_lock);

Linus Torvalds's avatar
Linus Torvalds committed
250 251 252 253 254 255 256
/*
 *  Enable/Disable so called prefetch mode
 */
static void ht_set_prefetch(ide_drive_t *drive, u8 state)
{
	unsigned long flags;
	int t = HT_PREFETCH_MODE << 8;
257 258 259

	spin_lock_irqsave(&ht6560b_lock, flags);

Linus Torvalds's avatar
Linus Torvalds committed
260 261 262 263 264 265 266 267 268 269 270
	/*
	 *  Prefetch mode and unmask irq seems to conflict
	 */
	if (state) {
		drive->drive_data |= t;   /* enable prefetch mode */
		drive->no_unmask = 1;
		drive->unmask = 0;
	} else {
		drive->drive_data &= ~t;  /* disable prefetch mode */
		drive->no_unmask = 0;
	}
271 272 273

	spin_unlock_irqrestore(&ht6560b_lock, flags);

Linus Torvalds's avatar
Linus Torvalds committed
274 275 276 277 278
#ifdef DEBUG
	printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis"));
#endif
}

279
static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
Linus Torvalds's avatar
Linus Torvalds committed
280 281 282 283 284 285 286 287 288 289
{
	unsigned long flags;
	u8 timing;
	
	switch (pio) {
	case 8:         /* set prefetch off */
	case 9:         /* set prefetch on */
		ht_set_prefetch(drive, pio & 1);
		return;
	}
290

Linus Torvalds's avatar
Linus Torvalds committed
291
	timing = ht_pio2timings(drive, pio);
292 293

	spin_lock_irqsave(&ht6560b_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
294 295
	drive->drive_data &= 0xff00;
	drive->drive_data |= timing;
296 297
	spin_unlock_irqrestore(&ht6560b_lock, flags);

Linus Torvalds's avatar
Linus Torvalds committed
298 299 300 301 302
#ifdef DEBUG
	printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing);
#endif
}

303 304 305 306 307
int probe_ht6560b = 0;

module_param_named(probe, probe_ht6560b, bool, 0);
MODULE_PARM_DESC(probe, "probe for HT6560B chipset");

308
static int __init ht6560b_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
309 310
{
	ide_hwif_t *hwif, *mate;
311
	static u8 idx[4] = { 0, 1, 0xff, 0xff };
Linus Torvalds's avatar
Linus Torvalds committed
312 313
	int t;

314 315 316
	if (probe_ht6560b == 0)
		return -ENODEV;

Linus Torvalds's avatar
Linus Torvalds committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
	hwif = &ide_hwifs[0];
	mate = &ide_hwifs[1];

	if (!request_region(HT_CONFIG_PORT, 1, hwif->name)) {
		printk(KERN_NOTICE "%s: HT_CONFIG_PORT not found\n",
			__FUNCTION__);
		return -ENODEV;
	}

	if (!try_to_init_ht6560b()) {
		printk(KERN_NOTICE "%s: HBA not found\n", __FUNCTION__);
		goto release_region;
	}

	hwif->chipset = ide_ht6560b;
	hwif->selectproc = &ht6560b_selectproc;
333
	hwif->host_flags = IDE_HFLAG_ABUSE_PREFETCH;
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
334
	hwif->pio_mask = ATA_PIO5;
335
	hwif->set_pio_mode = &ht6560b_set_pio_mode;
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338 339 340
	hwif->serialized = 1;	/* is this needed? */
	hwif->mate = mate;

	mate->chipset = ide_ht6560b;
	mate->selectproc = &ht6560b_selectproc;
341
	mate->host_flags = IDE_HFLAG_ABUSE_PREFETCH;
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
342
	mate->pio_mask = ATA_PIO5;
343
	mate->set_pio_mode = &ht6560b_set_pio_mode;
Linus Torvalds's avatar
Linus Torvalds committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	mate->serialized = 1;	/* is this needed? */
	mate->mate = hwif;
	mate->channel = 1;

	/*
	 * Setting default configurations for drives
	 */
	t = (HT_CONFIG_DEFAULT << 8);
	t |= HT_TIMING_DEFAULT;
	hwif->drives[0].drive_data = t;
	hwif->drives[1].drive_data = t;

	t |= (HT_SECONDARY_IF << 8);
	mate->drives[0].drive_data = t;
	mate->drives[1].drive_data = t;

360
	ide_device_add(idx);
Linus Torvalds's avatar
Linus Torvalds committed
361 362 363 364 365 366 367 368 369 370 371 372 373

	return 0;

release_region:
	release_region(HT_CONFIG_PORT, 1);
	return -ENODEV;
}

module_init(ht6560b_init);

MODULE_AUTHOR("See Local File");
MODULE_DESCRIPTION("HT-6560B EIDE-controller support");
MODULE_LICENSE("GPL");