ht6560b.c 10.4 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10
/*
 *  Copyright (C) 1995-2000  Linus Torvalds & author (see below)
 */

/*
 *  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>
11
 *             Jan Evert van Grootheest   <j.e.van.grootheest@caiway.nl>
Linus Torvalds's avatar
Linus Torvalds committed
12 13 14
 *
 */

15
#define DRV_NAME	"ht6560b"
16
#define HT6560B_VERSION "v0.08"
Linus Torvalds's avatar
Linus Torvalds committed
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

#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/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
46 47 48 49 50 51

static inline u8 HT_CONFIG(ide_drive_t *drive)
{
	return ((unsigned long)ide_get_drivedata(drive) & 0xff00) >> 8;
}

Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/*
 * 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
67
 * they use Select register to fetch timings to the ide board right after
Linus Torvalds's avatar
Linus Torvalds committed
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
 * 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.
 */
97 98 99 100 101
static inline u8 HT_TIMING(ide_drive_t *drive)
{
	return (unsigned long)ide_get_drivedata(drive) & 0x00ff;
}

Linus Torvalds's avatar
Linus Torvalds committed
102 103 104 105 106 107 108 109 110 111 112 113
#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.
 */
114
static void ht6560b_dev_select(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
115
{
116
	ide_hwif_t *hwif = drive->hwif;
Linus Torvalds's avatar
Linus Torvalds committed
117 118 119 120 121 122
	unsigned long flags;
	static u8 current_select = 0;
	static u8 current_timing = 0;
	u8 select, timing;
	
	local_irq_save(flags);
123

Linus Torvalds's avatar
Linus Torvalds committed
124 125
	select = HT_CONFIG(drive);
	timing = HT_TIMING(drive);
126 127 128 129 130

	/*
	 * Need to enforce prefetch sometimes because otherwise
	 * it'll hang (hard).
	 */
131 132
	if (drive->media != ide_disk ||
	    (drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
133 134
		select |= HT_PREFETCH_MODE;

Linus Torvalds's avatar
Linus Torvalds committed
135 136 137
	if (select != current_select || timing != current_timing) {
		current_select = select;
		current_timing = timing;
138 139 140 141 142
		(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
143 144 145
		/*
		 * Set timing for this drive:
		 */
146 147
		outb(timing, hwif->io_ports.device_addr);
		(void)inb(hwif->io_ports.status_addr);
Linus Torvalds's avatar
Linus Torvalds committed
148 149 150 151 152 153
#ifdef DEBUG
		printk("ht6560b: %s: select=%#x timing=%#x\n",
			drive->name, select, timing);
#endif
	}
	local_irq_restore(flags);
154 155

	outb(drive->select | ATA_DEVICE_OBS, hwif->io_ports.device_addr);
Linus Torvalds's avatar
Linus Torvalds committed
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
}

/*
 * 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);
186 187 188
	outb(HT_TIMING_DEFAULT, 0x1f6);	/* Select register */
	(void)inb(0x1f7);		/* Status register */

189
	printk("ht6560b " HT6560B_VERSION
Linus Torvalds's avatar
Linus Torvalds committed
190 191 192 193
	       ": chipset detected and initialized"
#ifdef DEBUG
	       " with debug enabled"
#endif
194
	       "\n"
Linus Torvalds's avatar
Linus Torvalds committed
195 196 197 198
		);
	return 1;
}

199
static u8 ht_pio2timings(ide_drive_t *drive, const u8 pio)
Linus Torvalds's avatar
Linus Torvalds committed
200 201 202
{
	int active_time, recovery_time;
	int active_cycles, recovery_cycles;
203
	int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50;
204

Linus Torvalds's avatar
Linus Torvalds committed
205
        if (pio) {
206
		unsigned int cycle_time;
207
		struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
208 209 210

		cycle_time = ide_pio_cycle_time(drive, pio);

Linus Torvalds's avatar
Linus Torvalds committed
211 212 213 214 215
		/*
		 *  Just like opti621.c we try to calculate the
		 *  actual cycle time for recovery and activity
		 *  according system bus speed.
		 */
216 217
		active_time = t->active;
		recovery_time = cycle_time - active_time - t->setup;
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
		/*
		 *  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 */
	}
}

246 247
static DEFINE_SPINLOCK(ht6560b_lock);

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

	spin_lock_irqsave(&ht6560b_lock, flags);

258 259
	config = (unsigned long)ide_get_drivedata(drive);

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

272 273
	ide_set_drivedata(drive, (void *)config);

274 275
	spin_unlock_irqrestore(&ht6560b_lock, flags);

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

281
static void ht6560b_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
282
{
283
	unsigned long flags, config;
284
	const u8 pio = drive->pio_mode - XFER_PIO_0;
Linus Torvalds's avatar
Linus Torvalds committed
285 286 287 288 289 290 291 292
	u8 timing;
	
	switch (pio) {
	case 8:         /* set prefetch off */
	case 9:         /* set prefetch on */
		ht_set_prefetch(drive, pio & 1);
		return;
	}
293

Linus Torvalds's avatar
Linus Torvalds committed
294
	timing = ht_pio2timings(drive, pio);
295 296

	spin_lock_irqsave(&ht6560b_lock, flags);
297 298 299 300
	config = (unsigned long)ide_get_drivedata(drive);
	config &= 0xff00;
	config |= timing;
	ide_set_drivedata(drive, (void *)config);
301 302
	spin_unlock_irqrestore(&ht6560b_lock, flags);

Linus Torvalds's avatar
Linus Torvalds committed
303 304 305 306 307
#ifdef DEBUG
	printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing);
#endif
}

308
static void __init ht6560b_init_dev(ide_drive_t *drive)
309
{
310
	ide_hwif_t *hwif = drive->hwif;
311 312 313 314 315 316
	/* Setting default configurations for drives. */
	int t = (HT_CONFIG_DEFAULT << 8) | HT_TIMING_DEFAULT;

	if (hwif->channel)
		t |= (HT_SECONDARY_IF << 8);

317
	ide_set_drivedata(drive, (void *)t);
318 319
}

320
static bool probe_ht6560b;
321 322 323 324

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

325 326 327 328 329 330 331 332 333 334 335 336 337 338
static const struct ide_tp_ops ht6560b_tp_ops = {
	.exec_command		= ide_exec_command,
	.read_status		= ide_read_status,
	.read_altstatus		= ide_read_altstatus,
	.write_devctl		= ide_write_devctl,

	.dev_select		= ht6560b_dev_select,
	.tf_load		= ide_tf_load,
	.tf_read		= ide_tf_read,

	.input_data		= ide_input_data,
	.output_data		= ide_output_data,
};

339
static const struct ide_port_ops ht6560b_port_ops = {
340
	.init_dev		= ht6560b_init_dev,
341 342 343
	.set_pio_mode		= ht6560b_set_pio_mode,
};

344
static const struct ide_port_info ht6560b_port_info __initconst = {
345
	.name			= DRV_NAME,
346
	.chipset		= ide_ht6560b,
347
	.tp_ops 		= &ht6560b_tp_ops,
348
	.port_ops		= &ht6560b_port_ops,
349 350 351
	.host_flags		= IDE_HFLAG_SERIALIZE | /* is this needed? */
				  IDE_HFLAG_NO_DMA |
				  IDE_HFLAG_ABUSE_PREFETCH,
352
	.pio_mask		= ATA_PIO4,
353 354
};

355
static int __init ht6560b_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
356
{
357 358 359
	if (probe_ht6560b == 0)
		return -ENODEV;

360
	if (!request_region(HT_CONFIG_PORT, 1, DRV_NAME)) {
Linus Torvalds's avatar
Linus Torvalds committed
361
		printk(KERN_NOTICE "%s: HT_CONFIG_PORT not found\n",
362
			__func__);
Linus Torvalds's avatar
Linus Torvalds committed
363 364 365 366
		return -ENODEV;
	}

	if (!try_to_init_ht6560b()) {
367
		printk(KERN_NOTICE "%s: HBA not found\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
368 369 370
		goto release_region;
	}

371
	return ide_legacy_device_add(&ht6560b_port_info, 0);
Linus Torvalds's avatar
Linus Torvalds committed
372 373 374 375 376 377 378 379 380 381 382

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");