18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 1995-1996 Linus Torvalds & author (see below) 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * Principal Author/Maintainer: PODIEN@hml2.atlas.de (Wolfram Podien) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file provides support for the advanced features 108c2ecf20Sopenharmony_ci * of the UMC 8672 IDE interface. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Version 0.01 Initial version, hacked out of ide.c, 138c2ecf20Sopenharmony_ci * and #include'd rather than compiled separately. 148c2ecf20Sopenharmony_ci * This will get cleaned up in a subsequent release. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Version 0.02 now configs/compiles separate from ide.c -ml 178c2ecf20Sopenharmony_ci * Version 0.03 enhanced auto-tune, fix display bug 188c2ecf20Sopenharmony_ci * Version 0.05 replace sti() with restore_flags() -ml 198c2ecf20Sopenharmony_ci * add detection of possible race condition -ml 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * VLB Controller Support from 248c2ecf20Sopenharmony_ci * Wolfram Podien 258c2ecf20Sopenharmony_ci * Rohoefe 3 268c2ecf20Sopenharmony_ci * D28832 Achim 278c2ecf20Sopenharmony_ci * Germany 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * To enable UMC8672 support there must a lilo line like 308c2ecf20Sopenharmony_ci * append="ide0=umc8672"... 318c2ecf20Sopenharmony_ci * To set the speed according to the abilities of the hardware there must be a 328c2ecf20Sopenharmony_ci * line like 338c2ecf20Sopenharmony_ci * #define UMC_DRIVE0 11 348c2ecf20Sopenharmony_ci * in the beginning of the driver, which sets the speed of drive 0 to 11 (there 358c2ecf20Sopenharmony_ci * are some lines present). 0 - 11 are allowed speed values. These values are 368c2ecf20Sopenharmony_ci * the results from the DOS speed test program supplied from UMC. 11 is the 378c2ecf20Sopenharmony_ci * highest speed (about PIO mode 3) 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci#define REALLY_SLOW_IO /* some systems can safely undef this */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include <linux/module.h> 428c2ecf20Sopenharmony_ci#include <linux/types.h> 438c2ecf20Sopenharmony_ci#include <linux/kernel.h> 448c2ecf20Sopenharmony_ci#include <linux/delay.h> 458c2ecf20Sopenharmony_ci#include <linux/timer.h> 468c2ecf20Sopenharmony_ci#include <linux/mm.h> 478c2ecf20Sopenharmony_ci#include <linux/ioport.h> 488c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 498c2ecf20Sopenharmony_ci#include <linux/ide.h> 508c2ecf20Sopenharmony_ci#include <linux/init.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include <asm/io.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define DRV_NAME "umc8672" 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Default speeds. These can be changed with "auto-tune" and/or hdparm. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci#define UMC_DRIVE0 1 /* DOS measured drive speeds */ 608c2ecf20Sopenharmony_ci#define UMC_DRIVE1 1 /* 0 to 11 allowed */ 618c2ecf20Sopenharmony_ci#define UMC_DRIVE2 1 /* 11 = Fastest Speed */ 628c2ecf20Sopenharmony_ci#define UMC_DRIVE3 1 /* In case of crash reduce speed */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic u8 current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3}; 658c2ecf20Sopenharmony_cistatic const u8 pio_to_umc [5] = {0, 3, 7, 10, 11}; /* rough guesses */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 0 1 2 3 4 5 6 7 8 9 10 11 */ 688c2ecf20Sopenharmony_cistatic const u8 speedtab [3][12] = { 698c2ecf20Sopenharmony_ci {0x0f, 0x0b, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1}, 708c2ecf20Sopenharmony_ci {0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1}, 718c2ecf20Sopenharmony_ci {0xff, 0xcb, 0xc0, 0x58, 0x36, 0x33, 0x23, 0x22, 0x21, 0x11, 0x10, 0x0} 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void out_umc(char port, char wert) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci outb_p(port, 0x108); 778c2ecf20Sopenharmony_ci outb_p(wert, 0x109); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline u8 in_umc(char port) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci outb_p(port, 0x108); 838c2ecf20Sopenharmony_ci return inb_p(0x109); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void umc_set_speeds(u8 speeds[]) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int i, tmp; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci outb_p(0x5A, 0x108); /* enable umc */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci out_umc(0xd7, (speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4))); 938c2ecf20Sopenharmony_ci out_umc(0xd6, (speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4))); 948c2ecf20Sopenharmony_ci tmp = 0; 958c2ecf20Sopenharmony_ci for (i = 3; i >= 0; i--) 968c2ecf20Sopenharmony_ci tmp = (tmp << 2) | speedtab[1][speeds[i]]; 978c2ecf20Sopenharmony_ci out_umc(0xdc, tmp); 988c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 998c2ecf20Sopenharmony_ci out_umc(0xd0 + i, speedtab[2][speeds[i]]); 1008c2ecf20Sopenharmony_ci out_umc(0xd8 + i, speedtab[2][speeds[i]]); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci outb_p(0xa5, 0x108); /* disable umc */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci printk("umc8672: drive speeds [0 to 11]: %d %d %d %d\n", 1058c2ecf20Sopenharmony_ci speeds[0], speeds[1], speeds[2], speeds[3]); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void umc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci ide_hwif_t *mate = hwif->mate; 1118c2ecf20Sopenharmony_ci unsigned long flags; 1128c2ecf20Sopenharmony_ci const u8 pio = drive->pio_mode - XFER_PIO_0; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", 1158c2ecf20Sopenharmony_ci drive->name, pio, pio_to_umc[pio]); 1168c2ecf20Sopenharmony_ci if (mate) 1178c2ecf20Sopenharmony_ci spin_lock_irqsave(&mate->lock, flags); 1188c2ecf20Sopenharmony_ci if (mate && mate->handler) { 1198c2ecf20Sopenharmony_ci printk(KERN_ERR "umc8672: other interface is busy: exiting tune_umc()\n"); 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio]; 1228c2ecf20Sopenharmony_ci umc_set_speeds(current_speeds); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci if (mate) 1258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mate->lock, flags); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct ide_port_ops umc8672_port_ops = { 1298c2ecf20Sopenharmony_ci .set_pio_mode = umc_set_pio_mode, 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic const struct ide_port_info umc8672_port_info __initconst = { 1338c2ecf20Sopenharmony_ci .name = DRV_NAME, 1348c2ecf20Sopenharmony_ci .chipset = ide_umc8672, 1358c2ecf20Sopenharmony_ci .port_ops = &umc8672_port_ops, 1368c2ecf20Sopenharmony_ci .host_flags = IDE_HFLAG_NO_DMA, 1378c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int __init umc8672_probe(void) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci unsigned long flags; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!request_region(0x108, 2, "umc8672")) { 1458c2ecf20Sopenharmony_ci printk(KERN_ERR "umc8672: ports 0x108-0x109 already in use.\n"); 1468c2ecf20Sopenharmony_ci return 1; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci local_irq_save(flags); 1498c2ecf20Sopenharmony_ci outb_p(0x5A, 0x108); /* enable umc */ 1508c2ecf20Sopenharmony_ci if (in_umc (0xd5) != 0xa0) { 1518c2ecf20Sopenharmony_ci local_irq_restore(flags); 1528c2ecf20Sopenharmony_ci printk(KERN_ERR "umc8672: not found\n"); 1538c2ecf20Sopenharmony_ci release_region(0x108, 2); 1548c2ecf20Sopenharmony_ci return 1; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci outb_p(0xa5, 0x108); /* disable umc */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci umc_set_speeds(current_speeds); 1598c2ecf20Sopenharmony_ci local_irq_restore(flags); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return ide_legacy_device_add(&umc8672_port_info, 0); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic bool probe_umc8672; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cimodule_param_named(probe, probe_umc8672, bool, 0); 1678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(probe, "probe for UMC8672 chipset"); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int __init umc8672_init(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (probe_umc8672 == 0) 1728c2ecf20Sopenharmony_ci goto out; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (umc8672_probe() == 0) 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ciout: 1778c2ecf20Sopenharmony_ci return -ENODEV; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cimodule_init(umc8672_init); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Wolfram Podien"); 1838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support for UMC 8672 IDE chipset"); 1848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 185