18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/*
78c2ecf20Sopenharmony_ci *  Original authors:	abramov@cecmow.enet.dec.com (Igor Abramov)
88c2ecf20Sopenharmony_ci *			mlord@pobox.com (Mark Lord)
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  See linux/MAINTAINERS for address of current maintainer.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *  This file provides support for the advanced features and bugs
138c2ecf20Sopenharmony_ci *  of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *  These chips are basically fucked by design, and getting this driver
168c2ecf20Sopenharmony_ci *  to work on every motherboard design that uses this screwed chip seems
178c2ecf20Sopenharmony_ci *  bloody well impossible.  However, we're still trying.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *  Version 0.97 worked for everybody.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *  User feedback is essential.  Many thanks to the beta test team:
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci *  A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
248c2ecf20Sopenharmony_ci *  bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
258c2ecf20Sopenharmony_ci *  chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de,
268c2ecf20Sopenharmony_ci *  derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
278c2ecf20Sopenharmony_ci *  flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
288c2ecf20Sopenharmony_ci *  j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
298c2ecf20Sopenharmony_ci *  kerouac@ssnet.com, meskes@informatik.rwth-aachen.de, hzoli@cs.elte.hu,
308c2ecf20Sopenharmony_ci *  peter@udgaard.isgtec.com, phil@tazenda.demon.co.uk, roadcapw@cfw.com,
318c2ecf20Sopenharmony_ci *  s0033las@sun10.vsz.bme.hu, schaffer@tam.cornell.edu, sjd@slip.net,
328c2ecf20Sopenharmony_ci *  steve@ei.org, ulrpeg@bigcomm.gun.de, ism@tardis.ed.ac.uk, mack@cray.com
338c2ecf20Sopenharmony_ci *  liug@mama.indstate.edu, and others.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci *  Version 0.01	Initial version, hacked out of ide.c,
368c2ecf20Sopenharmony_ci *			and #include'd rather than compiled separately.
378c2ecf20Sopenharmony_ci *			This will get cleaned up in a subsequent release.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci *  Version 0.02	Fixes for vlb initialization code, enable prefetch
408c2ecf20Sopenharmony_ci *			for versions 'B' and 'C' of chip by default,
418c2ecf20Sopenharmony_ci *			some code cleanup.
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci *  Version 0.03	Added reset of secondary interface,
448c2ecf20Sopenharmony_ci *			and black list for devices which are not compatible
458c2ecf20Sopenharmony_ci *			with prefetch mode. Separate function for setting
468c2ecf20Sopenharmony_ci *			prefetch is added, possibly it will be called some
478c2ecf20Sopenharmony_ci *			day from ioctl processing code.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci *  Version 0.04	Now configs/compiles separate from ide.c
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci *  Version 0.05	Major rewrite of interface timing code.
528c2ecf20Sopenharmony_ci *			Added new function cmd640_set_mode to set PIO mode
538c2ecf20Sopenharmony_ci *			from ioctl call. New drives added to black list.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci *  Version 0.06	More code cleanup. Prefetch is enabled only for
568c2ecf20Sopenharmony_ci *			detected hard drives, not included in prefetch
578c2ecf20Sopenharmony_ci *			black list.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *  Version 0.07	Changed to more conservative drive tuning policy.
608c2ecf20Sopenharmony_ci *			Unknown drives, which report PIO < 4 are set to
618c2ecf20Sopenharmony_ci *			(reported_PIO - 1) if it is supported, or to PIO0.
628c2ecf20Sopenharmony_ci *			List of known drives extended by info provided by
638c2ecf20Sopenharmony_ci *			CMD at their ftp site.
648c2ecf20Sopenharmony_ci *
658c2ecf20Sopenharmony_ci *  Version 0.08	Added autotune/noautotune support.
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci *  Version 0.09	Try to be smarter about 2nd port enabling.
688c2ecf20Sopenharmony_ci *  Version 0.10	Be nice and don't reset 2nd port.
698c2ecf20Sopenharmony_ci *  Version 0.11	Try to handle more weird situations.
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci *  Version 0.12	Lots of bug fixes from Laszlo Peter
728c2ecf20Sopenharmony_ci *			irq unmasking disabled for reliability.
738c2ecf20Sopenharmony_ci *			try to be even smarter about the second port.
748c2ecf20Sopenharmony_ci *			tidy up source code formatting.
758c2ecf20Sopenharmony_ci *  Version 0.13	permit irq unmasking again.
768c2ecf20Sopenharmony_ci *  Version 0.90	massive code cleanup, some bugs fixed.
778c2ecf20Sopenharmony_ci *			defaults all drives to PIO mode0, prefetch off.
788c2ecf20Sopenharmony_ci *			autotune is OFF by default, with compile time flag.
798c2ecf20Sopenharmony_ci *			prefetch can be turned OFF/ON using "hdparm -p8/-p9"
808c2ecf20Sopenharmony_ci *			 (requires hdparm-3.1 or newer)
818c2ecf20Sopenharmony_ci *  Version 0.91	first release to linux-kernel list.
828c2ecf20Sopenharmony_ci *  Version 0.92	move initial reg dump to separate callable function
838c2ecf20Sopenharmony_ci *			change "readahead" to "prefetch" to avoid confusion
848c2ecf20Sopenharmony_ci *  Version 0.95	respect original BIOS timings unless autotuning.
858c2ecf20Sopenharmony_ci *			tons of code cleanup and rearrangement.
868c2ecf20Sopenharmony_ci *			added CONFIG_BLK_DEV_CMD640_ENHANCED option
878c2ecf20Sopenharmony_ci *			prevent use of unmask when prefetch is on
888c2ecf20Sopenharmony_ci *  Version 0.96	prevent use of io_32bit when prefetch is off
898c2ecf20Sopenharmony_ci *  Version 0.97	fix VLB secondary interface for sjd@slip.net
908c2ecf20Sopenharmony_ci *			other minor tune-ups:  0.96 was very good.
918c2ecf20Sopenharmony_ci *  Version 0.98	ignore PCI version when disabled by BIOS
928c2ecf20Sopenharmony_ci *  Version 0.99	display setup/active/recovery clocks with PIO mode
938c2ecf20Sopenharmony_ci *  Version 1.00	Mmm.. cannot depend on PCMD_ENA in all systems
948c2ecf20Sopenharmony_ci *  Version 1.01	slow/fast devsel can be selected with "hdparm -p6/-p7"
958c2ecf20Sopenharmony_ci *			 ("fast" is necessary for 32bit I/O in some systems)
968c2ecf20Sopenharmony_ci *  Version 1.02	fix bug that resulted in slow "setup times"
978c2ecf20Sopenharmony_ci *			 (patch courtesy of Zoltan Hidvegi)
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define CMD640_PREFETCH_MASKS 1
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*#define CMD640_DUMP_REGS */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#include <linux/types.h>
1058c2ecf20Sopenharmony_ci#include <linux/kernel.h>
1068c2ecf20Sopenharmony_ci#include <linux/delay.h>
1078c2ecf20Sopenharmony_ci#include <linux/ide.h>
1088c2ecf20Sopenharmony_ci#include <linux/init.h>
1098c2ecf20Sopenharmony_ci#include <linux/module.h>
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci#include <asm/io.h>
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define DRV_NAME "cmd640"
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic bool cmd640_vlb;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci * CMD640 specific registers definition.
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#define VID		0x00
1228c2ecf20Sopenharmony_ci#define DID		0x02
1238c2ecf20Sopenharmony_ci#define PCMD		0x04
1248c2ecf20Sopenharmony_ci#define   PCMD_ENA	0x01
1258c2ecf20Sopenharmony_ci#define PSTTS		0x06
1268c2ecf20Sopenharmony_ci#define REVID		0x08
1278c2ecf20Sopenharmony_ci#define PROGIF		0x09
1288c2ecf20Sopenharmony_ci#define SUBCL		0x0a
1298c2ecf20Sopenharmony_ci#define BASCL		0x0b
1308c2ecf20Sopenharmony_ci#define BaseA0		0x10
1318c2ecf20Sopenharmony_ci#define BaseA1		0x14
1328c2ecf20Sopenharmony_ci#define BaseA2		0x18
1338c2ecf20Sopenharmony_ci#define BaseA3		0x1c
1348c2ecf20Sopenharmony_ci#define INTLINE		0x3c
1358c2ecf20Sopenharmony_ci#define INPINE		0x3d
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#define	CFR		0x50
1388c2ecf20Sopenharmony_ci#define   CFR_DEVREV		0x03
1398c2ecf20Sopenharmony_ci#define   CFR_IDE01INTR		0x04
1408c2ecf20Sopenharmony_ci#define	  CFR_DEVID		0x18
1418c2ecf20Sopenharmony_ci#define	  CFR_AT_VESA_078h	0x20
1428c2ecf20Sopenharmony_ci#define	  CFR_DSA1		0x40
1438c2ecf20Sopenharmony_ci#define	  CFR_DSA0		0x80
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci#define CNTRL		0x51
1468c2ecf20Sopenharmony_ci#define	  CNTRL_DIS_RA0		0x40
1478c2ecf20Sopenharmony_ci#define   CNTRL_DIS_RA1		0x80
1488c2ecf20Sopenharmony_ci#define	  CNTRL_ENA_2ND		0x08
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#define	CMDTIM		0x52
1518c2ecf20Sopenharmony_ci#define	ARTTIM0		0x53
1528c2ecf20Sopenharmony_ci#define	DRWTIM0		0x54
1538c2ecf20Sopenharmony_ci#define ARTTIM1 	0x55
1548c2ecf20Sopenharmony_ci#define DRWTIM1		0x56
1558c2ecf20Sopenharmony_ci#define ARTTIM23	0x57
1568c2ecf20Sopenharmony_ci#define   ARTTIM23_DIS_RA2	0x04
1578c2ecf20Sopenharmony_ci#define   ARTTIM23_DIS_RA3	0x08
1588c2ecf20Sopenharmony_ci#define   ARTTIM23_IDE23INTR	0x10
1598c2ecf20Sopenharmony_ci#define DRWTIM23	0x58
1608c2ecf20Sopenharmony_ci#define BRST		0x59
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/*
1638c2ecf20Sopenharmony_ci * Registers and masks for easy access by drive index:
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic u8 prefetch_regs[4]  = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
1668c2ecf20Sopenharmony_cistatic u8 prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
1718c2ecf20Sopenharmony_cistatic u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/*
1748c2ecf20Sopenharmony_ci * Current cmd640 timing values for each drive.
1758c2ecf20Sopenharmony_ci * The defaults for each are the slowest possible timings.
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_cistatic u8 setup_counts[4]    = {4, 4, 4, 4};     /* Address setup count (in clocks) */
1788c2ecf20Sopenharmony_cistatic u8 active_counts[4]   = {16, 16, 16, 16}; /* Active count   (encoded) */
1798c2ecf20Sopenharmony_cistatic u8 recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cmd640_lock);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/*
1868c2ecf20Sopenharmony_ci * Interface to access cmd640x registers
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_cistatic unsigned int cmd640_key;
1898c2ecf20Sopenharmony_cistatic void (*__put_cmd640_reg)(u16 reg, u8 val);
1908c2ecf20Sopenharmony_cistatic u8 (*__get_cmd640_reg)(u16 reg);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/*
1938c2ecf20Sopenharmony_ci * This is read from the CFR reg, and is used in several places.
1948c2ecf20Sopenharmony_ci */
1958c2ecf20Sopenharmony_cistatic unsigned int cmd640_chip_version;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*
1988c2ecf20Sopenharmony_ci * The CMD640x chip does not support DWORD config write cycles, but some
1998c2ecf20Sopenharmony_ci * of the BIOSes use them to implement the config services.
2008c2ecf20Sopenharmony_ci * Therefore, we must use direct IO instead.
2018c2ecf20Sopenharmony_ci */
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/* PCI method 1 access */
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void put_cmd640_reg_pci1(u16 reg, u8 val)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
2088c2ecf20Sopenharmony_ci	outb_p(val, (reg & 3) | 0xcfc);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic u8 get_cmd640_reg_pci1(u16 reg)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
2148c2ecf20Sopenharmony_ci	return inb_p((reg & 3) | 0xcfc);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/* PCI method 2 access (from CMD datasheet) */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void put_cmd640_reg_pci2(u16 reg, u8 val)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	outb_p(0x10, 0xcf8);
2228c2ecf20Sopenharmony_ci	outb_p(val, cmd640_key + reg);
2238c2ecf20Sopenharmony_ci	outb_p(0, 0xcf8);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic u8 get_cmd640_reg_pci2(u16 reg)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	u8 b;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	outb_p(0x10, 0xcf8);
2318c2ecf20Sopenharmony_ci	b = inb_p(cmd640_key + reg);
2328c2ecf20Sopenharmony_ci	outb_p(0, 0xcf8);
2338c2ecf20Sopenharmony_ci	return b;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/* VLB access */
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic void put_cmd640_reg_vlb(u16 reg, u8 val)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	outb_p(reg, cmd640_key);
2418c2ecf20Sopenharmony_ci	outb_p(val, cmd640_key + 4);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic u8 get_cmd640_reg_vlb(u16 reg)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	outb_p(reg, cmd640_key);
2478c2ecf20Sopenharmony_ci	return inb_p(cmd640_key + 4);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic u8 get_cmd640_reg(u16 reg)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	unsigned long flags;
2538c2ecf20Sopenharmony_ci	u8 b;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd640_lock, flags);
2568c2ecf20Sopenharmony_ci	b = __get_cmd640_reg(reg);
2578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd640_lock, flags);
2588c2ecf20Sopenharmony_ci	return b;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic void put_cmd640_reg(u16 reg, u8 val)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	unsigned long flags;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd640_lock, flags);
2668c2ecf20Sopenharmony_ci	__put_cmd640_reg(reg, val);
2678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd640_lock, flags);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int __init match_pci_cmd640_device(void)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	const u8 ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
2738c2ecf20Sopenharmony_ci	unsigned int i;
2748c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
2758c2ecf20Sopenharmony_ci		if (get_cmd640_reg(i) != ven_dev[i])
2768c2ecf20Sopenharmony_ci			return 0;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci#ifdef STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT
2798c2ecf20Sopenharmony_ci	if ((get_cmd640_reg(PCMD) & PCMD_ENA) == 0) {
2808c2ecf20Sopenharmony_ci		printk("ide: cmd640 on PCI disabled by BIOS\n");
2818c2ecf20Sopenharmony_ci		return 0;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci#endif /* STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT */
2848c2ecf20Sopenharmony_ci	return 1; /* success */
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * Probe for CMD640x -- pci method 1
2898c2ecf20Sopenharmony_ci */
2908c2ecf20Sopenharmony_cistatic int __init probe_for_cmd640_pci1(void)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	__get_cmd640_reg = get_cmd640_reg_pci1;
2938c2ecf20Sopenharmony_ci	__put_cmd640_reg = put_cmd640_reg_pci1;
2948c2ecf20Sopenharmony_ci	for (cmd640_key = 0x80000000;
2958c2ecf20Sopenharmony_ci	     cmd640_key <= 0x8000f800;
2968c2ecf20Sopenharmony_ci	     cmd640_key += 0x800) {
2978c2ecf20Sopenharmony_ci		if (match_pci_cmd640_device())
2988c2ecf20Sopenharmony_ci			return 1; /* success */
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	return 0;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * Probe for CMD640x -- pci method 2
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_cistatic int __init probe_for_cmd640_pci2(void)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	__get_cmd640_reg = get_cmd640_reg_pci2;
3098c2ecf20Sopenharmony_ci	__put_cmd640_reg = put_cmd640_reg_pci2;
3108c2ecf20Sopenharmony_ci	for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) {
3118c2ecf20Sopenharmony_ci		if (match_pci_cmd640_device())
3128c2ecf20Sopenharmony_ci			return 1; /* success */
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/*
3188c2ecf20Sopenharmony_ci * Probe for CMD640x -- vlb
3198c2ecf20Sopenharmony_ci */
3208c2ecf20Sopenharmony_cistatic int __init probe_for_cmd640_vlb(void)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	u8 b;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	__get_cmd640_reg = get_cmd640_reg_vlb;
3258c2ecf20Sopenharmony_ci	__put_cmd640_reg = put_cmd640_reg_vlb;
3268c2ecf20Sopenharmony_ci	cmd640_key = 0x178;
3278c2ecf20Sopenharmony_ci	b = get_cmd640_reg(CFR);
3288c2ecf20Sopenharmony_ci	if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) {
3298c2ecf20Sopenharmony_ci		cmd640_key = 0x78;
3308c2ecf20Sopenharmony_ci		b = get_cmd640_reg(CFR);
3318c2ecf20Sopenharmony_ci		if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h))
3328c2ecf20Sopenharmony_ci			return 0;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci	return 1; /* success */
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/*
3388c2ecf20Sopenharmony_ci *  Returns 1 if an IDE interface/drive exists at 0x170,
3398c2ecf20Sopenharmony_ci *  Returns 0 otherwise.
3408c2ecf20Sopenharmony_ci */
3418c2ecf20Sopenharmony_cistatic int __init secondary_port_responding(void)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	unsigned long flags;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd640_lock, flags);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	outb_p(0x0a, 0x176);	/* select drive0 */
3488c2ecf20Sopenharmony_ci	udelay(100);
3498c2ecf20Sopenharmony_ci	if ((inb_p(0x176) & 0x1f) != 0x0a) {
3508c2ecf20Sopenharmony_ci		outb_p(0x1a, 0x176); /* select drive1 */
3518c2ecf20Sopenharmony_ci		udelay(100);
3528c2ecf20Sopenharmony_ci		if ((inb_p(0x176) & 0x1f) != 0x1a) {
3538c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&cmd640_lock, flags);
3548c2ecf20Sopenharmony_ci			return 0; /* nothing responded */
3558c2ecf20Sopenharmony_ci		}
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd640_lock, flags);
3588c2ecf20Sopenharmony_ci	return 1; /* success */
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci#ifdef CMD640_DUMP_REGS
3628c2ecf20Sopenharmony_ci/*
3638c2ecf20Sopenharmony_ci * Dump out all cmd640 registers.  May be called from ide.c
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_cistatic void cmd640_dump_regs(void)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	unsigned int reg = cmd640_vlb ? 0x50 : 0x00;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* Dump current state of chip registers */
3708c2ecf20Sopenharmony_ci	printk("ide: cmd640 internal register dump:");
3718c2ecf20Sopenharmony_ci	for (; reg <= 0x59; reg++) {
3728c2ecf20Sopenharmony_ci		if (!(reg & 0x0f))
3738c2ecf20Sopenharmony_ci			printk("\n%04x:", reg);
3748c2ecf20Sopenharmony_ci		printk(" %02x", get_cmd640_reg(reg));
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci	printk("\n");
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci#endif
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic void __set_prefetch_mode(ide_drive_t *drive, int mode)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	if (mode) {	/* want prefetch on? */
3838c2ecf20Sopenharmony_ci#if CMD640_PREFETCH_MASKS
3848c2ecf20Sopenharmony_ci		drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
3858c2ecf20Sopenharmony_ci		drive->dev_flags &= ~IDE_DFLAG_UNMASK;
3868c2ecf20Sopenharmony_ci#endif
3878c2ecf20Sopenharmony_ci		drive->dev_flags &= ~IDE_DFLAG_NO_IO_32BIT;
3888c2ecf20Sopenharmony_ci	} else {
3898c2ecf20Sopenharmony_ci		drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK;
3908c2ecf20Sopenharmony_ci		drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT;
3918c2ecf20Sopenharmony_ci		drive->io_32bit = 0;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci#ifndef CONFIG_BLK_DEV_CMD640_ENHANCED
3968c2ecf20Sopenharmony_ci/*
3978c2ecf20Sopenharmony_ci * Check whether prefetch is on for a drive,
3988c2ecf20Sopenharmony_ci * and initialize the unmask flags for safe operation.
3998c2ecf20Sopenharmony_ci */
4008c2ecf20Sopenharmony_cistatic void __init check_prefetch(ide_drive_t *drive, unsigned int index)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	u8 b = get_cmd640_reg(prefetch_regs[index]);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	__set_prefetch_mode(drive, (b & prefetch_masks[index]) ? 0 : 1);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci#else
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/*
4098c2ecf20Sopenharmony_ci * Sets prefetch mode for a drive.
4108c2ecf20Sopenharmony_ci */
4118c2ecf20Sopenharmony_cistatic void set_prefetch_mode(ide_drive_t *drive, unsigned int index, int mode)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	unsigned long flags;
4148c2ecf20Sopenharmony_ci	int reg = prefetch_regs[index];
4158c2ecf20Sopenharmony_ci	u8 b;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd640_lock, flags);
4188c2ecf20Sopenharmony_ci	b = __get_cmd640_reg(reg);
4198c2ecf20Sopenharmony_ci	__set_prefetch_mode(drive, mode);
4208c2ecf20Sopenharmony_ci	if (mode)
4218c2ecf20Sopenharmony_ci		b &= ~prefetch_masks[index];	/* enable prefetch */
4228c2ecf20Sopenharmony_ci	else
4238c2ecf20Sopenharmony_ci		b |= prefetch_masks[index];	/* disable prefetch */
4248c2ecf20Sopenharmony_ci	__put_cmd640_reg(reg, b);
4258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd640_lock, flags);
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci/*
4298c2ecf20Sopenharmony_ci * Dump out current drive clocks settings
4308c2ecf20Sopenharmony_ci */
4318c2ecf20Sopenharmony_cistatic void display_clocks(unsigned int index)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	u8 active_count, recovery_count;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	active_count = active_counts[index];
4368c2ecf20Sopenharmony_ci	if (active_count == 1)
4378c2ecf20Sopenharmony_ci		++active_count;
4388c2ecf20Sopenharmony_ci	recovery_count = recovery_counts[index];
4398c2ecf20Sopenharmony_ci	if (active_count > 3 && recovery_count == 1)
4408c2ecf20Sopenharmony_ci		++recovery_count;
4418c2ecf20Sopenharmony_ci	if (cmd640_chip_version > 1)
4428c2ecf20Sopenharmony_ci		recovery_count += 1;  /* cmd640b uses (count + 1)*/
4438c2ecf20Sopenharmony_ci	printk(", clocks=%d/%d/%d\n", setup_counts[index], active_count, recovery_count);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci/*
4478c2ecf20Sopenharmony_ci * Pack active and recovery counts into single byte representation
4488c2ecf20Sopenharmony_ci * used by controller
4498c2ecf20Sopenharmony_ci */
4508c2ecf20Sopenharmony_cistatic inline u8 pack_nibbles(u8 upper, u8 lower)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	return ((upper & 0x0f) << 4) | (lower & 0x0f);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci/*
4568c2ecf20Sopenharmony_ci * This routine writes the prepared setup/active/recovery counts
4578c2ecf20Sopenharmony_ci * for a drive into the cmd640 chipset registers to active them.
4588c2ecf20Sopenharmony_ci */
4598c2ecf20Sopenharmony_cistatic void program_drive_counts(ide_drive_t *drive, unsigned int index)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	unsigned long flags;
4628c2ecf20Sopenharmony_ci	u8 setup_count    = setup_counts[index];
4638c2ecf20Sopenharmony_ci	u8 active_count   = active_counts[index];
4648c2ecf20Sopenharmony_ci	u8 recovery_count = recovery_counts[index];
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/*
4678c2ecf20Sopenharmony_ci	 * Set up address setup count and drive read/write timing registers.
4688c2ecf20Sopenharmony_ci	 * Primary interface has individual count/timing registers for
4698c2ecf20Sopenharmony_ci	 * each drive.  Secondary interface has one common set of registers,
4708c2ecf20Sopenharmony_ci	 * so we merge the timings, using the slowest value for each timing.
4718c2ecf20Sopenharmony_ci	 */
4728c2ecf20Sopenharmony_ci	if (index > 1) {
4738c2ecf20Sopenharmony_ci		ide_drive_t *peer = ide_get_pair_dev(drive);
4748c2ecf20Sopenharmony_ci		unsigned int mate = index ^ 1;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		if (peer) {
4778c2ecf20Sopenharmony_ci			if (setup_count < setup_counts[mate])
4788c2ecf20Sopenharmony_ci				setup_count = setup_counts[mate];
4798c2ecf20Sopenharmony_ci			if (active_count < active_counts[mate])
4808c2ecf20Sopenharmony_ci				active_count = active_counts[mate];
4818c2ecf20Sopenharmony_ci			if (recovery_count < recovery_counts[mate])
4828c2ecf20Sopenharmony_ci				recovery_count = recovery_counts[mate];
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/*
4878c2ecf20Sopenharmony_ci	 * Convert setup_count to internal chipset representation
4888c2ecf20Sopenharmony_ci	 */
4898c2ecf20Sopenharmony_ci	switch (setup_count) {
4908c2ecf20Sopenharmony_ci	case 4:	 setup_count = 0x00; break;
4918c2ecf20Sopenharmony_ci	case 3:	 setup_count = 0x80; break;
4928c2ecf20Sopenharmony_ci	case 1:
4938c2ecf20Sopenharmony_ci	case 2:	 setup_count = 0x40; break;
4948c2ecf20Sopenharmony_ci	default: setup_count = 0xc0; /* case 5 */
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/*
4988c2ecf20Sopenharmony_ci	 * Now that everything is ready, program the new timings
4998c2ecf20Sopenharmony_ci	 */
5008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd640_lock, flags);
5018c2ecf20Sopenharmony_ci	/*
5028c2ecf20Sopenharmony_ci	 * Program the address_setup clocks into ARTTIM reg,
5038c2ecf20Sopenharmony_ci	 * and then the active/recovery counts into the DRWTIM reg
5048c2ecf20Sopenharmony_ci	 * (this converts counts of 16 into counts of zero -- okay).
5058c2ecf20Sopenharmony_ci	 */
5068c2ecf20Sopenharmony_ci	setup_count |= __get_cmd640_reg(arttim_regs[index]) & 0x3f;
5078c2ecf20Sopenharmony_ci	__put_cmd640_reg(arttim_regs[index], setup_count);
5088c2ecf20Sopenharmony_ci	__put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count));
5098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd640_lock, flags);
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci/*
5138c2ecf20Sopenharmony_ci * Set a specific pio_mode for a drive
5148c2ecf20Sopenharmony_ci */
5158c2ecf20Sopenharmony_cistatic void cmd640_set_mode(ide_drive_t *drive, unsigned int index,
5168c2ecf20Sopenharmony_ci			    u8 pio_mode, unsigned int cycle_time)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct ide_timing *t;
5198c2ecf20Sopenharmony_ci	int setup_time, active_time, recovery_time, clock_time;
5208c2ecf20Sopenharmony_ci	u8 setup_count, active_count, recovery_count, recovery_count2, cycle_count;
5218c2ecf20Sopenharmony_ci	int bus_speed;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (cmd640_vlb)
5248c2ecf20Sopenharmony_ci		bus_speed = ide_vlb_clk ? ide_vlb_clk : 50;
5258c2ecf20Sopenharmony_ci	else
5268c2ecf20Sopenharmony_ci		bus_speed = ide_pci_clk ? ide_pci_clk : 33;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (pio_mode > 5)
5298c2ecf20Sopenharmony_ci		pio_mode = 5;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	t = ide_timing_find_mode(XFER_PIO_0 + pio_mode);
5328c2ecf20Sopenharmony_ci	setup_time  = t->setup;
5338c2ecf20Sopenharmony_ci	active_time = t->active;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	recovery_time = cycle_time - (setup_time + active_time);
5368c2ecf20Sopenharmony_ci	clock_time = 1000 / bus_speed;
5378c2ecf20Sopenharmony_ci	cycle_count = DIV_ROUND_UP(cycle_time, clock_time);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	setup_count = DIV_ROUND_UP(setup_time, clock_time);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	active_count = DIV_ROUND_UP(active_time, clock_time);
5428c2ecf20Sopenharmony_ci	if (active_count < 2)
5438c2ecf20Sopenharmony_ci		active_count = 2; /* minimum allowed by cmd640 */
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	recovery_count = DIV_ROUND_UP(recovery_time, clock_time);
5468c2ecf20Sopenharmony_ci	recovery_count2 = cycle_count - (setup_count + active_count);
5478c2ecf20Sopenharmony_ci	if (recovery_count2 > recovery_count)
5488c2ecf20Sopenharmony_ci		recovery_count = recovery_count2;
5498c2ecf20Sopenharmony_ci	if (recovery_count < 2)
5508c2ecf20Sopenharmony_ci		recovery_count = 2; /* minimum allowed by cmd640 */
5518c2ecf20Sopenharmony_ci	if (recovery_count > 17) {
5528c2ecf20Sopenharmony_ci		active_count += recovery_count - 17;
5538c2ecf20Sopenharmony_ci		recovery_count = 17;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci	if (active_count > 16)
5568c2ecf20Sopenharmony_ci		active_count = 16; /* maximum allowed by cmd640 */
5578c2ecf20Sopenharmony_ci	if (cmd640_chip_version > 1)
5588c2ecf20Sopenharmony_ci		recovery_count -= 1;  /* cmd640b uses (count + 1)*/
5598c2ecf20Sopenharmony_ci	if (recovery_count > 16)
5608c2ecf20Sopenharmony_ci		recovery_count = 16; /* maximum allowed by cmd640 */
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	setup_counts[index]    = setup_count;
5638c2ecf20Sopenharmony_ci	active_counts[index]   = active_count;
5648c2ecf20Sopenharmony_ci	recovery_counts[index] = recovery_count;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/*
5678c2ecf20Sopenharmony_ci	 * In a perfect world, we might set the drive pio mode here
5688c2ecf20Sopenharmony_ci	 * (using WIN_SETFEATURE) before continuing.
5698c2ecf20Sopenharmony_ci	 *
5708c2ecf20Sopenharmony_ci	 * But we do not, because:
5718c2ecf20Sopenharmony_ci	 *	1) this is the wrong place to do it (proper is do_special() in ide.c)
5728c2ecf20Sopenharmony_ci	 * 	2) in practice this is rarely, if ever, necessary
5738c2ecf20Sopenharmony_ci	 */
5748c2ecf20Sopenharmony_ci	program_drive_counts(drive, index);
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic void cmd640_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	unsigned int index = 0, cycle_time;
5808c2ecf20Sopenharmony_ci	const u8 pio = drive->pio_mode - XFER_PIO_0;
5818c2ecf20Sopenharmony_ci	u8 b;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	switch (pio) {
5848c2ecf20Sopenharmony_ci	case 6: /* set fast-devsel off */
5858c2ecf20Sopenharmony_ci	case 7: /* set fast-devsel on */
5868c2ecf20Sopenharmony_ci		b = get_cmd640_reg(CNTRL) & ~0x27;
5878c2ecf20Sopenharmony_ci		if (pio & 1)
5888c2ecf20Sopenharmony_ci			b |= 0x27;
5898c2ecf20Sopenharmony_ci		put_cmd640_reg(CNTRL, b);
5908c2ecf20Sopenharmony_ci		printk("%s: %sabled cmd640 fast host timing (devsel)\n",
5918c2ecf20Sopenharmony_ci			drive->name, (pio & 1) ? "en" : "dis");
5928c2ecf20Sopenharmony_ci		return;
5938c2ecf20Sopenharmony_ci	case 8: /* set prefetch off */
5948c2ecf20Sopenharmony_ci	case 9: /* set prefetch on */
5958c2ecf20Sopenharmony_ci		set_prefetch_mode(drive, index, pio & 1);
5968c2ecf20Sopenharmony_ci		printk("%s: %sabled cmd640 prefetch\n",
5978c2ecf20Sopenharmony_ci			drive->name, (pio & 1) ? "en" : "dis");
5988c2ecf20Sopenharmony_ci		return;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	cycle_time = ide_pio_cycle_time(drive, pio);
6028c2ecf20Sopenharmony_ci	cmd640_set_mode(drive, index, pio, cycle_time);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	printk("%s: selected cmd640 PIO mode%d (%dns)",
6058c2ecf20Sopenharmony_ci		drive->name, pio, cycle_time);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	display_clocks(index);
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic void __init cmd640_init_dev(ide_drive_t *drive)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	unsigned int i = drive->hwif->channel * 2 + (drive->dn & 1);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
6168c2ecf20Sopenharmony_ci	/*
6178c2ecf20Sopenharmony_ci	 * Reset timing to the slowest speed and turn off prefetch.
6188c2ecf20Sopenharmony_ci	 * This way, the drive identify code has a better chance.
6198c2ecf20Sopenharmony_ci	 */
6208c2ecf20Sopenharmony_ci	setup_counts[i]    =  4;	/* max possible */
6218c2ecf20Sopenharmony_ci	active_counts[i]   = 16;	/* max possible */
6228c2ecf20Sopenharmony_ci	recovery_counts[i] = 16;	/* max possible */
6238c2ecf20Sopenharmony_ci	program_drive_counts(drive, i);
6248c2ecf20Sopenharmony_ci	set_prefetch_mode(drive, i, 0);
6258c2ecf20Sopenharmony_ci	printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch cleared\n", i);
6268c2ecf20Sopenharmony_ci#else
6278c2ecf20Sopenharmony_ci	/*
6288c2ecf20Sopenharmony_ci	 * Set the drive unmask flags to match the prefetch setting.
6298c2ecf20Sopenharmony_ci	 */
6308c2ecf20Sopenharmony_ci	check_prefetch(drive, i);
6318c2ecf20Sopenharmony_ci	printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch(%s) preserved\n",
6328c2ecf20Sopenharmony_ci		i, (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT) ? "off" : "on");
6338c2ecf20Sopenharmony_ci#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic int cmd640_test_irq(ide_hwif_t *hwif)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	int irq_reg		= hwif->channel ? ARTTIM23 : CFR;
6398c2ecf20Sopenharmony_ci	u8  irq_mask		= hwif->channel ? ARTTIM23_IDE23INTR :
6408c2ecf20Sopenharmony_ci						  CFR_IDE01INTR;
6418c2ecf20Sopenharmony_ci	u8  irq_stat		= get_cmd640_reg(irq_reg);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return (irq_stat & irq_mask) ? 1 : 0;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic const struct ide_port_ops cmd640_port_ops = {
6478c2ecf20Sopenharmony_ci	.init_dev		= cmd640_init_dev,
6488c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
6498c2ecf20Sopenharmony_ci	.set_pio_mode		= cmd640_set_pio_mode,
6508c2ecf20Sopenharmony_ci#endif
6518c2ecf20Sopenharmony_ci	.test_irq		= cmd640_test_irq,
6528c2ecf20Sopenharmony_ci};
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic int pci_conf1(void)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	unsigned long flags;
6578c2ecf20Sopenharmony_ci	u32 tmp;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd640_lock, flags);
6608c2ecf20Sopenharmony_ci	outb(0x01, 0xCFB);
6618c2ecf20Sopenharmony_ci	tmp = inl(0xCF8);
6628c2ecf20Sopenharmony_ci	outl(0x80000000, 0xCF8);
6638c2ecf20Sopenharmony_ci	if (inl(0xCF8) == 0x80000000) {
6648c2ecf20Sopenharmony_ci		outl(tmp, 0xCF8);
6658c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&cmd640_lock, flags);
6668c2ecf20Sopenharmony_ci		return 1;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci	outl(tmp, 0xCF8);
6698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd640_lock, flags);
6708c2ecf20Sopenharmony_ci	return 0;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic int pci_conf2(void)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	unsigned long flags;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd640_lock, flags);
6788c2ecf20Sopenharmony_ci	outb(0x00, 0xCFB);
6798c2ecf20Sopenharmony_ci	outb(0x00, 0xCF8);
6808c2ecf20Sopenharmony_ci	outb(0x00, 0xCFA);
6818c2ecf20Sopenharmony_ci	if (inb(0xCF8) == 0x00 && inb(0xCF8) == 0x00) {
6828c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&cmd640_lock, flags);
6838c2ecf20Sopenharmony_ci		return 1;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd640_lock, flags);
6868c2ecf20Sopenharmony_ci	return 0;
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic const struct ide_port_info cmd640_port_info __initconst = {
6908c2ecf20Sopenharmony_ci	.chipset		= ide_cmd640,
6918c2ecf20Sopenharmony_ci	.host_flags		= IDE_HFLAG_SERIALIZE |
6928c2ecf20Sopenharmony_ci				  IDE_HFLAG_NO_DMA |
6938c2ecf20Sopenharmony_ci				  IDE_HFLAG_ABUSE_PREFETCH |
6948c2ecf20Sopenharmony_ci				  IDE_HFLAG_ABUSE_FAST_DEVSEL,
6958c2ecf20Sopenharmony_ci	.port_ops		= &cmd640_port_ops,
6968c2ecf20Sopenharmony_ci	.pio_mask		= ATA_PIO5,
6978c2ecf20Sopenharmony_ci};
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic int __init cmd640x_init_one(unsigned long base, unsigned long ctl)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	if (!request_region(base, 8, DRV_NAME)) {
7028c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
7038c2ecf20Sopenharmony_ci				DRV_NAME, base, base + 7);
7048c2ecf20Sopenharmony_ci		return -EBUSY;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (!request_region(ctl, 1, DRV_NAME)) {
7088c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n",
7098c2ecf20Sopenharmony_ci				DRV_NAME, ctl);
7108c2ecf20Sopenharmony_ci		release_region(base, 8);
7118c2ecf20Sopenharmony_ci		return -EBUSY;
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	return 0;
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci/*
7188c2ecf20Sopenharmony_ci * Probe for a cmd640 chipset, and initialize it if found.
7198c2ecf20Sopenharmony_ci */
7208c2ecf20Sopenharmony_cistatic int __init cmd640x_init(void)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	int second_port_cmd640 = 0, rc;
7238c2ecf20Sopenharmony_ci	const char *bus_type, *port2;
7248c2ecf20Sopenharmony_ci	u8 b, cfr;
7258c2ecf20Sopenharmony_ci	struct ide_hw hw[2], *hws[2];
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (cmd640_vlb && probe_for_cmd640_vlb()) {
7288c2ecf20Sopenharmony_ci		bus_type = "VLB";
7298c2ecf20Sopenharmony_ci	} else {
7308c2ecf20Sopenharmony_ci		cmd640_vlb = 0;
7318c2ecf20Sopenharmony_ci		/* Find out what kind of PCI probing is supported otherwise
7328c2ecf20Sopenharmony_ci		   Justin Gibbs will sulk.. */
7338c2ecf20Sopenharmony_ci		if (pci_conf1() && probe_for_cmd640_pci1())
7348c2ecf20Sopenharmony_ci			bus_type = "PCI (type1)";
7358c2ecf20Sopenharmony_ci		else if (pci_conf2() && probe_for_cmd640_pci2())
7368c2ecf20Sopenharmony_ci			bus_type = "PCI (type2)";
7378c2ecf20Sopenharmony_ci		else
7388c2ecf20Sopenharmony_ci			return 0;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci	/*
7418c2ecf20Sopenharmony_ci	 * Undocumented magic (there is no 0x5b reg in specs)
7428c2ecf20Sopenharmony_ci	 */
7438c2ecf20Sopenharmony_ci	put_cmd640_reg(0x5b, 0xbd);
7448c2ecf20Sopenharmony_ci	if (get_cmd640_reg(0x5b) != 0xbd) {
7458c2ecf20Sopenharmony_ci		printk(KERN_ERR "ide: cmd640 init failed: wrong value in reg 0x5b\n");
7468c2ecf20Sopenharmony_ci		return 0;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci	put_cmd640_reg(0x5b, 0);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci#ifdef CMD640_DUMP_REGS
7518c2ecf20Sopenharmony_ci	cmd640_dump_regs();
7528c2ecf20Sopenharmony_ci#endif
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	/*
7558c2ecf20Sopenharmony_ci	 * Documented magic begins here
7568c2ecf20Sopenharmony_ci	 */
7578c2ecf20Sopenharmony_ci	cfr = get_cmd640_reg(CFR);
7588c2ecf20Sopenharmony_ci	cmd640_chip_version = cfr & CFR_DEVREV;
7598c2ecf20Sopenharmony_ci	if (cmd640_chip_version == 0) {
7608c2ecf20Sopenharmony_ci		printk("ide: bad cmd640 revision: %d\n", cmd640_chip_version);
7618c2ecf20Sopenharmony_ci		return 0;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	rc = cmd640x_init_one(0x1f0, 0x3f6);
7658c2ecf20Sopenharmony_ci	if (rc)
7668c2ecf20Sopenharmony_ci		return rc;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	rc = cmd640x_init_one(0x170, 0x376);
7698c2ecf20Sopenharmony_ci	if (rc) {
7708c2ecf20Sopenharmony_ci		release_region(0x3f6, 1);
7718c2ecf20Sopenharmony_ci		release_region(0x1f0, 8);
7728c2ecf20Sopenharmony_ci		return rc;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	memset(&hw, 0, sizeof(hw));
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	ide_std_init_ports(&hw[0], 0x1f0, 0x3f6);
7788c2ecf20Sopenharmony_ci	hw[0].irq = 14;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	ide_std_init_ports(&hw[1], 0x170, 0x376);
7818c2ecf20Sopenharmony_ci	hw[1].irq = 15;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x"
7848c2ecf20Sopenharmony_ci			 "\n", 'a' + cmd640_chip_version - 1, bus_type, cfr);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/*
7878c2ecf20Sopenharmony_ci	 * Initialize data for primary port
7888c2ecf20Sopenharmony_ci	 */
7898c2ecf20Sopenharmony_ci	hws[0] = &hw[0];
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/*
7928c2ecf20Sopenharmony_ci	 * Ensure compatibility by always using the slowest timings
7938c2ecf20Sopenharmony_ci	 * for access to the drive's command register block,
7948c2ecf20Sopenharmony_ci	 * and reset the prefetch burstsize to default (512 bytes).
7958c2ecf20Sopenharmony_ci	 *
7968c2ecf20Sopenharmony_ci	 * Maybe we need a way to NOT do these on *some* systems?
7978c2ecf20Sopenharmony_ci	 */
7988c2ecf20Sopenharmony_ci	put_cmd640_reg(CMDTIM, 0);
7998c2ecf20Sopenharmony_ci	put_cmd640_reg(BRST, 0x40);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	b = get_cmd640_reg(CNTRL);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/*
8048c2ecf20Sopenharmony_ci	 * Try to enable the secondary interface, if not already enabled
8058c2ecf20Sopenharmony_ci	 */
8068c2ecf20Sopenharmony_ci	if (secondary_port_responding()) {
8078c2ecf20Sopenharmony_ci		if ((b & CNTRL_ENA_2ND)) {
8088c2ecf20Sopenharmony_ci			second_port_cmd640 = 1;
8098c2ecf20Sopenharmony_ci			port2 = "okay";
8108c2ecf20Sopenharmony_ci		} else if (cmd640_vlb) {
8118c2ecf20Sopenharmony_ci			second_port_cmd640 = 1;
8128c2ecf20Sopenharmony_ci			port2 = "alive";
8138c2ecf20Sopenharmony_ci		} else
8148c2ecf20Sopenharmony_ci			port2 = "not cmd640";
8158c2ecf20Sopenharmony_ci	} else {
8168c2ecf20Sopenharmony_ci		put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
8178c2ecf20Sopenharmony_ci		if (secondary_port_responding()) {
8188c2ecf20Sopenharmony_ci			second_port_cmd640 = 1;
8198c2ecf20Sopenharmony_ci			port2 = "enabled";
8208c2ecf20Sopenharmony_ci		} else {
8218c2ecf20Sopenharmony_ci			put_cmd640_reg(CNTRL, b); /* restore original setting */
8228c2ecf20Sopenharmony_ci			port2 = "not responding";
8238c2ecf20Sopenharmony_ci		}
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/*
8278c2ecf20Sopenharmony_ci	 * Initialize data for secondary cmd640 port, if enabled
8288c2ecf20Sopenharmony_ci	 */
8298c2ecf20Sopenharmony_ci	if (second_port_cmd640)
8308c2ecf20Sopenharmony_ci		hws[1] = &hw[1];
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	printk(KERN_INFO "cmd640: %sserialized, secondary interface %s\n",
8338c2ecf20Sopenharmony_ci			 second_port_cmd640 ? "" : "not ", port2);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci#ifdef CMD640_DUMP_REGS
8368c2ecf20Sopenharmony_ci	cmd640_dump_regs();
8378c2ecf20Sopenharmony_ci#endif
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	return ide_host_add(&cmd640_port_info, hws, second_port_cmd640 ? 2 : 1,
8408c2ecf20Sopenharmony_ci			    NULL);
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cimodule_param_named(probe_vlb, cmd640_vlb, bool, 0);
8448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(probe_vlb, "probe for VLB version of CMD640 chipset");
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cimodule_init(cmd640x_init);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
849