^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Principal Author/Maintainer: PODIEN@hml2.atlas.de (Wolfram Podien)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * This file provides support for the advanced features
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * of the UMC 8672 IDE interface.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * Version 0.01 Initial version, hacked out of ide.c,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * and #include'd rather than compiled separately.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * This will get cleaned up in a subsequent release.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * Version 0.02 now configs/compiles separate from ide.c -ml
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * Version 0.03 enhanced auto-tune, fix display bug
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * Version 0.05 replace sti() with restore_flags() -ml
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * add detection of possible race condition -ml
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * VLB Controller Support from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * Wolfram Podien
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * Rohoefe 3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * D28832 Achim
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * Germany
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * To enable UMC8672 support there must a lilo line like
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * append="ide0=umc8672"...
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * To set the speed according to the abilities of the hardware there must be a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * line like
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * #define UMC_DRIVE0 11
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * are some lines present). 0 - 11 are allowed speed values. These values are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * the results from the DOS speed test program supplied from UMC. 11 is the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * highest speed (about PIO mode 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #define REALLY_SLOW_IO /* some systems can safely undef this */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #include <linux/timer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #include <linux/mm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) #include <linux/blkdev.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #include <linux/ide.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #include <asm/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #define DRV_NAME "umc8672"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) * Default speeds. These can be changed with "auto-tune" and/or hdparm.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #define UMC_DRIVE0 1 /* DOS measured drive speeds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #define UMC_DRIVE1 1 /* 0 to 11 allowed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) #define UMC_DRIVE2 1 /* 11 = Fastest Speed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #define UMC_DRIVE3 1 /* In case of crash reduce speed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static u8 current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) static const u8 pio_to_umc [5] = {0, 3, 7, 10, 11}; /* rough guesses */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) /* 0 1 2 3 4 5 6 7 8 9 10 11 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static const u8 speedtab [3][12] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {0x0f, 0x0b, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) {0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) {0xff, 0xcb, 0xc0, 0x58, 0x36, 0x33, 0x23, 0x22, 0x21, 0x11, 0x10, 0x0}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) static void out_umc(char port, char wert)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) outb_p(port, 0x108);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) outb_p(wert, 0x109);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) static inline u8 in_umc(char port)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) outb_p(port, 0x108);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) return inb_p(0x109);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) static void umc_set_speeds(u8 speeds[])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) int i, tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) outb_p(0x5A, 0x108); /* enable umc */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) out_umc(0xd7, (speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) out_umc(0xd6, (speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) tmp = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) for (i = 3; i >= 0; i--)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) tmp = (tmp << 2) | speedtab[1][speeds[i]];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) out_umc(0xdc, tmp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) for (i = 0; i < 4; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) out_umc(0xd0 + i, speedtab[2][speeds[i]]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) out_umc(0xd8 + i, speedtab[2][speeds[i]]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) outb_p(0xa5, 0x108); /* disable umc */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) printk("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) speeds[0], speeds[1], speeds[2], speeds[3]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) static void umc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) ide_hwif_t *mate = hwif->mate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) const u8 pio = drive->pio_mode - XFER_PIO_0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) printk("%s: setting umc8672 to PIO mode%d (speed %d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) drive->name, pio, pio_to_umc[pio]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if (mate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) spin_lock_irqsave(&mate->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) if (mate && mate->handler) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) printk(KERN_ERR "umc8672: other interface is busy: exiting tune_umc()\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) umc_set_speeds(current_speeds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (mate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) spin_unlock_irqrestore(&mate->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) static const struct ide_port_ops umc8672_port_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) .set_pio_mode = umc_set_pio_mode,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) static const struct ide_port_info umc8672_port_info __initconst = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) .name = DRV_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .chipset = ide_umc8672,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .port_ops = &umc8672_port_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .host_flags = IDE_HFLAG_NO_DMA,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .pio_mask = ATA_PIO4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static int __init umc8672_probe(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (!request_region(0x108, 2, "umc8672")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) printk(KERN_ERR "umc8672: ports 0x108-0x109 already in use.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) outb_p(0x5A, 0x108); /* enable umc */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) if (in_umc (0xd5) != 0xa0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) printk(KERN_ERR "umc8672: not found\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) release_region(0x108, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) outb_p(0xa5, 0x108); /* disable umc */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) umc_set_speeds(current_speeds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) return ide_legacy_device_add(&umc8672_port_info, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static bool probe_umc8672;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) module_param_named(probe, probe_umc8672, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) MODULE_PARM_DESC(probe, "probe for UMC8672 chipset");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) static int __init umc8672_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) if (probe_umc8672 == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) if (umc8672_probe() == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) module_init(umc8672_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) MODULE_AUTHOR("Wolfram Podien");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) MODULE_LICENSE("GPL");