umc8672.c 5.1 KB
Newer Older
L
Linus Torvalds 已提交
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
/*
 *  linux/drivers/ide/legacy/umc8672.c		Version 0.05	Jul 31, 1996
 *
 *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
 */

/*
 *  Principal Author/Maintainer:  PODIEN@hml2.atlas.de (Wolfram Podien)
 *
 *  This file provides support for the advanced features
 *  of the UMC 8672 IDE interface.
 *
 *  Version 0.01	Initial version, hacked out of ide.c,
 *			and #include'd rather than compiled separately.
 *			This will get cleaned up in a subsequent release.
 *
 *  Version 0.02	now configs/compiles separate from ide.c  -ml
 *  Version 0.03	enhanced auto-tune, fix display bug
 *  Version 0.05	replace sti() with restore_flags()  -ml
 *			add detection of possible race condition  -ml
 */

/*
 * VLB Controller Support from 
 * Wolfram Podien
 * Rohoefe 3
 * D28832 Achim
 * Germany
 *
 * To enable UMC8672 support there must a lilo line like
 * append="ide0=umc8672"...
 * To set the speed according to the abilities of the hardware there must be a
 * line like
 * #define UMC_DRIVE0 11
 * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
 * are some lines present). 0 - 11 are allowed speed values. These values are
 * the results from the DOS speed test program supplied from UMC. 11 is the 
 * highest speed (about PIO mode 3)
 */
#define REALLY_SLOW_IO		/* some systems can safely undef this */

#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>

/*
 * Default speeds.  These can be changed with "auto-tune" and/or hdparm.
 */
#define UMC_DRIVE0      1              /* DOS measured drive speeds */
#define UMC_DRIVE1      1              /* 0 to 11 allowed */
#define UMC_DRIVE2      1              /* 11 = Fastest Speed */
#define UMC_DRIVE3      1              /* In case of crash reduce speed */

static u8 current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
static const u8 pio_to_umc [5] = {0,3,7,10,11};	/* rough guesses */

/*       0    1    2    3    4    5    6    7    8    9    10   11      */
static const u8 speedtab [3][12] = {
	{0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
	{0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
	{0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};

static void out_umc (char port,char wert)
{
	outb_p(port,0x108);
	outb_p(wert,0x109);
}

static inline u8 in_umc (char port)
{
	outb_p(port,0x108);
	return inb_p(0x109);
}

static void umc_set_speeds (u8 speeds[])
{
	int i, tmp;

	outb_p(0x5A,0x108); /* enable umc */

	out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
	out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
	tmp = 0;
	for (i = 3; i >= 0; i--) {
		tmp = (tmp << 2) | speedtab[1][speeds[i]];
	}
	out_umc (0xdc,tmp);
	for (i = 0;i < 4; i++) {
		out_umc (0xd0+i,speedtab[2][speeds[i]]);
		out_umc (0xd8+i,speedtab[2][speeds[i]]);
	}
	outb_p(0xa5,0x108); /* disable umc */

	printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
		speeds[0], speeds[1], speeds[2], speeds[3]);
}

static void tune_umc (ide_drive_t *drive, u8 pio)
{
	unsigned long flags;
	ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup;

113
	pio = ide_get_best_pio_mode(drive, pio, 4);
L
Linus Torvalds 已提交
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 142 143 144 145 146 147 148 149 150 151
	printk("%s: setting umc8672 to PIO mode%d (speed %d)\n",
		drive->name, pio, pio_to_umc[pio]);
	spin_lock_irqsave(&ide_lock, flags);
	if (hwgroup && hwgroup->handler != NULL) {
		printk(KERN_ERR "umc8672: other interface is busy: exiting tune_umc()\n");
	} else {
		current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
		umc_set_speeds (current_speeds);
	}
	spin_unlock_irqrestore(&ide_lock, flags);
}

static int __init umc8672_probe(void)
{
	unsigned long flags;
	ide_hwif_t *hwif, *mate;

	if (!request_region(0x108, 2, "umc8672")) {
		printk(KERN_ERR "umc8672: ports 0x108-0x109 already in use.\n");
		return 1;
	}
	local_irq_save(flags);
	outb_p(0x5A,0x108); /* enable umc */
	if (in_umc (0xd5) != 0xa0) {
		local_irq_restore(flags);
		printk(KERN_ERR "umc8672: not found\n");
		release_region(0x108, 2);
		return 1;  
	}
	outb_p(0xa5,0x108); /* disable umc */

	umc_set_speeds (current_speeds);
	local_irq_restore(flags);

	hwif = &ide_hwifs[0];
	mate = &ide_hwifs[1];

	hwif->chipset = ide_umc8672;
B
Bartlomiej Zolnierkiewicz 已提交
152
	hwif->pio_mask = ATA_PIO4;
L
Linus Torvalds 已提交
153 154 155 156
	hwif->tuneproc = &tune_umc;
	hwif->mate = mate;

	mate->chipset = ide_umc8672;
B
Bartlomiej Zolnierkiewicz 已提交
157
	mate->pio_mask = ATA_PIO4;
L
Linus Torvalds 已提交
158 159 160 161 162 163 164
	mate->tuneproc = &tune_umc;
	mate->mate = hwif;
	mate->channel = 1;

	probe_hwif_init(hwif);
	probe_hwif_init(mate);

165 166
	ide_proc_register_port(hwif);
	ide_proc_register_port(mate);
L
Linus Torvalds 已提交
167 168 169 170

	return 0;
}

171 172 173 174 175
int probe_umc8672 = 0;

module_param_named(probe, probe_umc8672, bool, 0);
MODULE_PARM_DESC(probe, "probe for UMC8672 chipset");

L
Linus Torvalds 已提交
176 177 178
/* Can be called directly from ide.c. */
int __init umc8672_init(void)
{
179 180 181 182 183 184 185
	if (probe_umc8672 == 0)
		goto out;

	if (umc8672_probe() == 0)
		return 0;;
out:
	return -ENODEV;;
L
Linus Torvalds 已提交
186 187 188 189 190 191 192 193 194
}

#ifdef MODULE
module_init(umc8672_init);
#endif

MODULE_AUTHOR("Wolfram Podien");
MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset");
MODULE_LICENSE("GPL");