162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Low-level parallel-port routines for 8255-based PC-style hardware.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Authors: Phil Blundell <philb@gnu.org>
562306a36Sopenharmony_ci *          Tim Waugh <tim@cyberelk.demon.co.uk>
662306a36Sopenharmony_ci *	    Jose Renau <renau@acm.org>
762306a36Sopenharmony_ci *          David Campbell
862306a36Sopenharmony_ci *          Andrea Arcangeli
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Cleaned up include files - Russell King <linux@arm.uk.linux.org>
1362306a36Sopenharmony_ci * DMA support - Bert De Jonghe <bert@sophis.be>
1462306a36Sopenharmony_ci * Many ECP bugs fixed.  Fred Barnes & Jamie Lokier, 1999
1562306a36Sopenharmony_ci * More PCI support now conditional on CONFIG_PCI, 03/2001, Paul G.
1662306a36Sopenharmony_ci * Various hacks, Fred Barnes, 04/2001
1762306a36Sopenharmony_ci * Updated probing logic - Adam Belay <ambx1@neo.rr.com>
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* This driver should work with any hardware that is broadly compatible
2162306a36Sopenharmony_ci * with that in the IBM PC.  This applies to the majority of integrated
2262306a36Sopenharmony_ci * I/O chipsets that are commonly available.  The expected register
2362306a36Sopenharmony_ci * layout is:
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci *	base+0		data
2662306a36Sopenharmony_ci *	base+1		status
2762306a36Sopenharmony_ci *	base+2		control
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * In addition, there are some optional registers:
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci *	base+3		EPP address
3262306a36Sopenharmony_ci *	base+4		EPP data
3362306a36Sopenharmony_ci *	base+0x400	ECP config A
3462306a36Sopenharmony_ci *	base+0x401	ECP config B
3562306a36Sopenharmony_ci *	base+0x402	ECP control
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * All registers are 8 bits wide and read/write.  If your hardware differs
3862306a36Sopenharmony_ci * only in register addresses (eg because your registers are on 32-bit
3962306a36Sopenharmony_ci * word boundaries) then you can alter the constants in parport_pc.h to
4062306a36Sopenharmony_ci * accommodate this.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Note that the ECP registers may not start at offset 0x400 for PCI cards,
4362306a36Sopenharmony_ci * but rather will start at port->base_hi.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <linux/module.h>
4762306a36Sopenharmony_ci#include <linux/init.h>
4862306a36Sopenharmony_ci#include <linux/sched/signal.h>
4962306a36Sopenharmony_ci#include <linux/delay.h>
5062306a36Sopenharmony_ci#include <linux/errno.h>
5162306a36Sopenharmony_ci#include <linux/interrupt.h>
5262306a36Sopenharmony_ci#include <linux/ioport.h>
5362306a36Sopenharmony_ci#include <linux/kernel.h>
5462306a36Sopenharmony_ci#include <linux/slab.h>
5562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
5662306a36Sopenharmony_ci#include <linux/pci.h>
5762306a36Sopenharmony_ci#include <linux/pnp.h>
5862306a36Sopenharmony_ci#include <linux/platform_device.h>
5962306a36Sopenharmony_ci#include <linux/sysctl.h>
6062306a36Sopenharmony_ci#include <linux/io.h>
6162306a36Sopenharmony_ci#include <linux/uaccess.h>
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#include <asm/dma.h>
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#include <linux/parport.h>
6662306a36Sopenharmony_ci#include <linux/parport_pc.h>
6762306a36Sopenharmony_ci#include <linux/via.h>
6862306a36Sopenharmony_ci#include <asm/parport.h>
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define PARPORT_PC_MAX_PORTS PARPORT_MAX
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#ifdef CONFIG_ISA_DMA_API
7362306a36Sopenharmony_ci#define HAS_DMA
7462306a36Sopenharmony_ci#endif
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* ECR modes */
7762306a36Sopenharmony_ci#define ECR_SPP 00
7862306a36Sopenharmony_ci#define ECR_PS2 01
7962306a36Sopenharmony_ci#define ECR_PPF 02
8062306a36Sopenharmony_ci#define ECR_ECP 03
8162306a36Sopenharmony_ci#define ECR_EPP 04
8262306a36Sopenharmony_ci#define ECR_VND 05
8362306a36Sopenharmony_ci#define ECR_TST 06
8462306a36Sopenharmony_ci#define ECR_CNF 07
8562306a36Sopenharmony_ci#define ECR_MODE_MASK 0xe0
8662306a36Sopenharmony_ci#define ECR_WRITE(p, v) frob_econtrol((p), 0xff, (v))
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#undef DEBUG
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define NR_SUPERIOS 3
9162306a36Sopenharmony_cistatic struct superio_struct {	/* For Super-IO chips autodetection */
9262306a36Sopenharmony_ci	int io;
9362306a36Sopenharmony_ci	int irq;
9462306a36Sopenharmony_ci	int dma;
9562306a36Sopenharmony_ci} superios[NR_SUPERIOS] = { {0,},};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int user_specified;
9862306a36Sopenharmony_ci#if defined(CONFIG_PARPORT_PC_SUPERIO) || \
9962306a36Sopenharmony_ci       (defined(CONFIG_PARPORT_1284) && defined(CONFIG_PARPORT_PC_FIFO))
10062306a36Sopenharmony_cistatic int verbose_probing;
10162306a36Sopenharmony_ci#endif
10262306a36Sopenharmony_cistatic int pci_registered_parport;
10362306a36Sopenharmony_cistatic int pnp_registered_parport;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* frob_control, but for ECR */
10662306a36Sopenharmony_cistatic void frob_econtrol(struct parport *pb, unsigned char m,
10762306a36Sopenharmony_ci			   unsigned char v)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	const struct parport_pc_private *priv = pb->physport->private_data;
11062306a36Sopenharmony_ci	unsigned char ecr_writable = priv->ecr_writable;
11162306a36Sopenharmony_ci	unsigned char ectr = 0;
11262306a36Sopenharmony_ci	unsigned char new;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (m != 0xff)
11562306a36Sopenharmony_ci		ectr = inb(ECONTROL(pb));
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	new = (ectr & ~m) ^ v;
11862306a36Sopenharmony_ci	if (ecr_writable)
11962306a36Sopenharmony_ci		/* All known users of the ECR mask require bit 0 to be set. */
12062306a36Sopenharmony_ci		new = (new & ecr_writable) | 1;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	pr_debug("frob_econtrol(%02x,%02x): %02x -> %02x\n", m, v, ectr, new);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	outb(new, ECONTROL(pb));
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic inline void frob_set_mode(struct parport *p, int mode)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	frob_econtrol(p, ECR_MODE_MASK, mode << 5);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_FIFO
13362306a36Sopenharmony_ci/* Safely change the mode bits in the ECR
13462306a36Sopenharmony_ci   Returns:
13562306a36Sopenharmony_ci	    0    : Success
13662306a36Sopenharmony_ci	   -EBUSY: Could not drain FIFO in some finite amount of time,
13762306a36Sopenharmony_ci		   mode not changed!
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_cistatic int change_mode(struct parport *p, int m)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	const struct parport_pc_private *priv = p->physport->private_data;
14262306a36Sopenharmony_ci	unsigned char oecr;
14362306a36Sopenharmony_ci	int mode;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	pr_debug("parport change_mode ECP-ISA to mode 0x%02x\n", m);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (!priv->ecr) {
14862306a36Sopenharmony_ci		printk(KERN_DEBUG "change_mode: but there's no ECR!\n");
14962306a36Sopenharmony_ci		return 0;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* Bits <7:5> contain the mode. */
15362306a36Sopenharmony_ci	oecr = inb(ECONTROL(p));
15462306a36Sopenharmony_ci	mode = (oecr >> 5) & 0x7;
15562306a36Sopenharmony_ci	if (mode == m)
15662306a36Sopenharmony_ci		return 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (mode >= 2 && !(priv->ctr & 0x20)) {
15962306a36Sopenharmony_ci		/* This mode resets the FIFO, so we may
16062306a36Sopenharmony_ci		 * have to wait for it to drain first. */
16162306a36Sopenharmony_ci		unsigned long expire = jiffies + p->physport->cad->timeout;
16262306a36Sopenharmony_ci		int counter;
16362306a36Sopenharmony_ci		switch (mode) {
16462306a36Sopenharmony_ci		case ECR_PPF: /* Parallel Port FIFO mode */
16562306a36Sopenharmony_ci		case ECR_ECP: /* ECP Parallel Port mode */
16662306a36Sopenharmony_ci			/* Busy wait for 200us */
16762306a36Sopenharmony_ci			for (counter = 0; counter < 40; counter++) {
16862306a36Sopenharmony_ci				if (inb(ECONTROL(p)) & 0x01)
16962306a36Sopenharmony_ci					break;
17062306a36Sopenharmony_ci				if (signal_pending(current))
17162306a36Sopenharmony_ci					break;
17262306a36Sopenharmony_ci				udelay(5);
17362306a36Sopenharmony_ci			}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci			/* Poll slowly. */
17662306a36Sopenharmony_ci			while (!(inb(ECONTROL(p)) & 0x01)) {
17762306a36Sopenharmony_ci				if (time_after_eq(jiffies, expire))
17862306a36Sopenharmony_ci					/* The FIFO is stuck. */
17962306a36Sopenharmony_ci					return -EBUSY;
18062306a36Sopenharmony_ci				schedule_timeout_interruptible(
18162306a36Sopenharmony_ci							msecs_to_jiffies(10));
18262306a36Sopenharmony_ci				if (signal_pending(current))
18362306a36Sopenharmony_ci					break;
18462306a36Sopenharmony_ci			}
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (mode >= 2 && m >= 2) {
18962306a36Sopenharmony_ci		/* We have to go through mode 001 */
19062306a36Sopenharmony_ci		oecr &= ~(7 << 5);
19162306a36Sopenharmony_ci		oecr |= ECR_PS2 << 5;
19262306a36Sopenharmony_ci		ECR_WRITE(p, oecr);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Set the mode. */
19662306a36Sopenharmony_ci	oecr &= ~(7 << 5);
19762306a36Sopenharmony_ci	oecr |= m << 5;
19862306a36Sopenharmony_ci	ECR_WRITE(p, oecr);
19962306a36Sopenharmony_ci	return 0;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci#endif /* FIFO support */
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/*
20462306a36Sopenharmony_ci * Clear TIMEOUT BIT in EPP MODE
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * This is also used in SPP detection.
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_cistatic int clear_epp_timeout(struct parport *pb)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	unsigned char r;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!(parport_pc_read_status(pb) & 0x01))
21362306a36Sopenharmony_ci		return 1;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* To clear timeout some chips require double read */
21662306a36Sopenharmony_ci	parport_pc_read_status(pb);
21762306a36Sopenharmony_ci	r = parport_pc_read_status(pb);
21862306a36Sopenharmony_ci	outb(r | 0x01, STATUS(pb)); /* Some reset by writing 1 */
21962306a36Sopenharmony_ci	outb(r & 0xfe, STATUS(pb)); /* Others by writing 0 */
22062306a36Sopenharmony_ci	r = parport_pc_read_status(pb);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return !(r & 0x01);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/*
22662306a36Sopenharmony_ci * Access functions.
22762306a36Sopenharmony_ci *
22862306a36Sopenharmony_ci * Most of these aren't static because they may be used by the
22962306a36Sopenharmony_ci * parport_xxx_yyy macros.  extern __inline__ versions of several
23062306a36Sopenharmony_ci * of these are in parport_pc.h.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic void parport_pc_init_state(struct pardevice *dev,
23462306a36Sopenharmony_ci						struct parport_state *s)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	s->u.pc.ctr = 0xc;
23762306a36Sopenharmony_ci	if (dev->irq_func &&
23862306a36Sopenharmony_ci	    dev->port->irq != PARPORT_IRQ_NONE)
23962306a36Sopenharmony_ci		/* Set ackIntEn */
24062306a36Sopenharmony_ci		s->u.pc.ctr |= 0x10;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	s->u.pc.ecr = 0x34; /* NetMos chip can cause problems 0x24;
24362306a36Sopenharmony_ci			     * D.Gruszka VScom */
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic void parport_pc_save_state(struct parport *p, struct parport_state *s)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	const struct parport_pc_private *priv = p->physport->private_data;
24962306a36Sopenharmony_ci	s->u.pc.ctr = priv->ctr;
25062306a36Sopenharmony_ci	if (priv->ecr)
25162306a36Sopenharmony_ci		s->u.pc.ecr = inb(ECONTROL(p));
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void parport_pc_restore_state(struct parport *p,
25562306a36Sopenharmony_ci						struct parport_state *s)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct parport_pc_private *priv = p->physport->private_data;
25862306a36Sopenharmony_ci	register unsigned char c = s->u.pc.ctr & priv->ctr_writable;
25962306a36Sopenharmony_ci	outb(c, CONTROL(p));
26062306a36Sopenharmony_ci	priv->ctr = c;
26162306a36Sopenharmony_ci	if (priv->ecr)
26262306a36Sopenharmony_ci		ECR_WRITE(p, s->u.pc.ecr);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_1284
26662306a36Sopenharmony_cistatic size_t parport_pc_epp_read_data(struct parport *port, void *buf,
26762306a36Sopenharmony_ci				       size_t length, int flags)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	size_t got = 0;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (flags & PARPORT_W91284PIC) {
27262306a36Sopenharmony_ci		unsigned char status;
27362306a36Sopenharmony_ci		size_t left = length;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		/* use knowledge about data lines..:
27662306a36Sopenharmony_ci		 *  nFault is 0 if there is at least 1 byte in the Warp's FIFO
27762306a36Sopenharmony_ci		 *  pError is 1 if there are 16 bytes in the Warp's FIFO
27862306a36Sopenharmony_ci		 */
27962306a36Sopenharmony_ci		status = inb(STATUS(port));
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		while (!(status & 0x08) && got < length) {
28262306a36Sopenharmony_ci			if (left >= 16 && (status & 0x20) && !(status & 0x08)) {
28362306a36Sopenharmony_ci				/* can grab 16 bytes from warp fifo */
28462306a36Sopenharmony_ci				if (!((long)buf & 0x03))
28562306a36Sopenharmony_ci					insl(EPPDATA(port), buf, 4);
28662306a36Sopenharmony_ci				else
28762306a36Sopenharmony_ci					insb(EPPDATA(port), buf, 16);
28862306a36Sopenharmony_ci				buf += 16;
28962306a36Sopenharmony_ci				got += 16;
29062306a36Sopenharmony_ci				left -= 16;
29162306a36Sopenharmony_ci			} else {
29262306a36Sopenharmony_ci				/* grab single byte from the warp fifo */
29362306a36Sopenharmony_ci				*((char *)buf) = inb(EPPDATA(port));
29462306a36Sopenharmony_ci				buf++;
29562306a36Sopenharmony_ci				got++;
29662306a36Sopenharmony_ci				left--;
29762306a36Sopenharmony_ci			}
29862306a36Sopenharmony_ci			status = inb(STATUS(port));
29962306a36Sopenharmony_ci			if (status & 0x01) {
30062306a36Sopenharmony_ci				/* EPP timeout should never occur... */
30162306a36Sopenharmony_ci				printk(KERN_DEBUG "%s: EPP timeout occurred while talking to w91284pic (should not have done)\n",
30262306a36Sopenharmony_ci				       port->name);
30362306a36Sopenharmony_ci				clear_epp_timeout(port);
30462306a36Sopenharmony_ci			}
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci		return got;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci	if ((length > 1) && ((flags & PARPORT_EPP_FAST_32)
30962306a36Sopenharmony_ci			   || flags & PARPORT_EPP_FAST_16
31062306a36Sopenharmony_ci			   || flags & PARPORT_EPP_FAST_8)) {
31162306a36Sopenharmony_ci		if ((flags & PARPORT_EPP_FAST_32)
31262306a36Sopenharmony_ci		    && !(((long)buf | length) & 0x03))
31362306a36Sopenharmony_ci			insl(EPPDATA(port), buf, (length >> 2));
31462306a36Sopenharmony_ci		else if ((flags & PARPORT_EPP_FAST_16)
31562306a36Sopenharmony_ci			 && !(((long)buf | length) & 0x01))
31662306a36Sopenharmony_ci			insw(EPPDATA(port), buf, length >> 1);
31762306a36Sopenharmony_ci		else
31862306a36Sopenharmony_ci			insb(EPPDATA(port), buf, length);
31962306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
32062306a36Sopenharmony_ci			clear_epp_timeout(port);
32162306a36Sopenharmony_ci			return -EIO;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci		return length;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	for (; got < length; got++) {
32662306a36Sopenharmony_ci		*((char *)buf) = inb(EPPDATA(port));
32762306a36Sopenharmony_ci		buf++;
32862306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
32962306a36Sopenharmony_ci			/* EPP timeout */
33062306a36Sopenharmony_ci			clear_epp_timeout(port);
33162306a36Sopenharmony_ci			break;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return got;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic size_t parport_pc_epp_write_data(struct parport *port, const void *buf,
33962306a36Sopenharmony_ci					size_t length, int flags)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	size_t written = 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if ((length > 1) && ((flags & PARPORT_EPP_FAST_32)
34462306a36Sopenharmony_ci			   || flags & PARPORT_EPP_FAST_16
34562306a36Sopenharmony_ci			   || flags & PARPORT_EPP_FAST_8)) {
34662306a36Sopenharmony_ci		if ((flags & PARPORT_EPP_FAST_32)
34762306a36Sopenharmony_ci		    && !(((long)buf | length) & 0x03))
34862306a36Sopenharmony_ci			outsl(EPPDATA(port), buf, (length >> 2));
34962306a36Sopenharmony_ci		else if ((flags & PARPORT_EPP_FAST_16)
35062306a36Sopenharmony_ci			 && !(((long)buf | length) & 0x01))
35162306a36Sopenharmony_ci			outsw(EPPDATA(port), buf, length >> 1);
35262306a36Sopenharmony_ci		else
35362306a36Sopenharmony_ci			outsb(EPPDATA(port), buf, length);
35462306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
35562306a36Sopenharmony_ci			clear_epp_timeout(port);
35662306a36Sopenharmony_ci			return -EIO;
35762306a36Sopenharmony_ci		}
35862306a36Sopenharmony_ci		return length;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci	for (; written < length; written++) {
36162306a36Sopenharmony_ci		outb(*((char *)buf), EPPDATA(port));
36262306a36Sopenharmony_ci		buf++;
36362306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
36462306a36Sopenharmony_ci			clear_epp_timeout(port);
36562306a36Sopenharmony_ci			break;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return written;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic size_t parport_pc_epp_read_addr(struct parport *port, void *buf,
37362306a36Sopenharmony_ci					size_t length, int flags)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	size_t got = 0;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
37862306a36Sopenharmony_ci		insb(EPPADDR(port), buf, length);
37962306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
38062306a36Sopenharmony_ci			clear_epp_timeout(port);
38162306a36Sopenharmony_ci			return -EIO;
38262306a36Sopenharmony_ci		}
38362306a36Sopenharmony_ci		return length;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	for (; got < length; got++) {
38662306a36Sopenharmony_ci		*((char *)buf) = inb(EPPADDR(port));
38762306a36Sopenharmony_ci		buf++;
38862306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
38962306a36Sopenharmony_ci			clear_epp_timeout(port);
39062306a36Sopenharmony_ci			break;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return got;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic size_t parport_pc_epp_write_addr(struct parport *port,
39862306a36Sopenharmony_ci					 const void *buf, size_t length,
39962306a36Sopenharmony_ci					 int flags)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	size_t written = 0;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
40462306a36Sopenharmony_ci		outsb(EPPADDR(port), buf, length);
40562306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
40662306a36Sopenharmony_ci			clear_epp_timeout(port);
40762306a36Sopenharmony_ci			return -EIO;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci		return length;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci	for (; written < length; written++) {
41262306a36Sopenharmony_ci		outb(*((char *)buf), EPPADDR(port));
41362306a36Sopenharmony_ci		buf++;
41462306a36Sopenharmony_ci		if (inb(STATUS(port)) & 0x01) {
41562306a36Sopenharmony_ci			clear_epp_timeout(port);
41662306a36Sopenharmony_ci			break;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return written;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic size_t parport_pc_ecpepp_read_data(struct parport *port, void *buf,
42462306a36Sopenharmony_ci					  size_t length, int flags)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	size_t got;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	frob_set_mode(port, ECR_EPP);
42962306a36Sopenharmony_ci	parport_pc_data_reverse(port);
43062306a36Sopenharmony_ci	parport_pc_write_control(port, 0x4);
43162306a36Sopenharmony_ci	got = parport_pc_epp_read_data(port, buf, length, flags);
43262306a36Sopenharmony_ci	frob_set_mode(port, ECR_PS2);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return got;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic size_t parport_pc_ecpepp_write_data(struct parport *port,
43862306a36Sopenharmony_ci					   const void *buf, size_t length,
43962306a36Sopenharmony_ci					   int flags)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	size_t written;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	frob_set_mode(port, ECR_EPP);
44462306a36Sopenharmony_ci	parport_pc_write_control(port, 0x4);
44562306a36Sopenharmony_ci	parport_pc_data_forward(port);
44662306a36Sopenharmony_ci	written = parport_pc_epp_write_data(port, buf, length, flags);
44762306a36Sopenharmony_ci	frob_set_mode(port, ECR_PS2);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return written;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic size_t parport_pc_ecpepp_read_addr(struct parport *port, void *buf,
45362306a36Sopenharmony_ci					  size_t length, int flags)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	size_t got;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	frob_set_mode(port, ECR_EPP);
45862306a36Sopenharmony_ci	parport_pc_data_reverse(port);
45962306a36Sopenharmony_ci	parport_pc_write_control(port, 0x4);
46062306a36Sopenharmony_ci	got = parport_pc_epp_read_addr(port, buf, length, flags);
46162306a36Sopenharmony_ci	frob_set_mode(port, ECR_PS2);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return got;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic size_t parport_pc_ecpepp_write_addr(struct parport *port,
46762306a36Sopenharmony_ci					    const void *buf, size_t length,
46862306a36Sopenharmony_ci					    int flags)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	size_t written;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	frob_set_mode(port, ECR_EPP);
47362306a36Sopenharmony_ci	parport_pc_write_control(port, 0x4);
47462306a36Sopenharmony_ci	parport_pc_data_forward(port);
47562306a36Sopenharmony_ci	written = parport_pc_epp_write_addr(port, buf, length, flags);
47662306a36Sopenharmony_ci	frob_set_mode(port, ECR_PS2);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return written;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci#endif /* IEEE 1284 support */
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_FIFO
48362306a36Sopenharmony_cistatic size_t parport_pc_fifo_write_block_pio(struct parport *port,
48462306a36Sopenharmony_ci					       const void *buf, size_t length)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	int ret = 0;
48762306a36Sopenharmony_ci	const unsigned char *bufp = buf;
48862306a36Sopenharmony_ci	size_t left = length;
48962306a36Sopenharmony_ci	unsigned long expire = jiffies + port->physport->cad->timeout;
49062306a36Sopenharmony_ci	const unsigned long fifo = FIFO(port);
49162306a36Sopenharmony_ci	int poll_for = 8; /* 80 usecs */
49262306a36Sopenharmony_ci	const struct parport_pc_private *priv = port->physport->private_data;
49362306a36Sopenharmony_ci	const int fifo_depth = priv->fifo_depth;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	port = port->physport;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* We don't want to be interrupted every character. */
49862306a36Sopenharmony_ci	parport_pc_disable_irq(port);
49962306a36Sopenharmony_ci	/* set nErrIntrEn and serviceIntr */
50062306a36Sopenharmony_ci	frob_econtrol(port, (1<<4) | (1<<2), (1<<4) | (1<<2));
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* Forward mode. */
50362306a36Sopenharmony_ci	parport_pc_data_forward(port); /* Must be in PS2 mode */
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	while (left) {
50662306a36Sopenharmony_ci		unsigned char byte;
50762306a36Sopenharmony_ci		unsigned char ecrval = inb(ECONTROL(port));
50862306a36Sopenharmony_ci		int i = 0;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		if (need_resched() && time_before(jiffies, expire))
51162306a36Sopenharmony_ci			/* Can't yield the port. */
51262306a36Sopenharmony_ci			schedule();
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		/* Anyone else waiting for the port? */
51562306a36Sopenharmony_ci		if (port->waithead) {
51662306a36Sopenharmony_ci			printk(KERN_DEBUG "Somebody wants the port\n");
51762306a36Sopenharmony_ci			break;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (ecrval & 0x02) {
52162306a36Sopenharmony_ci			/* FIFO is full. Wait for interrupt. */
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci			/* Clear serviceIntr */
52462306a36Sopenharmony_ci			ECR_WRITE(port, ecrval & ~(1<<2));
52562306a36Sopenharmony_cifalse_alarm:
52662306a36Sopenharmony_ci			ret = parport_wait_event(port, HZ);
52762306a36Sopenharmony_ci			if (ret < 0)
52862306a36Sopenharmony_ci				break;
52962306a36Sopenharmony_ci			ret = 0;
53062306a36Sopenharmony_ci			if (!time_before(jiffies, expire)) {
53162306a36Sopenharmony_ci				/* Timed out. */
53262306a36Sopenharmony_ci				printk(KERN_DEBUG "FIFO write timed out\n");
53362306a36Sopenharmony_ci				break;
53462306a36Sopenharmony_ci			}
53562306a36Sopenharmony_ci			ecrval = inb(ECONTROL(port));
53662306a36Sopenharmony_ci			if (!(ecrval & (1<<2))) {
53762306a36Sopenharmony_ci				if (need_resched() &&
53862306a36Sopenharmony_ci				    time_before(jiffies, expire))
53962306a36Sopenharmony_ci					schedule();
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci				goto false_alarm;
54262306a36Sopenharmony_ci			}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci			continue;
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		/* Can't fail now. */
54862306a36Sopenharmony_ci		expire = jiffies + port->cad->timeout;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cipoll:
55162306a36Sopenharmony_ci		if (signal_pending(current))
55262306a36Sopenharmony_ci			break;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		if (ecrval & 0x01) {
55562306a36Sopenharmony_ci			/* FIFO is empty. Blast it full. */
55662306a36Sopenharmony_ci			const int n = left < fifo_depth ? left : fifo_depth;
55762306a36Sopenharmony_ci			outsb(fifo, bufp, n);
55862306a36Sopenharmony_ci			bufp += n;
55962306a36Sopenharmony_ci			left -= n;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci			/* Adjust the poll time. */
56262306a36Sopenharmony_ci			if (i < (poll_for - 2))
56362306a36Sopenharmony_ci				poll_for--;
56462306a36Sopenharmony_ci			continue;
56562306a36Sopenharmony_ci		} else if (i++ < poll_for) {
56662306a36Sopenharmony_ci			udelay(10);
56762306a36Sopenharmony_ci			ecrval = inb(ECONTROL(port));
56862306a36Sopenharmony_ci			goto poll;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		/* Half-full(call me an optimist) */
57262306a36Sopenharmony_ci		byte = *bufp++;
57362306a36Sopenharmony_ci		outb(byte, fifo);
57462306a36Sopenharmony_ci		left--;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci	dump_parport_state("leave fifo_write_block_pio", port);
57762306a36Sopenharmony_ci	return length - left;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci#ifdef HAS_DMA
58162306a36Sopenharmony_cistatic size_t parport_pc_fifo_write_block_dma(struct parport *port,
58262306a36Sopenharmony_ci					       const void *buf, size_t length)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	int ret = 0;
58562306a36Sopenharmony_ci	unsigned long dmaflag;
58662306a36Sopenharmony_ci	size_t left = length;
58762306a36Sopenharmony_ci	const struct parport_pc_private *priv = port->physport->private_data;
58862306a36Sopenharmony_ci	struct device *dev = port->physport->dev;
58962306a36Sopenharmony_ci	dma_addr_t dma_addr, dma_handle;
59062306a36Sopenharmony_ci	size_t maxlen = 0x10000; /* max 64k per DMA transfer */
59162306a36Sopenharmony_ci	unsigned long start = (unsigned long) buf;
59262306a36Sopenharmony_ci	unsigned long end = (unsigned long) buf + length - 1;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	dump_parport_state("enter fifo_write_block_dma", port);
59562306a36Sopenharmony_ci	if (end < MAX_DMA_ADDRESS) {
59662306a36Sopenharmony_ci		/* If it would cross a 64k boundary, cap it at the end. */
59762306a36Sopenharmony_ci		if ((start ^ end) & ~0xffffUL)
59862306a36Sopenharmony_ci			maxlen = 0x10000 - (start & 0xffff);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		dma_addr = dma_handle = dma_map_single(dev, (void *)buf, length,
60162306a36Sopenharmony_ci						       DMA_TO_DEVICE);
60262306a36Sopenharmony_ci	} else {
60362306a36Sopenharmony_ci		/* above 16 MB we use a bounce buffer as ISA-DMA
60462306a36Sopenharmony_ci		   is not possible */
60562306a36Sopenharmony_ci		maxlen   = PAGE_SIZE;          /* sizeof(priv->dma_buf) */
60662306a36Sopenharmony_ci		dma_addr = priv->dma_handle;
60762306a36Sopenharmony_ci		dma_handle = 0;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	port = port->physport;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/* We don't want to be interrupted every character. */
61362306a36Sopenharmony_ci	parport_pc_disable_irq(port);
61462306a36Sopenharmony_ci	/* set nErrIntrEn and serviceIntr */
61562306a36Sopenharmony_ci	frob_econtrol(port, (1<<4) | (1<<2), (1<<4) | (1<<2));
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	/* Forward mode. */
61862306a36Sopenharmony_ci	parport_pc_data_forward(port); /* Must be in PS2 mode */
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	while (left) {
62162306a36Sopenharmony_ci		unsigned long expire = jiffies + port->physport->cad->timeout;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci		size_t count = left;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		if (count > maxlen)
62662306a36Sopenharmony_ci			count = maxlen;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		if (!dma_handle)   /* bounce buffer ! */
62962306a36Sopenharmony_ci			memcpy(priv->dma_buf, buf, count);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		dmaflag = claim_dma_lock();
63262306a36Sopenharmony_ci		disable_dma(port->dma);
63362306a36Sopenharmony_ci		clear_dma_ff(port->dma);
63462306a36Sopenharmony_ci		set_dma_mode(port->dma, DMA_MODE_WRITE);
63562306a36Sopenharmony_ci		set_dma_addr(port->dma, dma_addr);
63662306a36Sopenharmony_ci		set_dma_count(port->dma, count);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		/* Set DMA mode */
63962306a36Sopenharmony_ci		frob_econtrol(port, 1<<3, 1<<3);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		/* Clear serviceIntr */
64262306a36Sopenharmony_ci		frob_econtrol(port, 1<<2, 0);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		enable_dma(port->dma);
64562306a36Sopenharmony_ci		release_dma_lock(dmaflag);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		/* assume DMA will be successful */
64862306a36Sopenharmony_ci		left -= count;
64962306a36Sopenharmony_ci		buf  += count;
65062306a36Sopenharmony_ci		if (dma_handle)
65162306a36Sopenharmony_ci			dma_addr += count;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		/* Wait for interrupt. */
65462306a36Sopenharmony_cifalse_alarm:
65562306a36Sopenharmony_ci		ret = parport_wait_event(port, HZ);
65662306a36Sopenharmony_ci		if (ret < 0)
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci		ret = 0;
65962306a36Sopenharmony_ci		if (!time_before(jiffies, expire)) {
66062306a36Sopenharmony_ci			/* Timed out. */
66162306a36Sopenharmony_ci			printk(KERN_DEBUG "DMA write timed out\n");
66262306a36Sopenharmony_ci			break;
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci		/* Is serviceIntr set? */
66562306a36Sopenharmony_ci		if (!(inb(ECONTROL(port)) & (1<<2))) {
66662306a36Sopenharmony_ci			cond_resched();
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci			goto false_alarm;
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		dmaflag = claim_dma_lock();
67262306a36Sopenharmony_ci		disable_dma(port->dma);
67362306a36Sopenharmony_ci		clear_dma_ff(port->dma);
67462306a36Sopenharmony_ci		count = get_dma_residue(port->dma);
67562306a36Sopenharmony_ci		release_dma_lock(dmaflag);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		cond_resched(); /* Can't yield the port. */
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		/* Anyone else waiting for the port? */
68062306a36Sopenharmony_ci		if (port->waithead) {
68162306a36Sopenharmony_ci			printk(KERN_DEBUG "Somebody wants the port\n");
68262306a36Sopenharmony_ci			break;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		/* update for possible DMA residue ! */
68662306a36Sopenharmony_ci		buf  -= count;
68762306a36Sopenharmony_ci		left += count;
68862306a36Sopenharmony_ci		if (dma_handle)
68962306a36Sopenharmony_ci			dma_addr -= count;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Maybe got here through break, so adjust for DMA residue! */
69362306a36Sopenharmony_ci	dmaflag = claim_dma_lock();
69462306a36Sopenharmony_ci	disable_dma(port->dma);
69562306a36Sopenharmony_ci	clear_dma_ff(port->dma);
69662306a36Sopenharmony_ci	left += get_dma_residue(port->dma);
69762306a36Sopenharmony_ci	release_dma_lock(dmaflag);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Turn off DMA mode */
70062306a36Sopenharmony_ci	frob_econtrol(port, 1<<3, 0);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (dma_handle)
70362306a36Sopenharmony_ci		dma_unmap_single(dev, dma_handle, length, DMA_TO_DEVICE);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	dump_parport_state("leave fifo_write_block_dma", port);
70662306a36Sopenharmony_ci	return length - left;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci#endif
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic inline size_t parport_pc_fifo_write_block(struct parport *port,
71162306a36Sopenharmony_ci					       const void *buf, size_t length)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci#ifdef HAS_DMA
71462306a36Sopenharmony_ci	if (port->dma != PARPORT_DMA_NONE)
71562306a36Sopenharmony_ci		return parport_pc_fifo_write_block_dma(port, buf, length);
71662306a36Sopenharmony_ci#endif
71762306a36Sopenharmony_ci	return parport_pc_fifo_write_block_pio(port, buf, length);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/* Parallel Port FIFO mode (ECP chipsets) */
72162306a36Sopenharmony_cistatic size_t parport_pc_compat_write_block_pio(struct parport *port,
72262306a36Sopenharmony_ci						 const void *buf, size_t length,
72362306a36Sopenharmony_ci						 int flags)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	size_t written;
72662306a36Sopenharmony_ci	int r;
72762306a36Sopenharmony_ci	unsigned long expire;
72862306a36Sopenharmony_ci	const struct parport_pc_private *priv = port->physport->private_data;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Special case: a timeout of zero means we cannot call schedule().
73162306a36Sopenharmony_ci	 * Also if O_NONBLOCK is set then use the default implementation. */
73262306a36Sopenharmony_ci	if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
73362306a36Sopenharmony_ci		return parport_ieee1284_write_compat(port, buf,
73462306a36Sopenharmony_ci						      length, flags);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* Set up parallel port FIFO mode.*/
73762306a36Sopenharmony_ci	parport_pc_data_forward(port); /* Must be in PS2 mode */
73862306a36Sopenharmony_ci	parport_pc_frob_control(port, PARPORT_CONTROL_STROBE, 0);
73962306a36Sopenharmony_ci	r = change_mode(port, ECR_PPF); /* Parallel port FIFO */
74062306a36Sopenharmony_ci	if (r)
74162306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: Warning change_mode ECR_PPF failed\n",
74262306a36Sopenharmony_ci		       port->name);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* Write the data to the FIFO. */
74762306a36Sopenharmony_ci	written = parport_pc_fifo_write_block(port, buf, length);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	/* Finish up. */
75062306a36Sopenharmony_ci	/* For some hardware we don't want to touch the mode until
75162306a36Sopenharmony_ci	 * the FIFO is empty, so allow 4 seconds for each position
75262306a36Sopenharmony_ci	 * in the fifo.
75362306a36Sopenharmony_ci	 */
75462306a36Sopenharmony_ci	expire = jiffies + (priv->fifo_depth * HZ * 4);
75562306a36Sopenharmony_ci	do {
75662306a36Sopenharmony_ci		/* Wait for the FIFO to empty */
75762306a36Sopenharmony_ci		r = change_mode(port, ECR_PS2);
75862306a36Sopenharmony_ci		if (r != -EBUSY)
75962306a36Sopenharmony_ci			break;
76062306a36Sopenharmony_ci	} while (time_before(jiffies, expire));
76162306a36Sopenharmony_ci	if (r == -EBUSY) {
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: FIFO is stuck\n", port->name);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		/* Prevent further data transfer. */
76662306a36Sopenharmony_ci		frob_set_mode(port, ECR_TST);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		/* Adjust for the contents of the FIFO. */
76962306a36Sopenharmony_ci		for (written -= priv->fifo_depth; ; written++) {
77062306a36Sopenharmony_ci			if (inb(ECONTROL(port)) & 0x2) {
77162306a36Sopenharmony_ci				/* Full up. */
77262306a36Sopenharmony_ci				break;
77362306a36Sopenharmony_ci			}
77462306a36Sopenharmony_ci			outb(0, FIFO(port));
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		/* Reset the FIFO and return to PS2 mode. */
77862306a36Sopenharmony_ci		frob_set_mode(port, ECR_PS2);
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	r = parport_wait_peripheral(port,
78262306a36Sopenharmony_ci				     PARPORT_STATUS_BUSY,
78362306a36Sopenharmony_ci				     PARPORT_STATUS_BUSY);
78462306a36Sopenharmony_ci	if (r)
78562306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: BUSY timeout (%d) in compat_write_block_pio\n",
78662306a36Sopenharmony_ci		       port->name, r);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	return written;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci/* ECP */
79462306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_1284
79562306a36Sopenharmony_cistatic size_t parport_pc_ecp_write_block_pio(struct parport *port,
79662306a36Sopenharmony_ci					      const void *buf, size_t length,
79762306a36Sopenharmony_ci					      int flags)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	size_t written;
80062306a36Sopenharmony_ci	int r;
80162306a36Sopenharmony_ci	unsigned long expire;
80262306a36Sopenharmony_ci	const struct parport_pc_private *priv = port->physport->private_data;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/* Special case: a timeout of zero means we cannot call schedule().
80562306a36Sopenharmony_ci	 * Also if O_NONBLOCK is set then use the default implementation. */
80662306a36Sopenharmony_ci	if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
80762306a36Sopenharmony_ci		return parport_ieee1284_ecp_write_data(port, buf,
80862306a36Sopenharmony_ci							length, flags);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* Switch to forward mode if necessary. */
81162306a36Sopenharmony_ci	if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
81262306a36Sopenharmony_ci		/* Event 47: Set nInit high. */
81362306a36Sopenharmony_ci		parport_frob_control(port,
81462306a36Sopenharmony_ci				      PARPORT_CONTROL_INIT
81562306a36Sopenharmony_ci				      | PARPORT_CONTROL_AUTOFD,
81662306a36Sopenharmony_ci				      PARPORT_CONTROL_INIT
81762306a36Sopenharmony_ci				      | PARPORT_CONTROL_AUTOFD);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci		/* Event 49: PError goes high. */
82062306a36Sopenharmony_ci		r = parport_wait_peripheral(port,
82162306a36Sopenharmony_ci					     PARPORT_STATUS_PAPEROUT,
82262306a36Sopenharmony_ci					     PARPORT_STATUS_PAPEROUT);
82362306a36Sopenharmony_ci		if (r) {
82462306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: PError timeout (%d) in ecp_write_block_pio\n",
82562306a36Sopenharmony_ci			       port->name, r);
82662306a36Sopenharmony_ci		}
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	/* Set up ECP parallel port mode.*/
83062306a36Sopenharmony_ci	parport_pc_data_forward(port); /* Must be in PS2 mode */
83162306a36Sopenharmony_ci	parport_pc_frob_control(port,
83262306a36Sopenharmony_ci				 PARPORT_CONTROL_STROBE |
83362306a36Sopenharmony_ci				 PARPORT_CONTROL_AUTOFD,
83462306a36Sopenharmony_ci				 0);
83562306a36Sopenharmony_ci	r = change_mode(port, ECR_ECP); /* ECP FIFO */
83662306a36Sopenharmony_ci	if (r)
83762306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n",
83862306a36Sopenharmony_ci		       port->name);
83962306a36Sopenharmony_ci	port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* Write the data to the FIFO. */
84262306a36Sopenharmony_ci	written = parport_pc_fifo_write_block(port, buf, length);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Finish up. */
84562306a36Sopenharmony_ci	/* For some hardware we don't want to touch the mode until
84662306a36Sopenharmony_ci	 * the FIFO is empty, so allow 4 seconds for each position
84762306a36Sopenharmony_ci	 * in the fifo.
84862306a36Sopenharmony_ci	 */
84962306a36Sopenharmony_ci	expire = jiffies + (priv->fifo_depth * (HZ * 4));
85062306a36Sopenharmony_ci	do {
85162306a36Sopenharmony_ci		/* Wait for the FIFO to empty */
85262306a36Sopenharmony_ci		r = change_mode(port, ECR_PS2);
85362306a36Sopenharmony_ci		if (r != -EBUSY)
85462306a36Sopenharmony_ci			break;
85562306a36Sopenharmony_ci	} while (time_before(jiffies, expire));
85662306a36Sopenharmony_ci	if (r == -EBUSY) {
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: FIFO is stuck\n", port->name);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		/* Prevent further data transfer. */
86162306a36Sopenharmony_ci		frob_set_mode(port, ECR_TST);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		/* Adjust for the contents of the FIFO. */
86462306a36Sopenharmony_ci		for (written -= priv->fifo_depth; ; written++) {
86562306a36Sopenharmony_ci			if (inb(ECONTROL(port)) & 0x2) {
86662306a36Sopenharmony_ci				/* Full up. */
86762306a36Sopenharmony_ci				break;
86862306a36Sopenharmony_ci			}
86962306a36Sopenharmony_ci			outb(0, FIFO(port));
87062306a36Sopenharmony_ci		}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		/* Reset the FIFO and return to PS2 mode. */
87362306a36Sopenharmony_ci		frob_set_mode(port, ECR_PS2);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		/* Host transfer recovery. */
87662306a36Sopenharmony_ci		parport_pc_data_reverse(port); /* Must be in PS2 mode */
87762306a36Sopenharmony_ci		udelay(5);
87862306a36Sopenharmony_ci		parport_frob_control(port, PARPORT_CONTROL_INIT, 0);
87962306a36Sopenharmony_ci		r = parport_wait_peripheral(port, PARPORT_STATUS_PAPEROUT, 0);
88062306a36Sopenharmony_ci		if (r)
88162306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: PE,1 timeout (%d) in ecp_write_block_pio\n",
88262306a36Sopenharmony_ci			       port->name, r);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		parport_frob_control(port,
88562306a36Sopenharmony_ci				      PARPORT_CONTROL_INIT,
88662306a36Sopenharmony_ci				      PARPORT_CONTROL_INIT);
88762306a36Sopenharmony_ci		r = parport_wait_peripheral(port,
88862306a36Sopenharmony_ci					     PARPORT_STATUS_PAPEROUT,
88962306a36Sopenharmony_ci					     PARPORT_STATUS_PAPEROUT);
89062306a36Sopenharmony_ci		if (r)
89162306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: PE,2 timeout (%d) in ecp_write_block_pio\n",
89262306a36Sopenharmony_ci			       port->name, r);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	r = parport_wait_peripheral(port,
89662306a36Sopenharmony_ci				     PARPORT_STATUS_BUSY,
89762306a36Sopenharmony_ci				     PARPORT_STATUS_BUSY);
89862306a36Sopenharmony_ci	if (r)
89962306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: BUSY timeout (%d) in ecp_write_block_pio\n",
90062306a36Sopenharmony_ci		       port->name, r);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	return written;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci#endif /* IEEE 1284 support */
90762306a36Sopenharmony_ci#endif /* Allowed to use FIFO/DMA */
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci/*
91162306a36Sopenharmony_ci *	******************************************
91262306a36Sopenharmony_ci *	INITIALISATION AND MODULE STUFF BELOW HERE
91362306a36Sopenharmony_ci *	******************************************
91462306a36Sopenharmony_ci */
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/* GCC is not inlining extern inline function later overwritten to non-inline,
91762306a36Sopenharmony_ci   so we use outlined_ variants here.  */
91862306a36Sopenharmony_cistatic const struct parport_operations parport_pc_ops = {
91962306a36Sopenharmony_ci	.write_data	= parport_pc_write_data,
92062306a36Sopenharmony_ci	.read_data	= parport_pc_read_data,
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	.write_control	= parport_pc_write_control,
92362306a36Sopenharmony_ci	.read_control	= parport_pc_read_control,
92462306a36Sopenharmony_ci	.frob_control	= parport_pc_frob_control,
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	.read_status	= parport_pc_read_status,
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	.enable_irq	= parport_pc_enable_irq,
92962306a36Sopenharmony_ci	.disable_irq	= parport_pc_disable_irq,
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	.data_forward	= parport_pc_data_forward,
93262306a36Sopenharmony_ci	.data_reverse	= parport_pc_data_reverse,
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	.init_state	= parport_pc_init_state,
93562306a36Sopenharmony_ci	.save_state	= parport_pc_save_state,
93662306a36Sopenharmony_ci	.restore_state	= parport_pc_restore_state,
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	.epp_write_data	= parport_ieee1284_epp_write_data,
93962306a36Sopenharmony_ci	.epp_read_data	= parport_ieee1284_epp_read_data,
94062306a36Sopenharmony_ci	.epp_write_addr	= parport_ieee1284_epp_write_addr,
94162306a36Sopenharmony_ci	.epp_read_addr	= parport_ieee1284_epp_read_addr,
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	.ecp_write_data	= parport_ieee1284_ecp_write_data,
94462306a36Sopenharmony_ci	.ecp_read_data	= parport_ieee1284_ecp_read_data,
94562306a36Sopenharmony_ci	.ecp_write_addr	= parport_ieee1284_ecp_write_addr,
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	.compat_write_data	= parport_ieee1284_write_compat,
94862306a36Sopenharmony_ci	.nibble_read_data	= parport_ieee1284_read_nibble,
94962306a36Sopenharmony_ci	.byte_read_data		= parport_ieee1284_read_byte,
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
95262306a36Sopenharmony_ci};
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_SUPERIO
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic struct superio_struct *find_free_superio(void)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	int i;
95962306a36Sopenharmony_ci	for (i = 0; i < NR_SUPERIOS; i++)
96062306a36Sopenharmony_ci		if (superios[i].io == 0)
96162306a36Sopenharmony_ci			return &superios[i];
96262306a36Sopenharmony_ci	return NULL;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/* Super-IO chipset detection, Winbond, SMSC */
96762306a36Sopenharmony_cistatic void show_parconfig_smsc37c669(int io, int key)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	int cr1, cr4, cra, cr23, cr26, cr27;
97062306a36Sopenharmony_ci	struct superio_struct *s;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	static const char *const modes[] = {
97362306a36Sopenharmony_ci		"SPP and Bidirectional (PS/2)",
97462306a36Sopenharmony_ci		"EPP and SPP",
97562306a36Sopenharmony_ci		"ECP",
97662306a36Sopenharmony_ci		"ECP and EPP" };
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	outb(key, io);
97962306a36Sopenharmony_ci	outb(key, io);
98062306a36Sopenharmony_ci	outb(1, io);
98162306a36Sopenharmony_ci	cr1 = inb(io + 1);
98262306a36Sopenharmony_ci	outb(4, io);
98362306a36Sopenharmony_ci	cr4 = inb(io + 1);
98462306a36Sopenharmony_ci	outb(0x0a, io);
98562306a36Sopenharmony_ci	cra = inb(io + 1);
98662306a36Sopenharmony_ci	outb(0x23, io);
98762306a36Sopenharmony_ci	cr23 = inb(io + 1);
98862306a36Sopenharmony_ci	outb(0x26, io);
98962306a36Sopenharmony_ci	cr26 = inb(io + 1);
99062306a36Sopenharmony_ci	outb(0x27, io);
99162306a36Sopenharmony_ci	cr27 = inb(io + 1);
99262306a36Sopenharmony_ci	outb(0xaa, io);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (verbose_probing) {
99562306a36Sopenharmony_ci		pr_info("SMSC 37c669 LPT Config: cr_1=0x%02x, 4=0x%02x, A=0x%2x, 23=0x%02x, 26=0x%02x, 27=0x%02x\n",
99662306a36Sopenharmony_ci			cr1, cr4, cra, cr23, cr26, cr27);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		/* The documentation calls DMA and IRQ-Lines by letters, so
99962306a36Sopenharmony_ci		   the board maker can/will wire them
100062306a36Sopenharmony_ci		   appropriately/randomly...  G=reserved H=IDE-irq, */
100162306a36Sopenharmony_ci		pr_info("SMSC LPT Config: io=0x%04x, irq=%c, dma=%c, fifo threshold=%d\n",
100262306a36Sopenharmony_ci			cr23 * 4,
100362306a36Sopenharmony_ci			(cr27 & 0x0f) ? 'A' - 1 + (cr27 & 0x0f) : '-',
100462306a36Sopenharmony_ci			(cr26 & 0x0f) ? 'A' - 1 + (cr26 & 0x0f) : '-',
100562306a36Sopenharmony_ci			cra & 0x0f);
100662306a36Sopenharmony_ci		pr_info("SMSC LPT Config: enabled=%s power=%s\n",
100762306a36Sopenharmony_ci			(cr23 * 4 >= 0x100) ? "yes" : "no",
100862306a36Sopenharmony_ci			(cr1 & 4) ? "yes" : "no");
100962306a36Sopenharmony_ci		pr_info("SMSC LPT Config: Port mode=%s, EPP version =%s\n",
101062306a36Sopenharmony_ci			(cr1 & 0x08) ? "Standard mode only (SPP)"
101162306a36Sopenharmony_ci			: modes[cr4 & 0x03],
101262306a36Sopenharmony_ci			(cr4 & 0x40) ? "1.7" : "1.9");
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* Heuristics !  BIOS setup for this mainboard device limits
101662306a36Sopenharmony_ci	   the choices to standard settings, i.e. io-address and IRQ
101762306a36Sopenharmony_ci	   are related, however DMA can be 1 or 3, assume DMA_A=DMA1,
101862306a36Sopenharmony_ci	   DMA_C=DMA3 (this is true e.g. for TYAN 1564D Tomcat IV) */
101962306a36Sopenharmony_ci	if (cr23 * 4 >= 0x100) { /* if active */
102062306a36Sopenharmony_ci		s = find_free_superio();
102162306a36Sopenharmony_ci		if (s == NULL)
102262306a36Sopenharmony_ci			pr_info("Super-IO: too many chips!\n");
102362306a36Sopenharmony_ci		else {
102462306a36Sopenharmony_ci			int d;
102562306a36Sopenharmony_ci			switch (cr23 * 4) {
102662306a36Sopenharmony_ci			case 0x3bc:
102762306a36Sopenharmony_ci				s->io = 0x3bc;
102862306a36Sopenharmony_ci				s->irq = 7;
102962306a36Sopenharmony_ci				break;
103062306a36Sopenharmony_ci			case 0x378:
103162306a36Sopenharmony_ci				s->io = 0x378;
103262306a36Sopenharmony_ci				s->irq = 7;
103362306a36Sopenharmony_ci				break;
103462306a36Sopenharmony_ci			case 0x278:
103562306a36Sopenharmony_ci				s->io = 0x278;
103662306a36Sopenharmony_ci				s->irq = 5;
103762306a36Sopenharmony_ci			}
103862306a36Sopenharmony_ci			d = (cr26 & 0x0f);
103962306a36Sopenharmony_ci			if (d == 1 || d == 3)
104062306a36Sopenharmony_ci				s->dma = d;
104162306a36Sopenharmony_ci			else
104262306a36Sopenharmony_ci				s->dma = PARPORT_DMA_NONE;
104362306a36Sopenharmony_ci		}
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cistatic void show_parconfig_winbond(int io, int key)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	int cr30, cr60, cr61, cr70, cr74, crf0;
105162306a36Sopenharmony_ci	struct superio_struct *s;
105262306a36Sopenharmony_ci	static const char *const modes[] = {
105362306a36Sopenharmony_ci		"Standard (SPP) and Bidirectional(PS/2)", /* 0 */
105462306a36Sopenharmony_ci		"EPP-1.9 and SPP",
105562306a36Sopenharmony_ci		"ECP",
105662306a36Sopenharmony_ci		"ECP and EPP-1.9",
105762306a36Sopenharmony_ci		"Standard (SPP)",
105862306a36Sopenharmony_ci		"EPP-1.7 and SPP",		/* 5 */
105962306a36Sopenharmony_ci		"undefined!",
106062306a36Sopenharmony_ci		"ECP and EPP-1.7" };
106162306a36Sopenharmony_ci	static char *const irqtypes[] = {
106262306a36Sopenharmony_ci		"pulsed low, high-Z",
106362306a36Sopenharmony_ci		"follows nACK" };
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* The registers are called compatible-PnP because the
106662306a36Sopenharmony_ci	   register layout is modelled after ISA-PnP, the access
106762306a36Sopenharmony_ci	   method is just another ... */
106862306a36Sopenharmony_ci	outb(key, io);
106962306a36Sopenharmony_ci	outb(key, io);
107062306a36Sopenharmony_ci	outb(0x07, io);   /* Register 7: Select Logical Device */
107162306a36Sopenharmony_ci	outb(0x01, io + 1); /* LD1 is Parallel Port */
107262306a36Sopenharmony_ci	outb(0x30, io);
107362306a36Sopenharmony_ci	cr30 = inb(io + 1);
107462306a36Sopenharmony_ci	outb(0x60, io);
107562306a36Sopenharmony_ci	cr60 = inb(io + 1);
107662306a36Sopenharmony_ci	outb(0x61, io);
107762306a36Sopenharmony_ci	cr61 = inb(io + 1);
107862306a36Sopenharmony_ci	outb(0x70, io);
107962306a36Sopenharmony_ci	cr70 = inb(io + 1);
108062306a36Sopenharmony_ci	outb(0x74, io);
108162306a36Sopenharmony_ci	cr74 = inb(io + 1);
108262306a36Sopenharmony_ci	outb(0xf0, io);
108362306a36Sopenharmony_ci	crf0 = inb(io + 1);
108462306a36Sopenharmony_ci	outb(0xaa, io);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (verbose_probing) {
108762306a36Sopenharmony_ci		pr_info("Winbond LPT Config: cr_30=%02x 60,61=%02x%02x 70=%02x 74=%02x, f0=%02x\n",
108862306a36Sopenharmony_ci			cr30, cr60, cr61, cr70, cr74, crf0);
108962306a36Sopenharmony_ci		pr_info("Winbond LPT Config: active=%s, io=0x%02x%02x irq=%d, ",
109062306a36Sopenharmony_ci			(cr30 & 0x01) ? "yes" : "no", cr60, cr61, cr70 & 0x0f);
109162306a36Sopenharmony_ci		if ((cr74 & 0x07) > 3)
109262306a36Sopenharmony_ci			pr_cont("dma=none\n");
109362306a36Sopenharmony_ci		else
109462306a36Sopenharmony_ci			pr_cont("dma=%d\n", cr74 & 0x07);
109562306a36Sopenharmony_ci		pr_info("Winbond LPT Config: irqtype=%s, ECP fifo threshold=%d\n",
109662306a36Sopenharmony_ci			irqtypes[crf0 >> 7], (crf0 >> 3) & 0x0f);
109762306a36Sopenharmony_ci		pr_info("Winbond LPT Config: Port mode=%s\n",
109862306a36Sopenharmony_ci			modes[crf0 & 0x07]);
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (cr30 & 0x01) { /* the settings can be interrogated later ... */
110262306a36Sopenharmony_ci		s = find_free_superio();
110362306a36Sopenharmony_ci		if (s == NULL)
110462306a36Sopenharmony_ci			pr_info("Super-IO: too many chips!\n");
110562306a36Sopenharmony_ci		else {
110662306a36Sopenharmony_ci			s->io = (cr60 << 8) | cr61;
110762306a36Sopenharmony_ci			s->irq = cr70 & 0x0f;
110862306a36Sopenharmony_ci			s->dma = (((cr74 & 0x07) > 3) ?
110962306a36Sopenharmony_ci					   PARPORT_DMA_NONE : (cr74 & 0x07));
111062306a36Sopenharmony_ci		}
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	const char *type = "unknown";
111762306a36Sopenharmony_ci	int id, progif = 2;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (devid == devrev)
112062306a36Sopenharmony_ci		/* simple heuristics, we happened to read some
112162306a36Sopenharmony_ci		   non-winbond register */
112262306a36Sopenharmony_ci		return;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	id = (devid << 8) | devrev;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* Values are from public data sheets pdf files, I can just
112762306a36Sopenharmony_ci	   confirm 83977TF is correct :-) */
112862306a36Sopenharmony_ci	if (id == 0x9771)
112962306a36Sopenharmony_ci		type = "83977F/AF";
113062306a36Sopenharmony_ci	else if (id == 0x9773)
113162306a36Sopenharmony_ci		type = "83977TF / SMSC 97w33x/97w34x";
113262306a36Sopenharmony_ci	else if (id == 0x9774)
113362306a36Sopenharmony_ci		type = "83977ATF";
113462306a36Sopenharmony_ci	else if ((id & ~0x0f) == 0x5270)
113562306a36Sopenharmony_ci		type = "83977CTF / SMSC 97w36x";
113662306a36Sopenharmony_ci	else if ((id & ~0x0f) == 0x52f0)
113762306a36Sopenharmony_ci		type = "83977EF / SMSC 97w35x";
113862306a36Sopenharmony_ci	else if ((id & ~0x0f) == 0x5210)
113962306a36Sopenharmony_ci		type = "83627";
114062306a36Sopenharmony_ci	else if ((id & ~0x0f) == 0x6010)
114162306a36Sopenharmony_ci		type = "83697HF";
114262306a36Sopenharmony_ci	else if ((oldid & 0x0f) == 0x0a) {
114362306a36Sopenharmony_ci		type = "83877F";
114462306a36Sopenharmony_ci		progif = 1;
114562306a36Sopenharmony_ci	} else if ((oldid & 0x0f) == 0x0b) {
114662306a36Sopenharmony_ci		type = "83877AF";
114762306a36Sopenharmony_ci		progif = 1;
114862306a36Sopenharmony_ci	} else if ((oldid & 0x0f) == 0x0c) {
114962306a36Sopenharmony_ci		type = "83877TF";
115062306a36Sopenharmony_ci		progif = 1;
115162306a36Sopenharmony_ci	} else if ((oldid & 0x0f) == 0x0d) {
115262306a36Sopenharmony_ci		type = "83877ATF";
115362306a36Sopenharmony_ci		progif = 1;
115462306a36Sopenharmony_ci	} else
115562306a36Sopenharmony_ci		progif = 0;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (verbose_probing)
115862306a36Sopenharmony_ci		pr_info("Winbond chip at EFER=0x%x key=0x%02x devid=%02x devrev=%02x oldid=%02x type=%s\n",
115962306a36Sopenharmony_ci			efer, key, devid, devrev, oldid, type);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (progif == 2)
116262306a36Sopenharmony_ci		show_parconfig_winbond(efer, key);
116362306a36Sopenharmony_ci}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_cistatic void decode_smsc(int efer, int key, int devid, int devrev)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	const char *type = "unknown";
116862306a36Sopenharmony_ci	void (*func)(int io, int key);
116962306a36Sopenharmony_ci	int id;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (devid == devrev)
117262306a36Sopenharmony_ci		/* simple heuristics, we happened to read some
117362306a36Sopenharmony_ci		   non-smsc register */
117462306a36Sopenharmony_ci		return;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	func = NULL;
117762306a36Sopenharmony_ci	id = (devid << 8) | devrev;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	if (id == 0x0302) {
118062306a36Sopenharmony_ci		type = "37c669";
118162306a36Sopenharmony_ci		func = show_parconfig_smsc37c669;
118262306a36Sopenharmony_ci	} else if (id == 0x6582)
118362306a36Sopenharmony_ci		type = "37c665IR";
118462306a36Sopenharmony_ci	else if	(devid == 0x65)
118562306a36Sopenharmony_ci		type = "37c665GT";
118662306a36Sopenharmony_ci	else if	(devid == 0x66)
118762306a36Sopenharmony_ci		type = "37c666GT";
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (verbose_probing)
119062306a36Sopenharmony_ci		pr_info("SMSC chip at EFER=0x%x key=0x%02x devid=%02x devrev=%02x type=%s\n",
119162306a36Sopenharmony_ci			efer, key, devid, devrev, type);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (func)
119462306a36Sopenharmony_ci		func(efer, key);
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic void winbond_check(int io, int key)
119962306a36Sopenharmony_ci{
120062306a36Sopenharmony_ci	int origval, devid, devrev, oldid, x_devid, x_devrev, x_oldid;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	if (!request_region(io, 3, __func__))
120362306a36Sopenharmony_ci		return;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	origval = inb(io); /* Save original value */
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	/* First probe without key */
120862306a36Sopenharmony_ci	outb(0x20, io);
120962306a36Sopenharmony_ci	x_devid = inb(io + 1);
121062306a36Sopenharmony_ci	outb(0x21, io);
121162306a36Sopenharmony_ci	x_devrev = inb(io + 1);
121262306a36Sopenharmony_ci	outb(0x09, io);
121362306a36Sopenharmony_ci	x_oldid = inb(io + 1);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	outb(key, io);
121662306a36Sopenharmony_ci	outb(key, io);     /* Write Magic Sequence to EFER, extended
121762306a36Sopenharmony_ci			      function enable register */
121862306a36Sopenharmony_ci	outb(0x20, io);    /* Write EFIR, extended function index register */
121962306a36Sopenharmony_ci	devid = inb(io + 1);  /* Read EFDR, extended function data register */
122062306a36Sopenharmony_ci	outb(0x21, io);
122162306a36Sopenharmony_ci	devrev = inb(io + 1);
122262306a36Sopenharmony_ci	outb(0x09, io);
122362306a36Sopenharmony_ci	oldid = inb(io + 1);
122462306a36Sopenharmony_ci	outb(0xaa, io);    /* Magic Seal */
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	outb(origval, io); /* in case we poked some entirely different hardware */
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid))
122962306a36Sopenharmony_ci		goto out; /* protection against false positives */
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	decode_winbond(io, key, devid, devrev, oldid);
123262306a36Sopenharmony_ciout:
123362306a36Sopenharmony_ci	release_region(io, 3);
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic void winbond_check2(int io, int key)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	int origval[3], devid, devrev, oldid, x_devid, x_devrev, x_oldid;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	if (!request_region(io, 3, __func__))
124162306a36Sopenharmony_ci		return;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	origval[0] = inb(io); /* Save original values */
124462306a36Sopenharmony_ci	origval[1] = inb(io + 1);
124562306a36Sopenharmony_ci	origval[2] = inb(io + 2);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	/* First probe without the key */
124862306a36Sopenharmony_ci	outb(0x20, io + 2);
124962306a36Sopenharmony_ci	x_devid = inb(io + 2);
125062306a36Sopenharmony_ci	outb(0x21, io + 1);
125162306a36Sopenharmony_ci	x_devrev = inb(io + 2);
125262306a36Sopenharmony_ci	outb(0x09, io + 1);
125362306a36Sopenharmony_ci	x_oldid = inb(io + 2);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	outb(key, io);     /* Write Magic Byte to EFER, extended
125662306a36Sopenharmony_ci			      function enable register */
125762306a36Sopenharmony_ci	outb(0x20, io + 2);  /* Write EFIR, extended function index register */
125862306a36Sopenharmony_ci	devid = inb(io + 2);  /* Read EFDR, extended function data register */
125962306a36Sopenharmony_ci	outb(0x21, io + 1);
126062306a36Sopenharmony_ci	devrev = inb(io + 2);
126162306a36Sopenharmony_ci	outb(0x09, io + 1);
126262306a36Sopenharmony_ci	oldid = inb(io + 2);
126362306a36Sopenharmony_ci	outb(0xaa, io);    /* Magic Seal */
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	outb(origval[0], io); /* in case we poked some entirely different hardware */
126662306a36Sopenharmony_ci	outb(origval[1], io + 1);
126762306a36Sopenharmony_ci	outb(origval[2], io + 2);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (x_devid == devid && x_devrev == devrev && x_oldid == oldid)
127062306a36Sopenharmony_ci		goto out; /* protection against false positives */
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	decode_winbond(io, key, devid, devrev, oldid);
127362306a36Sopenharmony_ciout:
127462306a36Sopenharmony_ci	release_region(io, 3);
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic void smsc_check(int io, int key)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	int origval, id, rev, oldid, oldrev, x_id, x_rev, x_oldid, x_oldrev;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (!request_region(io, 3, __func__))
128262306a36Sopenharmony_ci		return;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	origval = inb(io); /* Save original value */
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/* First probe without the key */
128762306a36Sopenharmony_ci	outb(0x0d, io);
128862306a36Sopenharmony_ci	x_oldid = inb(io + 1);
128962306a36Sopenharmony_ci	outb(0x0e, io);
129062306a36Sopenharmony_ci	x_oldrev = inb(io + 1);
129162306a36Sopenharmony_ci	outb(0x20, io);
129262306a36Sopenharmony_ci	x_id = inb(io + 1);
129362306a36Sopenharmony_ci	outb(0x21, io);
129462306a36Sopenharmony_ci	x_rev = inb(io + 1);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	outb(key, io);
129762306a36Sopenharmony_ci	outb(key, io);     /* Write Magic Sequence to EFER, extended
129862306a36Sopenharmony_ci			      function enable register */
129962306a36Sopenharmony_ci	outb(0x0d, io);    /* Write EFIR, extended function index register */
130062306a36Sopenharmony_ci	oldid = inb(io + 1);  /* Read EFDR, extended function data register */
130162306a36Sopenharmony_ci	outb(0x0e, io);
130262306a36Sopenharmony_ci	oldrev = inb(io + 1);
130362306a36Sopenharmony_ci	outb(0x20, io);
130462306a36Sopenharmony_ci	id = inb(io + 1);
130562306a36Sopenharmony_ci	outb(0x21, io);
130662306a36Sopenharmony_ci	rev = inb(io + 1);
130762306a36Sopenharmony_ci	outb(0xaa, io);    /* Magic Seal */
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	outb(origval, io); /* in case we poked some entirely different hardware */
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (x_id == id && x_oldrev == oldrev &&
131262306a36Sopenharmony_ci	    x_oldid == oldid && x_rev == rev)
131362306a36Sopenharmony_ci		goto out; /* protection against false positives */
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	decode_smsc(io, key, oldid, oldrev);
131662306a36Sopenharmony_ciout:
131762306a36Sopenharmony_ci	release_region(io, 3);
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic void detect_and_report_winbond(void)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	if (verbose_probing)
132462306a36Sopenharmony_ci		printk(KERN_DEBUG "Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
132562306a36Sopenharmony_ci	winbond_check(0x3f0, 0x87);
132662306a36Sopenharmony_ci	winbond_check(0x370, 0x87);
132762306a36Sopenharmony_ci	winbond_check(0x2e , 0x87);
132862306a36Sopenharmony_ci	winbond_check(0x4e , 0x87);
132962306a36Sopenharmony_ci	winbond_check(0x3f0, 0x86);
133062306a36Sopenharmony_ci	winbond_check2(0x250, 0x88);
133162306a36Sopenharmony_ci	winbond_check2(0x250, 0x89);
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic void detect_and_report_smsc(void)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	if (verbose_probing)
133762306a36Sopenharmony_ci		printk(KERN_DEBUG "SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
133862306a36Sopenharmony_ci	smsc_check(0x3f0, 0x55);
133962306a36Sopenharmony_ci	smsc_check(0x370, 0x55);
134062306a36Sopenharmony_ci	smsc_check(0x3f0, 0x44);
134162306a36Sopenharmony_ci	smsc_check(0x370, 0x44);
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic void detect_and_report_it87(void)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	u16 dev;
134762306a36Sopenharmony_ci	u8 origval, r;
134862306a36Sopenharmony_ci	if (verbose_probing)
134962306a36Sopenharmony_ci		printk(KERN_DEBUG "IT8705 Super-IO detection, now testing port 2E ...\n");
135062306a36Sopenharmony_ci	if (!request_muxed_region(0x2e, 2, __func__))
135162306a36Sopenharmony_ci		return;
135262306a36Sopenharmony_ci	origval = inb(0x2e);		/* Save original value */
135362306a36Sopenharmony_ci	outb(0x87, 0x2e);
135462306a36Sopenharmony_ci	outb(0x01, 0x2e);
135562306a36Sopenharmony_ci	outb(0x55, 0x2e);
135662306a36Sopenharmony_ci	outb(0x55, 0x2e);
135762306a36Sopenharmony_ci	outb(0x20, 0x2e);
135862306a36Sopenharmony_ci	dev = inb(0x2f) << 8;
135962306a36Sopenharmony_ci	outb(0x21, 0x2e);
136062306a36Sopenharmony_ci	dev |= inb(0x2f);
136162306a36Sopenharmony_ci	if (dev == 0x8712 || dev == 0x8705 || dev == 0x8715 ||
136262306a36Sopenharmony_ci	    dev == 0x8716 || dev == 0x8718 || dev == 0x8726) {
136362306a36Sopenharmony_ci		pr_info("IT%04X SuperIO detected\n", dev);
136462306a36Sopenharmony_ci		outb(0x07, 0x2E);	/* Parallel Port */
136562306a36Sopenharmony_ci		outb(0x03, 0x2F);
136662306a36Sopenharmony_ci		outb(0xF0, 0x2E);	/* BOOT 0x80 off */
136762306a36Sopenharmony_ci		r = inb(0x2f);
136862306a36Sopenharmony_ci		outb(0xF0, 0x2E);
136962306a36Sopenharmony_ci		outb(r | 8, 0x2F);
137062306a36Sopenharmony_ci		outb(0x02, 0x2E);	/* Lock */
137162306a36Sopenharmony_ci		outb(0x02, 0x2F);
137262306a36Sopenharmony_ci	} else {
137362306a36Sopenharmony_ci		outb(origval, 0x2e);	/* Oops, sorry to disturb */
137462306a36Sopenharmony_ci	}
137562306a36Sopenharmony_ci	release_region(0x2e, 2);
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci#endif /* CONFIG_PARPORT_PC_SUPERIO */
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic struct superio_struct *find_superio(struct parport *p)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	int i;
138262306a36Sopenharmony_ci	for (i = 0; i < NR_SUPERIOS; i++)
138362306a36Sopenharmony_ci		if (superios[i].io == p->base)
138462306a36Sopenharmony_ci			return &superios[i];
138562306a36Sopenharmony_ci	return NULL;
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic int get_superio_dma(struct parport *p)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	struct superio_struct *s = find_superio(p);
139162306a36Sopenharmony_ci	if (s)
139262306a36Sopenharmony_ci		return s->dma;
139362306a36Sopenharmony_ci	return PARPORT_DMA_NONE;
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_cistatic int get_superio_irq(struct parport *p)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	struct superio_struct *s = find_superio(p);
139962306a36Sopenharmony_ci	if (s)
140062306a36Sopenharmony_ci		return s->irq;
140162306a36Sopenharmony_ci	return PARPORT_IRQ_NONE;
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci/* --- Mode detection ------------------------------------- */
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci/*
140862306a36Sopenharmony_ci * Checks for port existence, all ports support SPP MODE
140962306a36Sopenharmony_ci * Returns:
141062306a36Sopenharmony_ci *         0           :  No parallel port at this address
141162306a36Sopenharmony_ci *  PARPORT_MODE_PCSPP :  SPP port detected
141262306a36Sopenharmony_ci *                        (if the user specified an ioport himself,
141362306a36Sopenharmony_ci *                         this shall always be the case!)
141462306a36Sopenharmony_ci *
141562306a36Sopenharmony_ci */
141662306a36Sopenharmony_cistatic int parport_SPP_supported(struct parport *pb)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	unsigned char r, w;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	/*
142162306a36Sopenharmony_ci	 * first clear an eventually pending EPP timeout
142262306a36Sopenharmony_ci	 * I (sailer@ife.ee.ethz.ch) have an SMSC chipset
142362306a36Sopenharmony_ci	 * that does not even respond to SPP cycles if an EPP
142462306a36Sopenharmony_ci	 * timeout is pending
142562306a36Sopenharmony_ci	 */
142662306a36Sopenharmony_ci	clear_epp_timeout(pb);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	/* Do a simple read-write test to make sure the port exists. */
142962306a36Sopenharmony_ci	w = 0xc;
143062306a36Sopenharmony_ci	outb(w, CONTROL(pb));
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/* Is there a control register that we can read from?  Some
143362306a36Sopenharmony_ci	 * ports don't allow reads, so read_control just returns a
143462306a36Sopenharmony_ci	 * software copy. Some ports _do_ allow reads, so bypass the
143562306a36Sopenharmony_ci	 * software copy here.  In addition, some bits aren't
143662306a36Sopenharmony_ci	 * writable. */
143762306a36Sopenharmony_ci	r = inb(CONTROL(pb));
143862306a36Sopenharmony_ci	if ((r & 0xf) == w) {
143962306a36Sopenharmony_ci		w = 0xe;
144062306a36Sopenharmony_ci		outb(w, CONTROL(pb));
144162306a36Sopenharmony_ci		r = inb(CONTROL(pb));
144262306a36Sopenharmony_ci		outb(0xc, CONTROL(pb));
144362306a36Sopenharmony_ci		if ((r & 0xf) == w)
144462306a36Sopenharmony_ci			return PARPORT_MODE_PCSPP;
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (user_specified)
144862306a36Sopenharmony_ci		/* That didn't work, but the user thinks there's a
144962306a36Sopenharmony_ci		 * port here. */
145062306a36Sopenharmony_ci		pr_info("parport 0x%lx (WARNING): CTR: wrote 0x%02x, read 0x%02x\n",
145162306a36Sopenharmony_ci			pb->base, w, r);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/* Try the data register.  The data lines aren't tri-stated at
145462306a36Sopenharmony_ci	 * this stage, so we expect back what we wrote. */
145562306a36Sopenharmony_ci	w = 0xaa;
145662306a36Sopenharmony_ci	parport_pc_write_data(pb, w);
145762306a36Sopenharmony_ci	r = parport_pc_read_data(pb);
145862306a36Sopenharmony_ci	if (r == w) {
145962306a36Sopenharmony_ci		w = 0x55;
146062306a36Sopenharmony_ci		parport_pc_write_data(pb, w);
146162306a36Sopenharmony_ci		r = parport_pc_read_data(pb);
146262306a36Sopenharmony_ci		if (r == w)
146362306a36Sopenharmony_ci			return PARPORT_MODE_PCSPP;
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (user_specified) {
146762306a36Sopenharmony_ci		/* Didn't work, but the user is convinced this is the
146862306a36Sopenharmony_ci		 * place. */
146962306a36Sopenharmony_ci		pr_info("parport 0x%lx (WARNING): DATA: wrote 0x%02x, read 0x%02x\n",
147062306a36Sopenharmony_ci			pb->base, w, r);
147162306a36Sopenharmony_ci		pr_info("parport 0x%lx: You gave this address, but there is probably no parallel port there!\n",
147262306a36Sopenharmony_ci			pb->base);
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	/* It's possible that we can't read the control register or
147662306a36Sopenharmony_ci	 * the data register.  In that case just believe the user. */
147762306a36Sopenharmony_ci	if (user_specified)
147862306a36Sopenharmony_ci		return PARPORT_MODE_PCSPP;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	return 0;
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci/* Check for ECR
148462306a36Sopenharmony_ci *
148562306a36Sopenharmony_ci * Old style XT ports alias io ports every 0x400, hence accessing ECR
148662306a36Sopenharmony_ci * on these cards actually accesses the CTR.
148762306a36Sopenharmony_ci *
148862306a36Sopenharmony_ci * Modern cards don't do this but reading from ECR will return 0xff
148962306a36Sopenharmony_ci * regardless of what is written here if the card does NOT support
149062306a36Sopenharmony_ci * ECP.
149162306a36Sopenharmony_ci *
149262306a36Sopenharmony_ci * We first check to see if ECR is the same as CTR.  If not, the low
149362306a36Sopenharmony_ci * two bits of ECR aren't writable, so we check by writing ECR and
149462306a36Sopenharmony_ci * reading it back to see if it's what we expect.
149562306a36Sopenharmony_ci */
149662306a36Sopenharmony_cistatic int parport_ECR_present(struct parport *pb)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	struct parport_pc_private *priv = pb->private_data;
149962306a36Sopenharmony_ci	unsigned char r = 0xc;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (!priv->ecr_writable) {
150262306a36Sopenharmony_ci		outb(r, CONTROL(pb));
150362306a36Sopenharmony_ci		if ((inb(ECONTROL(pb)) & 0x3) == (r & 0x3)) {
150462306a36Sopenharmony_ci			outb(r ^ 0x2, CONTROL(pb)); /* Toggle bit 1 */
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci			r = inb(CONTROL(pb));
150762306a36Sopenharmony_ci			if ((inb(ECONTROL(pb)) & 0x2) == (r & 0x2))
150862306a36Sopenharmony_ci				/* Sure that no ECR register exists */
150962306a36Sopenharmony_ci				goto no_reg;
151062306a36Sopenharmony_ci		}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci		if ((inb(ECONTROL(pb)) & 0x3) != 0x1)
151362306a36Sopenharmony_ci			goto no_reg;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		ECR_WRITE(pb, 0x34);
151662306a36Sopenharmony_ci		if (inb(ECONTROL(pb)) != 0x35)
151762306a36Sopenharmony_ci			goto no_reg;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	priv->ecr = 1;
152162306a36Sopenharmony_ci	outb(0xc, CONTROL(pb));
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	/* Go to mode 000 */
152462306a36Sopenharmony_ci	frob_set_mode(pb, ECR_SPP);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	return 1;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci no_reg:
152962306a36Sopenharmony_ci	outb(0xc, CONTROL(pb));
153062306a36Sopenharmony_ci	return 0;
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_1284
153462306a36Sopenharmony_ci/* Detect PS/2 support.
153562306a36Sopenharmony_ci *
153662306a36Sopenharmony_ci * Bit 5 (0x20) sets the PS/2 data direction; setting this high
153762306a36Sopenharmony_ci * allows us to read data from the data lines.  In theory we would get back
153862306a36Sopenharmony_ci * 0xff but any peripheral attached to the port may drag some or all of the
153962306a36Sopenharmony_ci * lines down to zero.  So if we get back anything that isn't the contents
154062306a36Sopenharmony_ci * of the data register we deem PS/2 support to be present.
154162306a36Sopenharmony_ci *
154262306a36Sopenharmony_ci * Some SPP ports have "half PS/2" ability - you can't turn off the line
154362306a36Sopenharmony_ci * drivers, but an external peripheral with sufficiently beefy drivers of
154462306a36Sopenharmony_ci * its own can overpower them and assert its own levels onto the bus, from
154562306a36Sopenharmony_ci * where they can then be read back as normal.  Ports with this property
154662306a36Sopenharmony_ci * and the right type of device attached are likely to fail the SPP test,
154762306a36Sopenharmony_ci * (as they will appear to have stuck bits) and so the fact that they might
154862306a36Sopenharmony_ci * be misdetected here is rather academic.
154962306a36Sopenharmony_ci */
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_cistatic int parport_PS2_supported(struct parport *pb)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	int ok = 0;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	clear_epp_timeout(pb);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	/* try to tri-state the buffer */
155862306a36Sopenharmony_ci	parport_pc_data_reverse(pb);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	parport_pc_write_data(pb, 0x55);
156162306a36Sopenharmony_ci	if (parport_pc_read_data(pb) != 0x55)
156262306a36Sopenharmony_ci		ok++;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	parport_pc_write_data(pb, 0xaa);
156562306a36Sopenharmony_ci	if (parport_pc_read_data(pb) != 0xaa)
156662306a36Sopenharmony_ci		ok++;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	/* cancel input mode */
156962306a36Sopenharmony_ci	parport_pc_data_forward(pb);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	if (ok) {
157262306a36Sopenharmony_ci		pb->modes |= PARPORT_MODE_TRISTATE;
157362306a36Sopenharmony_ci	} else {
157462306a36Sopenharmony_ci		struct parport_pc_private *priv = pb->private_data;
157562306a36Sopenharmony_ci		priv->ctr_writable &= ~0x20;
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	return ok;
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_FIFO
158262306a36Sopenharmony_cistatic int parport_ECP_supported(struct parport *pb)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	int i;
158562306a36Sopenharmony_ci	int config, configb;
158662306a36Sopenharmony_ci	int pword;
158762306a36Sopenharmony_ci	struct parport_pc_private *priv = pb->private_data;
158862306a36Sopenharmony_ci	/* Translate ECP intrLine to ISA irq value */
158962306a36Sopenharmony_ci	static const int intrline[] = { 0, 7, 9, 10, 11, 14, 15, 5 };
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	/* If there is no ECR, we have no hope of supporting ECP. */
159262306a36Sopenharmony_ci	if (!priv->ecr)
159362306a36Sopenharmony_ci		return 0;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	/* Find out FIFO depth */
159662306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
159762306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_TST << 5); /* TEST FIFO */
159862306a36Sopenharmony_ci	for (i = 0; i < 1024 && !(inb(ECONTROL(pb)) & 0x02); i++)
159962306a36Sopenharmony_ci		outb(0xaa, FIFO(pb));
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	/*
160262306a36Sopenharmony_ci	 * Using LGS chipset it uses ECR register, but
160362306a36Sopenharmony_ci	 * it doesn't support ECP or FIFO MODE
160462306a36Sopenharmony_ci	 */
160562306a36Sopenharmony_ci	if (i == 1024) {
160662306a36Sopenharmony_ci		ECR_WRITE(pb, ECR_SPP << 5);
160762306a36Sopenharmony_ci		return 0;
160862306a36Sopenharmony_ci	}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	priv->fifo_depth = i;
161162306a36Sopenharmony_ci	if (verbose_probing)
161262306a36Sopenharmony_ci		printk(KERN_DEBUG "0x%lx: FIFO is %d bytes\n", pb->base, i);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	/* Find out writeIntrThreshold */
161562306a36Sopenharmony_ci	frob_econtrol(pb, 1<<2, 1<<2);
161662306a36Sopenharmony_ci	frob_econtrol(pb, 1<<2, 0);
161762306a36Sopenharmony_ci	for (i = 1; i <= priv->fifo_depth; i++) {
161862306a36Sopenharmony_ci		inb(FIFO(pb));
161962306a36Sopenharmony_ci		udelay(50);
162062306a36Sopenharmony_ci		if (inb(ECONTROL(pb)) & (1<<2))
162162306a36Sopenharmony_ci			break;
162262306a36Sopenharmony_ci	}
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	if (i <= priv->fifo_depth) {
162562306a36Sopenharmony_ci		if (verbose_probing)
162662306a36Sopenharmony_ci			printk(KERN_DEBUG "0x%lx: writeIntrThreshold is %d\n",
162762306a36Sopenharmony_ci			       pb->base, i);
162862306a36Sopenharmony_ci	} else
162962306a36Sopenharmony_ci		/* Number of bytes we know we can write if we get an
163062306a36Sopenharmony_ci		   interrupt. */
163162306a36Sopenharmony_ci		i = 0;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	priv->writeIntrThreshold = i;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	/* Find out readIntrThreshold */
163662306a36Sopenharmony_ci	frob_set_mode(pb, ECR_PS2); /* Reset FIFO and enable PS2 */
163762306a36Sopenharmony_ci	parport_pc_data_reverse(pb); /* Must be in PS2 mode */
163862306a36Sopenharmony_ci	frob_set_mode(pb, ECR_TST); /* Test FIFO */
163962306a36Sopenharmony_ci	frob_econtrol(pb, 1<<2, 1<<2);
164062306a36Sopenharmony_ci	frob_econtrol(pb, 1<<2, 0);
164162306a36Sopenharmony_ci	for (i = 1; i <= priv->fifo_depth; i++) {
164262306a36Sopenharmony_ci		outb(0xaa, FIFO(pb));
164362306a36Sopenharmony_ci		if (inb(ECONTROL(pb)) & (1<<2))
164462306a36Sopenharmony_ci			break;
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	if (i <= priv->fifo_depth) {
164862306a36Sopenharmony_ci		if (verbose_probing)
164962306a36Sopenharmony_ci			pr_info("0x%lx: readIntrThreshold is %d\n",
165062306a36Sopenharmony_ci				pb->base, i);
165162306a36Sopenharmony_ci	} else
165262306a36Sopenharmony_ci		/* Number of bytes we can read if we get an interrupt. */
165362306a36Sopenharmony_ci		i = 0;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	priv->readIntrThreshold = i;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
165862306a36Sopenharmony_ci	ECR_WRITE(pb, 0xf4); /* Configuration mode */
165962306a36Sopenharmony_ci	config = inb(CONFIGA(pb));
166062306a36Sopenharmony_ci	pword = (config >> 4) & 0x7;
166162306a36Sopenharmony_ci	switch (pword) {
166262306a36Sopenharmony_ci	case 0:
166362306a36Sopenharmony_ci		pword = 2;
166462306a36Sopenharmony_ci		pr_warn("0x%lx: Unsupported pword size!\n", pb->base);
166562306a36Sopenharmony_ci		break;
166662306a36Sopenharmony_ci	case 2:
166762306a36Sopenharmony_ci		pword = 4;
166862306a36Sopenharmony_ci		pr_warn("0x%lx: Unsupported pword size!\n", pb->base);
166962306a36Sopenharmony_ci		break;
167062306a36Sopenharmony_ci	default:
167162306a36Sopenharmony_ci		pr_warn("0x%lx: Unknown implementation ID\n", pb->base);
167262306a36Sopenharmony_ci		fallthrough;	/* Assume 1 */
167362306a36Sopenharmony_ci	case 1:
167462306a36Sopenharmony_ci		pword = 1;
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci	priv->pword = pword;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	if (verbose_probing) {
167962306a36Sopenharmony_ci		printk(KERN_DEBUG "0x%lx: PWord is %d bits\n",
168062306a36Sopenharmony_ci		       pb->base, 8 * pword);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		printk(KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n",
168362306a36Sopenharmony_ci		       pb->base, config & 0x80 ? "Level" : "Pulses");
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci		configb = inb(CONFIGB(pb));
168662306a36Sopenharmony_ci		printk(KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
168762306a36Sopenharmony_ci		       pb->base, config, configb);
168862306a36Sopenharmony_ci		printk(KERN_DEBUG "0x%lx: ECP settings irq=", pb->base);
168962306a36Sopenharmony_ci		if ((configb >> 3) & 0x07)
169062306a36Sopenharmony_ci			pr_cont("%d", intrline[(configb >> 3) & 0x07]);
169162306a36Sopenharmony_ci		else
169262306a36Sopenharmony_ci			pr_cont("<none or set by other means>");
169362306a36Sopenharmony_ci		pr_cont(" dma=");
169462306a36Sopenharmony_ci		if ((configb & 0x03) == 0x00)
169562306a36Sopenharmony_ci			pr_cont("<none or set by other means>\n");
169662306a36Sopenharmony_ci		else
169762306a36Sopenharmony_ci			pr_cont("%d\n", configb & 0x07);
169862306a36Sopenharmony_ci	}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	/* Go back to mode 000 */
170162306a36Sopenharmony_ci	frob_set_mode(pb, ECR_SPP);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	return 1;
170462306a36Sopenharmony_ci}
170562306a36Sopenharmony_ci#endif
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci#ifdef CONFIG_X86_32
170862306a36Sopenharmony_cistatic int intel_bug_present_check_epp(struct parport *pb)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	const struct parport_pc_private *priv = pb->private_data;
171162306a36Sopenharmony_ci	int bug_present = 0;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (priv->ecr) {
171462306a36Sopenharmony_ci		/* store value of ECR */
171562306a36Sopenharmony_ci		unsigned char ecr = inb(ECONTROL(pb));
171662306a36Sopenharmony_ci		unsigned char i;
171762306a36Sopenharmony_ci		for (i = 0x00; i < 0x80; i += 0x20) {
171862306a36Sopenharmony_ci			ECR_WRITE(pb, i);
171962306a36Sopenharmony_ci			if (clear_epp_timeout(pb)) {
172062306a36Sopenharmony_ci				/* Phony EPP in ECP. */
172162306a36Sopenharmony_ci				bug_present = 1;
172262306a36Sopenharmony_ci				break;
172362306a36Sopenharmony_ci			}
172462306a36Sopenharmony_ci		}
172562306a36Sopenharmony_ci		/* return ECR into the inital state */
172662306a36Sopenharmony_ci		ECR_WRITE(pb, ecr);
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	return bug_present;
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_cistatic int intel_bug_present(struct parport *pb)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci/* Check whether the device is legacy, not PCI or PCMCIA. Only legacy is known to be affected. */
173462306a36Sopenharmony_ci	if (pb->dev != NULL) {
173562306a36Sopenharmony_ci		return 0;
173662306a36Sopenharmony_ci	}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	return intel_bug_present_check_epp(pb);
173962306a36Sopenharmony_ci}
174062306a36Sopenharmony_ci#else
174162306a36Sopenharmony_cistatic int intel_bug_present(struct parport *pb)
174262306a36Sopenharmony_ci{
174362306a36Sopenharmony_ci	return 0;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci#endif /* CONFIG_X86_32 */
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_cistatic int parport_ECPPS2_supported(struct parport *pb)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	const struct parport_pc_private *priv = pb->private_data;
175062306a36Sopenharmony_ci	int result;
175162306a36Sopenharmony_ci	unsigned char oecr;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	if (!priv->ecr)
175462306a36Sopenharmony_ci		return 0;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	oecr = inb(ECONTROL(pb));
175762306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_PS2 << 5);
175862306a36Sopenharmony_ci	result = parport_PS2_supported(pb);
175962306a36Sopenharmony_ci	ECR_WRITE(pb, oecr);
176062306a36Sopenharmony_ci	return result;
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci/* EPP mode detection  */
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cistatic int parport_EPP_supported(struct parport *pb)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	/*
176862306a36Sopenharmony_ci	 * Theory:
176962306a36Sopenharmony_ci	 *	Bit 0 of STR is the EPP timeout bit, this bit is 0
177062306a36Sopenharmony_ci	 *	when EPP is possible and is set high when an EPP timeout
177162306a36Sopenharmony_ci	 *	occurs (EPP uses the HALT line to stop the CPU while it does
177262306a36Sopenharmony_ci	 *	the byte transfer, an EPP timeout occurs if the attached
177362306a36Sopenharmony_ci	 *	device fails to respond after 10 micro seconds).
177462306a36Sopenharmony_ci	 *
177562306a36Sopenharmony_ci	 *	This bit is cleared by either reading it (National Semi)
177662306a36Sopenharmony_ci	 *	or writing a 1 to the bit (SMC, UMC, WinBond), others ???
177762306a36Sopenharmony_ci	 *	This bit is always high in non EPP modes.
177862306a36Sopenharmony_ci	 */
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	/* If EPP timeout bit clear then EPP available */
178162306a36Sopenharmony_ci	if (!clear_epp_timeout(pb))
178262306a36Sopenharmony_ci		return 0;  /* No way to clear timeout */
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	/* Check for Intel bug. */
178562306a36Sopenharmony_ci	if (intel_bug_present(pb))
178662306a36Sopenharmony_ci		return 0;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	pb->modes |= PARPORT_MODE_EPP;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	/* Set up access functions to use EPP hardware. */
179162306a36Sopenharmony_ci	pb->ops->epp_read_data = parport_pc_epp_read_data;
179262306a36Sopenharmony_ci	pb->ops->epp_write_data = parport_pc_epp_write_data;
179362306a36Sopenharmony_ci	pb->ops->epp_read_addr = parport_pc_epp_read_addr;
179462306a36Sopenharmony_ci	pb->ops->epp_write_addr = parport_pc_epp_write_addr;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	return 1;
179762306a36Sopenharmony_ci}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_cistatic int parport_ECPEPP_supported(struct parport *pb)
180062306a36Sopenharmony_ci{
180162306a36Sopenharmony_ci	struct parport_pc_private *priv = pb->private_data;
180262306a36Sopenharmony_ci	int result;
180362306a36Sopenharmony_ci	unsigned char oecr;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	if (!priv->ecr)
180662306a36Sopenharmony_ci		return 0;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	oecr = inb(ECONTROL(pb));
180962306a36Sopenharmony_ci	/* Search for SMC style EPP+ECP mode */
181062306a36Sopenharmony_ci	ECR_WRITE(pb, 0x80);
181162306a36Sopenharmony_ci	outb(0x04, CONTROL(pb));
181262306a36Sopenharmony_ci	result = parport_EPP_supported(pb);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	ECR_WRITE(pb, oecr);
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	if (result) {
181762306a36Sopenharmony_ci		/* Set up access functions to use ECP+EPP hardware. */
181862306a36Sopenharmony_ci		pb->ops->epp_read_data = parport_pc_ecpepp_read_data;
181962306a36Sopenharmony_ci		pb->ops->epp_write_data = parport_pc_ecpepp_write_data;
182062306a36Sopenharmony_ci		pb->ops->epp_read_addr = parport_pc_ecpepp_read_addr;
182162306a36Sopenharmony_ci		pb->ops->epp_write_addr = parport_pc_ecpepp_write_addr;
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	return result;
182562306a36Sopenharmony_ci}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci#else /* No IEEE 1284 support */
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci/* Don't bother probing for modes we know we won't use. */
183062306a36Sopenharmony_cistatic int parport_PS2_supported(struct parport *pb) { return 0; }
183162306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_FIFO
183262306a36Sopenharmony_cistatic int parport_ECP_supported(struct parport *pb)
183362306a36Sopenharmony_ci{
183462306a36Sopenharmony_ci	return 0;
183562306a36Sopenharmony_ci}
183662306a36Sopenharmony_ci#endif
183762306a36Sopenharmony_cistatic int parport_EPP_supported(struct parport *pb)
183862306a36Sopenharmony_ci{
183962306a36Sopenharmony_ci	return 0;
184062306a36Sopenharmony_ci}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_cistatic int parport_ECPEPP_supported(struct parport *pb)
184362306a36Sopenharmony_ci{
184462306a36Sopenharmony_ci	return 0;
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_cistatic int parport_ECPPS2_supported(struct parport *pb)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	return 0;
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci#endif /* No IEEE 1284 support */
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci/* --- IRQ detection -------------------------------------- */
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci/* Only if supports ECP mode */
185762306a36Sopenharmony_cistatic int programmable_irq_support(struct parport *pb)
185862306a36Sopenharmony_ci{
185962306a36Sopenharmony_ci	int irq, intrLine;
186062306a36Sopenharmony_ci	unsigned char oecr = inb(ECONTROL(pb));
186162306a36Sopenharmony_ci	static const int lookup[8] = {
186262306a36Sopenharmony_ci		PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
186362306a36Sopenharmony_ci	};
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_CNF << 5); /* Configuration MODE */
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	intrLine = (inb(CONFIGB(pb)) >> 3) & 0x07;
186862306a36Sopenharmony_ci	irq = lookup[intrLine];
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	ECR_WRITE(pb, oecr);
187162306a36Sopenharmony_ci	return irq;
187262306a36Sopenharmony_ci}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_cistatic int irq_probe_ECP(struct parport *pb)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	int i;
187762306a36Sopenharmony_ci	unsigned long irqs;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	irqs = probe_irq_on();
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
188262306a36Sopenharmony_ci	ECR_WRITE(pb, (ECR_TST << 5) | 0x04);
188362306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_TST << 5);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	/* If Full FIFO sure that writeIntrThreshold is generated */
188662306a36Sopenharmony_ci	for (i = 0; i < 1024 && !(inb(ECONTROL(pb)) & 0x02) ; i++)
188762306a36Sopenharmony_ci		outb(0xaa, FIFO(pb));
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	pb->irq = probe_irq_off(irqs);
189062306a36Sopenharmony_ci	ECR_WRITE(pb, ECR_SPP << 5);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	if (pb->irq <= 0)
189362306a36Sopenharmony_ci		pb->irq = PARPORT_IRQ_NONE;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	return pb->irq;
189662306a36Sopenharmony_ci}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci/*
189962306a36Sopenharmony_ci * This detection seems that only works in National Semiconductors
190062306a36Sopenharmony_ci * This doesn't work in SMC, LGS, and Winbond
190162306a36Sopenharmony_ci */
190262306a36Sopenharmony_cistatic int irq_probe_EPP(struct parport *pb)
190362306a36Sopenharmony_ci{
190462306a36Sopenharmony_ci#ifndef ADVANCED_DETECT
190562306a36Sopenharmony_ci	return PARPORT_IRQ_NONE;
190662306a36Sopenharmony_ci#else
190762306a36Sopenharmony_ci	int irqs;
190862306a36Sopenharmony_ci	unsigned char oecr;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	if (pb->modes & PARPORT_MODE_PCECR)
191162306a36Sopenharmony_ci		oecr = inb(ECONTROL(pb));
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	irqs = probe_irq_on();
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	if (pb->modes & PARPORT_MODE_PCECR)
191662306a36Sopenharmony_ci		frob_econtrol(pb, 0x10, 0x10);
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	clear_epp_timeout(pb);
191962306a36Sopenharmony_ci	parport_pc_frob_control(pb, 0x20, 0x20);
192062306a36Sopenharmony_ci	parport_pc_frob_control(pb, 0x10, 0x10);
192162306a36Sopenharmony_ci	clear_epp_timeout(pb);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	/* Device isn't expecting an EPP read
192462306a36Sopenharmony_ci	 * and generates an IRQ.
192562306a36Sopenharmony_ci	 */
192662306a36Sopenharmony_ci	parport_pc_read_epp(pb);
192762306a36Sopenharmony_ci	udelay(20);
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	pb->irq = probe_irq_off(irqs);
193062306a36Sopenharmony_ci	if (pb->modes & PARPORT_MODE_PCECR)
193162306a36Sopenharmony_ci		ECR_WRITE(pb, oecr);
193262306a36Sopenharmony_ci	parport_pc_write_control(pb, 0xc);
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	if (pb->irq <= 0)
193562306a36Sopenharmony_ci		pb->irq = PARPORT_IRQ_NONE;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	return pb->irq;
193862306a36Sopenharmony_ci#endif /* Advanced detection */
193962306a36Sopenharmony_ci}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_cistatic int irq_probe_SPP(struct parport *pb)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	/* Don't even try to do this. */
194462306a36Sopenharmony_ci	return PARPORT_IRQ_NONE;
194562306a36Sopenharmony_ci}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci/* We will attempt to share interrupt requests since other devices
194862306a36Sopenharmony_ci * such as sound cards and network cards seem to like using the
194962306a36Sopenharmony_ci * printer IRQs.
195062306a36Sopenharmony_ci *
195162306a36Sopenharmony_ci * When ECP is available we can autoprobe for IRQs.
195262306a36Sopenharmony_ci * NOTE: If we can autoprobe it, we can register the IRQ.
195362306a36Sopenharmony_ci */
195462306a36Sopenharmony_cistatic int parport_irq_probe(struct parport *pb)
195562306a36Sopenharmony_ci{
195662306a36Sopenharmony_ci	struct parport_pc_private *priv = pb->private_data;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	if (priv->ecr) {
195962306a36Sopenharmony_ci		pb->irq = programmable_irq_support(pb);
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci		if (pb->irq == PARPORT_IRQ_NONE)
196262306a36Sopenharmony_ci			pb->irq = irq_probe_ECP(pb);
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	if ((pb->irq == PARPORT_IRQ_NONE) && priv->ecr &&
196662306a36Sopenharmony_ci	    (pb->modes & PARPORT_MODE_EPP))
196762306a36Sopenharmony_ci		pb->irq = irq_probe_EPP(pb);
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	clear_epp_timeout(pb);
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_EPP))
197262306a36Sopenharmony_ci		pb->irq = irq_probe_EPP(pb);
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	clear_epp_timeout(pb);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	if (pb->irq == PARPORT_IRQ_NONE)
197762306a36Sopenharmony_ci		pb->irq = irq_probe_SPP(pb);
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	if (pb->irq == PARPORT_IRQ_NONE)
198062306a36Sopenharmony_ci		pb->irq = get_superio_irq(pb);
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	return pb->irq;
198362306a36Sopenharmony_ci}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci/* --- DMA detection -------------------------------------- */
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci/* Only if chipset conforms to ECP ISA Interface Standard */
198862306a36Sopenharmony_cistatic int programmable_dma_support(struct parport *p)
198962306a36Sopenharmony_ci{
199062306a36Sopenharmony_ci	unsigned char oecr = inb(ECONTROL(p));
199162306a36Sopenharmony_ci	int dma;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	frob_set_mode(p, ECR_CNF);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	dma = inb(CONFIGB(p)) & 0x07;
199662306a36Sopenharmony_ci	/* 000: Indicates jumpered 8-bit DMA if read-only.
199762306a36Sopenharmony_ci	   100: Indicates jumpered 16-bit DMA if read-only. */
199862306a36Sopenharmony_ci	if ((dma & 0x03) == 0)
199962306a36Sopenharmony_ci		dma = PARPORT_DMA_NONE;
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	ECR_WRITE(p, oecr);
200262306a36Sopenharmony_ci	return dma;
200362306a36Sopenharmony_ci}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cistatic int parport_dma_probe(struct parport *p)
200662306a36Sopenharmony_ci{
200762306a36Sopenharmony_ci	const struct parport_pc_private *priv = p->private_data;
200862306a36Sopenharmony_ci	if (priv->ecr)		/* ask ECP chipset first */
200962306a36Sopenharmony_ci		p->dma = programmable_dma_support(p);
201062306a36Sopenharmony_ci	if (p->dma == PARPORT_DMA_NONE) {
201162306a36Sopenharmony_ci		/* ask known Super-IO chips proper, although these
201262306a36Sopenharmony_ci		   claim ECP compatible, some don't report their DMA
201362306a36Sopenharmony_ci		   conforming to ECP standards */
201462306a36Sopenharmony_ci		p->dma = get_superio_dma(p);
201562306a36Sopenharmony_ci	}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	return p->dma;
201862306a36Sopenharmony_ci}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci/* --- Initialisation code -------------------------------- */
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_cistatic LIST_HEAD(ports_list);
202362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ports_lock);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_cistatic struct parport *__parport_pc_probe_port(unsigned long int base,
202662306a36Sopenharmony_ci					       unsigned long int base_hi,
202762306a36Sopenharmony_ci					       int irq, int dma,
202862306a36Sopenharmony_ci					       struct device *dev,
202962306a36Sopenharmony_ci					       int irqflags,
203062306a36Sopenharmony_ci					       unsigned int mode_mask,
203162306a36Sopenharmony_ci					       unsigned char ecr_writable)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	struct parport_pc_private *priv;
203462306a36Sopenharmony_ci	struct parport_operations *ops;
203562306a36Sopenharmony_ci	struct parport *p;
203662306a36Sopenharmony_ci	int probedirq = PARPORT_IRQ_NONE;
203762306a36Sopenharmony_ci	struct resource *base_res;
203862306a36Sopenharmony_ci	struct resource	*ECR_res = NULL;
203962306a36Sopenharmony_ci	struct resource	*EPP_res = NULL;
204062306a36Sopenharmony_ci	struct platform_device *pdev = NULL;
204162306a36Sopenharmony_ci	int ret;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	if (!dev) {
204462306a36Sopenharmony_ci		/* We need a physical device to attach to, but none was
204562306a36Sopenharmony_ci		 * provided. Create our own. */
204662306a36Sopenharmony_ci		pdev = platform_device_register_simple("parport_pc",
204762306a36Sopenharmony_ci						       base, NULL, 0);
204862306a36Sopenharmony_ci		if (IS_ERR(pdev))
204962306a36Sopenharmony_ci			return NULL;
205062306a36Sopenharmony_ci		dev = &pdev->dev;
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci		ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(24));
205362306a36Sopenharmony_ci		if (ret) {
205462306a36Sopenharmony_ci			dev_err(dev, "Unable to set coherent dma mask: disabling DMA\n");
205562306a36Sopenharmony_ci			dma = PARPORT_DMA_NONE;
205662306a36Sopenharmony_ci		}
205762306a36Sopenharmony_ci	}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL);
206062306a36Sopenharmony_ci	if (!ops)
206162306a36Sopenharmony_ci		goto out1;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	priv = kmalloc(sizeof(struct parport_pc_private), GFP_KERNEL);
206462306a36Sopenharmony_ci	if (!priv)
206562306a36Sopenharmony_ci		goto out2;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	/* a misnomer, actually - it's allocate and reserve parport number */
206862306a36Sopenharmony_ci	p = parport_register_port(base, irq, dma, ops);
206962306a36Sopenharmony_ci	if (!p)
207062306a36Sopenharmony_ci		goto out3;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	base_res = request_region(base, 3, p->name);
207362306a36Sopenharmony_ci	if (!base_res)
207462306a36Sopenharmony_ci		goto out4;
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	memcpy(ops, &parport_pc_ops, sizeof(struct parport_operations));
207762306a36Sopenharmony_ci	priv->ctr = 0xc;
207862306a36Sopenharmony_ci	priv->ctr_writable = ~0x10;
207962306a36Sopenharmony_ci	priv->ecr = 0;
208062306a36Sopenharmony_ci	priv->ecr_writable = ecr_writable;
208162306a36Sopenharmony_ci	priv->fifo_depth = 0;
208262306a36Sopenharmony_ci	priv->dma_buf = NULL;
208362306a36Sopenharmony_ci	priv->dma_handle = 0;
208462306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->list);
208562306a36Sopenharmony_ci	priv->port = p;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	p->dev = dev;
208862306a36Sopenharmony_ci	p->base_hi = base_hi;
208962306a36Sopenharmony_ci	p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT;
209062306a36Sopenharmony_ci	p->private_data = priv;
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	if (base_hi) {
209362306a36Sopenharmony_ci		ECR_res = request_region(base_hi, 3, p->name);
209462306a36Sopenharmony_ci		if (ECR_res)
209562306a36Sopenharmony_ci			parport_ECR_present(p);
209662306a36Sopenharmony_ci	}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	if (base != 0x3bc) {
209962306a36Sopenharmony_ci		EPP_res = request_region(base+0x3, 5, p->name);
210062306a36Sopenharmony_ci		if (EPP_res)
210162306a36Sopenharmony_ci			if (!parport_EPP_supported(p))
210262306a36Sopenharmony_ci				parport_ECPEPP_supported(p);
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci	if (!parport_SPP_supported(p))
210562306a36Sopenharmony_ci		/* No port. */
210662306a36Sopenharmony_ci		goto out5;
210762306a36Sopenharmony_ci	if (priv->ecr)
210862306a36Sopenharmony_ci		parport_ECPPS2_supported(p);
210962306a36Sopenharmony_ci	else
211062306a36Sopenharmony_ci		parport_PS2_supported(p);
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	p->size = (p->modes & PARPORT_MODE_EPP) ? 8 : 3;
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci	pr_info("%s: PC-style at 0x%lx", p->name, p->base);
211562306a36Sopenharmony_ci	if (p->base_hi && priv->ecr)
211662306a36Sopenharmony_ci		pr_cont(" (0x%lx)", p->base_hi);
211762306a36Sopenharmony_ci	if (p->irq == PARPORT_IRQ_AUTO) {
211862306a36Sopenharmony_ci		p->irq = PARPORT_IRQ_NONE;
211962306a36Sopenharmony_ci		parport_irq_probe(p);
212062306a36Sopenharmony_ci	} else if (p->irq == PARPORT_IRQ_PROBEONLY) {
212162306a36Sopenharmony_ci		p->irq = PARPORT_IRQ_NONE;
212262306a36Sopenharmony_ci		parport_irq_probe(p);
212362306a36Sopenharmony_ci		probedirq = p->irq;
212462306a36Sopenharmony_ci		p->irq = PARPORT_IRQ_NONE;
212562306a36Sopenharmony_ci	}
212662306a36Sopenharmony_ci	if (p->irq != PARPORT_IRQ_NONE) {
212762306a36Sopenharmony_ci		pr_cont(", irq %d", p->irq);
212862306a36Sopenharmony_ci		priv->ctr_writable |= 0x10;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci		if (p->dma == PARPORT_DMA_AUTO) {
213162306a36Sopenharmony_ci			p->dma = PARPORT_DMA_NONE;
213262306a36Sopenharmony_ci			parport_dma_probe(p);
213362306a36Sopenharmony_ci		}
213462306a36Sopenharmony_ci	}
213562306a36Sopenharmony_ci	if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
213662306a36Sopenharmony_ci					   is mandatory (see above) */
213762306a36Sopenharmony_ci		p->dma = PARPORT_DMA_NONE;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_FIFO
214062306a36Sopenharmony_ci	if (parport_ECP_supported(p) &&
214162306a36Sopenharmony_ci	    p->dma != PARPORT_DMA_NOFIFO &&
214262306a36Sopenharmony_ci	    priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) {
214362306a36Sopenharmony_ci		p->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
214462306a36Sopenharmony_ci		if (p->dma != PARPORT_DMA_NONE)
214562306a36Sopenharmony_ci			p->modes |= PARPORT_MODE_DMA;
214662306a36Sopenharmony_ci	} else
214762306a36Sopenharmony_ci		/* We can't use the DMA channel after all. */
214862306a36Sopenharmony_ci		p->dma = PARPORT_DMA_NONE;
214962306a36Sopenharmony_ci#endif /* Allowed to use FIFO/DMA */
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	p->modes &= ~mode_mask;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_FIFO
215462306a36Sopenharmony_ci	if ((p->modes & PARPORT_MODE_COMPAT) != 0)
215562306a36Sopenharmony_ci		p->ops->compat_write_data = parport_pc_compat_write_block_pio;
215662306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_1284
215762306a36Sopenharmony_ci	if ((p->modes & PARPORT_MODE_ECP) != 0)
215862306a36Sopenharmony_ci		p->ops->ecp_write_data = parport_pc_ecp_write_block_pio;
215962306a36Sopenharmony_ci#endif
216062306a36Sopenharmony_ci	if ((p->modes & (PARPORT_MODE_ECP | PARPORT_MODE_COMPAT)) != 0) {
216162306a36Sopenharmony_ci		if ((p->modes & PARPORT_MODE_DMA) != 0)
216262306a36Sopenharmony_ci			pr_cont(", dma %d", p->dma);
216362306a36Sopenharmony_ci		else
216462306a36Sopenharmony_ci			pr_cont(", using FIFO");
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci#endif /* Allowed to use FIFO/DMA */
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	pr_cont(" [");
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci#define printmode(x)							\
217162306a36Sopenharmony_cido {									\
217262306a36Sopenharmony_ci	if (p->modes & PARPORT_MODE_##x)				\
217362306a36Sopenharmony_ci		pr_cont("%s%s", f++ ? "," : "", #x);			\
217462306a36Sopenharmony_ci} while (0)
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	{
217762306a36Sopenharmony_ci		int f = 0;
217862306a36Sopenharmony_ci		printmode(PCSPP);
217962306a36Sopenharmony_ci		printmode(TRISTATE);
218062306a36Sopenharmony_ci		printmode(COMPAT);
218162306a36Sopenharmony_ci		printmode(EPP);
218262306a36Sopenharmony_ci		printmode(ECP);
218362306a36Sopenharmony_ci		printmode(DMA);
218462306a36Sopenharmony_ci	}
218562306a36Sopenharmony_ci#undef printmode
218662306a36Sopenharmony_ci#ifndef CONFIG_PARPORT_1284
218762306a36Sopenharmony_ci	pr_cont("(,...)");
218862306a36Sopenharmony_ci#endif /* CONFIG_PARPORT_1284 */
218962306a36Sopenharmony_ci	pr_cont("]\n");
219062306a36Sopenharmony_ci	if (probedirq != PARPORT_IRQ_NONE)
219162306a36Sopenharmony_ci		pr_info("%s: irq %d detected\n", p->name, probedirq);
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	/* If No ECP release the ports grabbed above. */
219462306a36Sopenharmony_ci	if (ECR_res && (p->modes & PARPORT_MODE_ECP) == 0) {
219562306a36Sopenharmony_ci		release_region(base_hi, 3);
219662306a36Sopenharmony_ci		ECR_res = NULL;
219762306a36Sopenharmony_ci	}
219862306a36Sopenharmony_ci	/* Likewise for EEP ports */
219962306a36Sopenharmony_ci	if (EPP_res && (p->modes & PARPORT_MODE_EPP) == 0) {
220062306a36Sopenharmony_ci		release_region(base+3, 5);
220162306a36Sopenharmony_ci		EPP_res = NULL;
220262306a36Sopenharmony_ci	}
220362306a36Sopenharmony_ci	if (p->irq != PARPORT_IRQ_NONE) {
220462306a36Sopenharmony_ci		if (request_irq(p->irq, parport_irq_handler,
220562306a36Sopenharmony_ci				 irqflags, p->name, p)) {
220662306a36Sopenharmony_ci			pr_warn("%s: irq %d in use, resorting to polled operation\n",
220762306a36Sopenharmony_ci				p->name, p->irq);
220862306a36Sopenharmony_ci			p->irq = PARPORT_IRQ_NONE;
220962306a36Sopenharmony_ci			p->dma = PARPORT_DMA_NONE;
221062306a36Sopenharmony_ci		}
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_FIFO
221362306a36Sopenharmony_ci#ifdef HAS_DMA
221462306a36Sopenharmony_ci		if (p->dma != PARPORT_DMA_NONE) {
221562306a36Sopenharmony_ci			if (request_dma(p->dma, p->name)) {
221662306a36Sopenharmony_ci				pr_warn("%s: dma %d in use, resorting to PIO operation\n",
221762306a36Sopenharmony_ci					p->name, p->dma);
221862306a36Sopenharmony_ci				p->dma = PARPORT_DMA_NONE;
221962306a36Sopenharmony_ci			} else {
222062306a36Sopenharmony_ci				priv->dma_buf =
222162306a36Sopenharmony_ci				  dma_alloc_coherent(dev,
222262306a36Sopenharmony_ci						       PAGE_SIZE,
222362306a36Sopenharmony_ci						       &priv->dma_handle,
222462306a36Sopenharmony_ci						       GFP_KERNEL);
222562306a36Sopenharmony_ci				if (!priv->dma_buf) {
222662306a36Sopenharmony_ci					pr_warn("%s: cannot get buffer for DMA, resorting to PIO operation\n",
222762306a36Sopenharmony_ci						p->name);
222862306a36Sopenharmony_ci					free_dma(p->dma);
222962306a36Sopenharmony_ci					p->dma = PARPORT_DMA_NONE;
223062306a36Sopenharmony_ci				}
223162306a36Sopenharmony_ci			}
223262306a36Sopenharmony_ci		}
223362306a36Sopenharmony_ci#endif
223462306a36Sopenharmony_ci#endif
223562306a36Sopenharmony_ci	}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	/* Done probing.  Now put the port into a sensible start-up state. */
223862306a36Sopenharmony_ci	if (priv->ecr)
223962306a36Sopenharmony_ci		/*
224062306a36Sopenharmony_ci		 * Put the ECP detected port in PS2 mode.
224162306a36Sopenharmony_ci		 * Do this also for ports that have ECR but don't do ECP.
224262306a36Sopenharmony_ci		 */
224362306a36Sopenharmony_ci		ECR_WRITE(p, 0x34);
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	parport_pc_write_data(p, 0);
224662306a36Sopenharmony_ci	parport_pc_data_forward(p);
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	/* Now that we've told the sharing engine about the port, and
224962306a36Sopenharmony_ci	   found out its characteristics, let the high-level drivers
225062306a36Sopenharmony_ci	   know about it. */
225162306a36Sopenharmony_ci	spin_lock(&ports_lock);
225262306a36Sopenharmony_ci	list_add(&priv->list, &ports_list);
225362306a36Sopenharmony_ci	spin_unlock(&ports_lock);
225462306a36Sopenharmony_ci	parport_announce_port(p);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	return p;
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ciout5:
225962306a36Sopenharmony_ci	if (ECR_res)
226062306a36Sopenharmony_ci		release_region(base_hi, 3);
226162306a36Sopenharmony_ci	if (EPP_res)
226262306a36Sopenharmony_ci		release_region(base+0x3, 5);
226362306a36Sopenharmony_ci	release_region(base, 3);
226462306a36Sopenharmony_ciout4:
226562306a36Sopenharmony_ci	parport_del_port(p);
226662306a36Sopenharmony_ciout3:
226762306a36Sopenharmony_ci	kfree(priv);
226862306a36Sopenharmony_ciout2:
226962306a36Sopenharmony_ci	kfree(ops);
227062306a36Sopenharmony_ciout1:
227162306a36Sopenharmony_ci	if (pdev)
227262306a36Sopenharmony_ci		platform_device_unregister(pdev);
227362306a36Sopenharmony_ci	return NULL;
227462306a36Sopenharmony_ci}
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_cistruct parport *parport_pc_probe_port(unsigned long int base,
227762306a36Sopenharmony_ci				      unsigned long int base_hi,
227862306a36Sopenharmony_ci				      int irq, int dma,
227962306a36Sopenharmony_ci				      struct device *dev,
228062306a36Sopenharmony_ci				      int irqflags)
228162306a36Sopenharmony_ci{
228262306a36Sopenharmony_ci	return __parport_pc_probe_port(base, base_hi, irq, dma,
228362306a36Sopenharmony_ci				       dev, irqflags, 0, 0);
228462306a36Sopenharmony_ci}
228562306a36Sopenharmony_ciEXPORT_SYMBOL(parport_pc_probe_port);
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_civoid parport_pc_unregister_port(struct parport *p)
228862306a36Sopenharmony_ci{
228962306a36Sopenharmony_ci	struct parport_pc_private *priv = p->private_data;
229062306a36Sopenharmony_ci	struct parport_operations *ops = p->ops;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	parport_remove_port(p);
229362306a36Sopenharmony_ci	spin_lock(&ports_lock);
229462306a36Sopenharmony_ci	list_del_init(&priv->list);
229562306a36Sopenharmony_ci	spin_unlock(&ports_lock);
229662306a36Sopenharmony_ci#if defined(CONFIG_PARPORT_PC_FIFO) && defined(HAS_DMA)
229762306a36Sopenharmony_ci	if (p->dma != PARPORT_DMA_NONE)
229862306a36Sopenharmony_ci		free_dma(p->dma);
229962306a36Sopenharmony_ci#endif
230062306a36Sopenharmony_ci	if (p->irq != PARPORT_IRQ_NONE)
230162306a36Sopenharmony_ci		free_irq(p->irq, p);
230262306a36Sopenharmony_ci	release_region(p->base, 3);
230362306a36Sopenharmony_ci	if (p->size > 3)
230462306a36Sopenharmony_ci		release_region(p->base + 3, p->size - 3);
230562306a36Sopenharmony_ci	if (p->modes & PARPORT_MODE_ECP)
230662306a36Sopenharmony_ci		release_region(p->base_hi, 3);
230762306a36Sopenharmony_ci#if defined(CONFIG_PARPORT_PC_FIFO) && defined(HAS_DMA)
230862306a36Sopenharmony_ci	if (priv->dma_buf)
230962306a36Sopenharmony_ci		dma_free_coherent(p->physport->dev, PAGE_SIZE,
231062306a36Sopenharmony_ci				    priv->dma_buf,
231162306a36Sopenharmony_ci				    priv->dma_handle);
231262306a36Sopenharmony_ci#endif
231362306a36Sopenharmony_ci	kfree(p->private_data);
231462306a36Sopenharmony_ci	parport_del_port(p);
231562306a36Sopenharmony_ci	kfree(ops); /* hope no-one cached it */
231662306a36Sopenharmony_ci}
231762306a36Sopenharmony_ciEXPORT_SYMBOL(parport_pc_unregister_port);
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci#ifdef CONFIG_PCI
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci/* ITE support maintained by Rich Liu <richliu@poorman.org> */
232262306a36Sopenharmony_cistatic int sio_ite_8872_probe(struct pci_dev *pdev, int autoirq, int autodma,
232362306a36Sopenharmony_ci			      const struct parport_pc_via_data *via)
232462306a36Sopenharmony_ci{
232562306a36Sopenharmony_ci	short inta_addr[6] = { 0x2A0, 0x2C0, 0x220, 0x240, 0x1E0 };
232662306a36Sopenharmony_ci	u32 ite8872set;
232762306a36Sopenharmony_ci	u32 ite8872_lpt, ite8872_lpthi;
232862306a36Sopenharmony_ci	u8 ite8872_irq, type;
232962306a36Sopenharmony_ci	int irq;
233062306a36Sopenharmony_ci	int i;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	pr_debug("sio_ite_8872_probe()\n");
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	/* make sure which one chip */
233562306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
233662306a36Sopenharmony_ci		if (request_region(inta_addr[i], 32, "it887x")) {
233762306a36Sopenharmony_ci			int test;
233862306a36Sopenharmony_ci			pci_write_config_dword(pdev, 0x60,
233962306a36Sopenharmony_ci						0xe5000000 | inta_addr[i]);
234062306a36Sopenharmony_ci			pci_write_config_dword(pdev, 0x78,
234162306a36Sopenharmony_ci						0x00000000 | inta_addr[i]);
234262306a36Sopenharmony_ci			test = inb(inta_addr[i]);
234362306a36Sopenharmony_ci			if (test != 0xff)
234462306a36Sopenharmony_ci				break;
234562306a36Sopenharmony_ci			release_region(inta_addr[i], 32);
234662306a36Sopenharmony_ci		}
234762306a36Sopenharmony_ci	}
234862306a36Sopenharmony_ci	if (i >= 5) {
234962306a36Sopenharmony_ci		pr_info("parport_pc: cannot find ITE8872 INTA\n");
235062306a36Sopenharmony_ci		return 0;
235162306a36Sopenharmony_ci	}
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	type = inb(inta_addr[i] + 0x18);
235462306a36Sopenharmony_ci	type &= 0x0f;
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci	switch (type) {
235762306a36Sopenharmony_ci	case 0x2:
235862306a36Sopenharmony_ci		pr_info("parport_pc: ITE8871 found (1P)\n");
235962306a36Sopenharmony_ci		ite8872set = 0x64200000;
236062306a36Sopenharmony_ci		break;
236162306a36Sopenharmony_ci	case 0xa:
236262306a36Sopenharmony_ci		pr_info("parport_pc: ITE8875 found (1P)\n");
236362306a36Sopenharmony_ci		ite8872set = 0x64200000;
236462306a36Sopenharmony_ci		break;
236562306a36Sopenharmony_ci	case 0xe:
236662306a36Sopenharmony_ci		pr_info("parport_pc: ITE8872 found (2S1P)\n");
236762306a36Sopenharmony_ci		ite8872set = 0x64e00000;
236862306a36Sopenharmony_ci		break;
236962306a36Sopenharmony_ci	case 0x6:
237062306a36Sopenharmony_ci		pr_info("parport_pc: ITE8873 found (1S)\n");
237162306a36Sopenharmony_ci		release_region(inta_addr[i], 32);
237262306a36Sopenharmony_ci		return 0;
237362306a36Sopenharmony_ci	case 0x8:
237462306a36Sopenharmony_ci		pr_info("parport_pc: ITE8874 found (2S)\n");
237562306a36Sopenharmony_ci		release_region(inta_addr[i], 32);
237662306a36Sopenharmony_ci		return 0;
237762306a36Sopenharmony_ci	default:
237862306a36Sopenharmony_ci		pr_info("parport_pc: unknown ITE887x\n");
237962306a36Sopenharmony_ci		pr_info("parport_pc: please mail 'lspci -nvv' output to Rich.Liu@ite.com.tw\n");
238062306a36Sopenharmony_ci		release_region(inta_addr[i], 32);
238162306a36Sopenharmony_ci		return 0;
238262306a36Sopenharmony_ci	}
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	pci_read_config_byte(pdev, 0x3c, &ite8872_irq);
238562306a36Sopenharmony_ci	pci_read_config_dword(pdev, 0x1c, &ite8872_lpt);
238662306a36Sopenharmony_ci	ite8872_lpt &= 0x0000ff00;
238762306a36Sopenharmony_ci	pci_read_config_dword(pdev, 0x20, &ite8872_lpthi);
238862306a36Sopenharmony_ci	ite8872_lpthi &= 0x0000ff00;
238962306a36Sopenharmony_ci	pci_write_config_dword(pdev, 0x6c, 0xe3000000 | ite8872_lpt);
239062306a36Sopenharmony_ci	pci_write_config_dword(pdev, 0x70, 0xe3000000 | ite8872_lpthi);
239162306a36Sopenharmony_ci	pci_write_config_dword(pdev, 0x80, (ite8872_lpthi<<16) | ite8872_lpt);
239262306a36Sopenharmony_ci	/* SET SPP&EPP , Parallel Port NO DMA , Enable All Function */
239362306a36Sopenharmony_ci	/* SET Parallel IRQ */
239462306a36Sopenharmony_ci	pci_write_config_dword(pdev, 0x9c,
239562306a36Sopenharmony_ci				ite8872set | (ite8872_irq * 0x11111));
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	pr_debug("ITE887x: The IRQ is %d\n", ite8872_irq);
239862306a36Sopenharmony_ci	pr_debug("ITE887x: The PARALLEL I/O port is 0x%x\n", ite8872_lpt);
239962306a36Sopenharmony_ci	pr_debug("ITE887x: The PARALLEL I/O porthi is 0x%x\n", ite8872_lpthi);
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	/* Let the user (or defaults) steer us away from interrupts */
240262306a36Sopenharmony_ci	irq = ite8872_irq;
240362306a36Sopenharmony_ci	if (autoirq != PARPORT_IRQ_AUTO)
240462306a36Sopenharmony_ci		irq = PARPORT_IRQ_NONE;
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	/*
240762306a36Sopenharmony_ci	 * Release the resource so that parport_pc_probe_port can get it.
240862306a36Sopenharmony_ci	 */
240962306a36Sopenharmony_ci	release_region(inta_addr[i], 32);
241062306a36Sopenharmony_ci	if (parport_pc_probe_port(ite8872_lpt, ite8872_lpthi,
241162306a36Sopenharmony_ci				   irq, PARPORT_DMA_NONE, &pdev->dev, 0)) {
241262306a36Sopenharmony_ci		pr_info("parport_pc: ITE 8872 parallel port: io=0x%X",
241362306a36Sopenharmony_ci			ite8872_lpt);
241462306a36Sopenharmony_ci		if (irq != PARPORT_IRQ_NONE)
241562306a36Sopenharmony_ci			pr_cont(", irq=%d", irq);
241662306a36Sopenharmony_ci		pr_cont("\n");
241762306a36Sopenharmony_ci		return 1;
241862306a36Sopenharmony_ci	}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	return 0;
242162306a36Sopenharmony_ci}
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci/* VIA 8231 support by Pavel Fedin <sonic_amiga@rambler.ru>
242462306a36Sopenharmony_ci   based on VIA 686a support code by Jeff Garzik <jgarzik@pobox.com> */
242562306a36Sopenharmony_cistatic int parport_init_mode;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci/* Data for two known VIA chips */
242862306a36Sopenharmony_cistatic struct parport_pc_via_data via_686a_data = {
242962306a36Sopenharmony_ci	0x51,
243062306a36Sopenharmony_ci	0x50,
243162306a36Sopenharmony_ci	0x85,
243262306a36Sopenharmony_ci	0x02,
243362306a36Sopenharmony_ci	0xE2,
243462306a36Sopenharmony_ci	0xF0,
243562306a36Sopenharmony_ci	0xE6
243662306a36Sopenharmony_ci};
243762306a36Sopenharmony_cistatic struct parport_pc_via_data via_8231_data = {
243862306a36Sopenharmony_ci	0x45,
243962306a36Sopenharmony_ci	0x44,
244062306a36Sopenharmony_ci	0x50,
244162306a36Sopenharmony_ci	0x04,
244262306a36Sopenharmony_ci	0xF2,
244362306a36Sopenharmony_ci	0xFA,
244462306a36Sopenharmony_ci	0xF6
244562306a36Sopenharmony_ci};
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_cistatic int sio_via_probe(struct pci_dev *pdev, int autoirq, int autodma,
244862306a36Sopenharmony_ci			 const struct parport_pc_via_data *via)
244962306a36Sopenharmony_ci{
245062306a36Sopenharmony_ci	u8 tmp, tmp2, siofunc;
245162306a36Sopenharmony_ci	u8 ppcontrol = 0;
245262306a36Sopenharmony_ci	int dma, irq;
245362306a36Sopenharmony_ci	unsigned port1, port2;
245462306a36Sopenharmony_ci	unsigned have_epp = 0;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	printk(KERN_DEBUG "parport_pc: VIA 686A/8231 detected\n");
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	switch (parport_init_mode) {
245962306a36Sopenharmony_ci	case 1:
246062306a36Sopenharmony_ci		printk(KERN_DEBUG "parport_pc: setting SPP mode\n");
246162306a36Sopenharmony_ci		siofunc = VIA_FUNCTION_PARPORT_SPP;
246262306a36Sopenharmony_ci		break;
246362306a36Sopenharmony_ci	case 2:
246462306a36Sopenharmony_ci		printk(KERN_DEBUG "parport_pc: setting PS/2 mode\n");
246562306a36Sopenharmony_ci		siofunc = VIA_FUNCTION_PARPORT_SPP;
246662306a36Sopenharmony_ci		ppcontrol = VIA_PARPORT_BIDIR;
246762306a36Sopenharmony_ci		break;
246862306a36Sopenharmony_ci	case 3:
246962306a36Sopenharmony_ci		printk(KERN_DEBUG "parport_pc: setting EPP mode\n");
247062306a36Sopenharmony_ci		siofunc = VIA_FUNCTION_PARPORT_EPP;
247162306a36Sopenharmony_ci		ppcontrol = VIA_PARPORT_BIDIR;
247262306a36Sopenharmony_ci		have_epp = 1;
247362306a36Sopenharmony_ci		break;
247462306a36Sopenharmony_ci	case 4:
247562306a36Sopenharmony_ci		printk(KERN_DEBUG "parport_pc: setting ECP mode\n");
247662306a36Sopenharmony_ci		siofunc = VIA_FUNCTION_PARPORT_ECP;
247762306a36Sopenharmony_ci		ppcontrol = VIA_PARPORT_BIDIR;
247862306a36Sopenharmony_ci		break;
247962306a36Sopenharmony_ci	case 5:
248062306a36Sopenharmony_ci		printk(KERN_DEBUG "parport_pc: setting EPP+ECP mode\n");
248162306a36Sopenharmony_ci		siofunc = VIA_FUNCTION_PARPORT_ECP;
248262306a36Sopenharmony_ci		ppcontrol = VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP;
248362306a36Sopenharmony_ci		have_epp = 1;
248462306a36Sopenharmony_ci		break;
248562306a36Sopenharmony_ci	default:
248662306a36Sopenharmony_ci		printk(KERN_DEBUG "parport_pc: probing current configuration\n");
248762306a36Sopenharmony_ci		siofunc = VIA_FUNCTION_PROBE;
248862306a36Sopenharmony_ci		break;
248962306a36Sopenharmony_ci	}
249062306a36Sopenharmony_ci	/*
249162306a36Sopenharmony_ci	 * unlock super i/o configuration
249262306a36Sopenharmony_ci	 */
249362306a36Sopenharmony_ci	pci_read_config_byte(pdev, via->via_pci_superio_config_reg, &tmp);
249462306a36Sopenharmony_ci	tmp |= via->via_pci_superio_config_data;
249562306a36Sopenharmony_ci	pci_write_config_byte(pdev, via->via_pci_superio_config_reg, tmp);
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	/* Bits 1-0: Parallel Port Mode / Enable */
249862306a36Sopenharmony_ci	outb(via->viacfg_function, VIA_CONFIG_INDEX);
249962306a36Sopenharmony_ci	tmp = inb(VIA_CONFIG_DATA);
250062306a36Sopenharmony_ci	/* Bit 5: EPP+ECP enable; bit 7: PS/2 bidirectional port enable */
250162306a36Sopenharmony_ci	outb(via->viacfg_parport_control, VIA_CONFIG_INDEX);
250262306a36Sopenharmony_ci	tmp2 = inb(VIA_CONFIG_DATA);
250362306a36Sopenharmony_ci	if (siofunc == VIA_FUNCTION_PROBE) {
250462306a36Sopenharmony_ci		siofunc = tmp & VIA_FUNCTION_PARPORT_DISABLE;
250562306a36Sopenharmony_ci		ppcontrol = tmp2;
250662306a36Sopenharmony_ci	} else {
250762306a36Sopenharmony_ci		tmp &= ~VIA_FUNCTION_PARPORT_DISABLE;
250862306a36Sopenharmony_ci		tmp |= siofunc;
250962306a36Sopenharmony_ci		outb(via->viacfg_function, VIA_CONFIG_INDEX);
251062306a36Sopenharmony_ci		outb(tmp, VIA_CONFIG_DATA);
251162306a36Sopenharmony_ci		tmp2 &= ~(VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP);
251262306a36Sopenharmony_ci		tmp2 |= ppcontrol;
251362306a36Sopenharmony_ci		outb(via->viacfg_parport_control, VIA_CONFIG_INDEX);
251462306a36Sopenharmony_ci		outb(tmp2, VIA_CONFIG_DATA);
251562306a36Sopenharmony_ci	}
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	/* Parallel Port I/O Base Address, bits 9-2 */
251862306a36Sopenharmony_ci	outb(via->viacfg_parport_base, VIA_CONFIG_INDEX);
251962306a36Sopenharmony_ci	port1 = inb(VIA_CONFIG_DATA) << 2;
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	printk(KERN_DEBUG "parport_pc: Current parallel port base: 0x%X\n",
252262306a36Sopenharmony_ci	       port1);
252362306a36Sopenharmony_ci	if (port1 == 0x3BC && have_epp) {
252462306a36Sopenharmony_ci		outb(via->viacfg_parport_base, VIA_CONFIG_INDEX);
252562306a36Sopenharmony_ci		outb((0x378 >> 2), VIA_CONFIG_DATA);
252662306a36Sopenharmony_ci		printk(KERN_DEBUG "parport_pc: Parallel port base changed to 0x378\n");
252762306a36Sopenharmony_ci		port1 = 0x378;
252862306a36Sopenharmony_ci	}
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	/*
253162306a36Sopenharmony_ci	 * lock super i/o configuration
253262306a36Sopenharmony_ci	 */
253362306a36Sopenharmony_ci	pci_read_config_byte(pdev, via->via_pci_superio_config_reg, &tmp);
253462306a36Sopenharmony_ci	tmp &= ~via->via_pci_superio_config_data;
253562306a36Sopenharmony_ci	pci_write_config_byte(pdev, via->via_pci_superio_config_reg, tmp);
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	if (siofunc == VIA_FUNCTION_PARPORT_DISABLE) {
253862306a36Sopenharmony_ci		pr_info("parport_pc: VIA parallel port disabled in BIOS\n");
253962306a36Sopenharmony_ci		return 0;
254062306a36Sopenharmony_ci	}
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	/* Bits 7-4: PnP Routing for Parallel Port IRQ */
254362306a36Sopenharmony_ci	pci_read_config_byte(pdev, via->via_pci_parport_irq_reg, &tmp);
254462306a36Sopenharmony_ci	irq = ((tmp & VIA_IRQCONTROL_PARALLEL) >> 4);
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	if (siofunc == VIA_FUNCTION_PARPORT_ECP) {
254762306a36Sopenharmony_ci		/* Bits 3-2: PnP Routing for Parallel Port DMA */
254862306a36Sopenharmony_ci		pci_read_config_byte(pdev, via->via_pci_parport_dma_reg, &tmp);
254962306a36Sopenharmony_ci		dma = ((tmp & VIA_DMACONTROL_PARALLEL) >> 2);
255062306a36Sopenharmony_ci	} else
255162306a36Sopenharmony_ci		/* if ECP not enabled, DMA is not enabled, assumed
255262306a36Sopenharmony_ci		   bogus 'dma' value */
255362306a36Sopenharmony_ci		dma = PARPORT_DMA_NONE;
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	/* Let the user (or defaults) steer us away from interrupts and DMA */
255662306a36Sopenharmony_ci	if (autoirq == PARPORT_IRQ_NONE) {
255762306a36Sopenharmony_ci		irq = PARPORT_IRQ_NONE;
255862306a36Sopenharmony_ci		dma = PARPORT_DMA_NONE;
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci	if (autodma == PARPORT_DMA_NONE)
256162306a36Sopenharmony_ci		dma = PARPORT_DMA_NONE;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	switch (port1) {
256462306a36Sopenharmony_ci	case 0x3bc:
256562306a36Sopenharmony_ci		port2 = 0x7bc; break;
256662306a36Sopenharmony_ci	case 0x378:
256762306a36Sopenharmony_ci		port2 = 0x778; break;
256862306a36Sopenharmony_ci	case 0x278:
256962306a36Sopenharmony_ci		port2 = 0x678; break;
257062306a36Sopenharmony_ci	default:
257162306a36Sopenharmony_ci		pr_info("parport_pc: Weird VIA parport base 0x%X, ignoring\n",
257262306a36Sopenharmony_ci			port1);
257362306a36Sopenharmony_ci		return 0;
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	/* filter bogus IRQs */
257762306a36Sopenharmony_ci	switch (irq) {
257862306a36Sopenharmony_ci	case 0:
257962306a36Sopenharmony_ci	case 2:
258062306a36Sopenharmony_ci	case 8:
258162306a36Sopenharmony_ci	case 13:
258262306a36Sopenharmony_ci		irq = PARPORT_IRQ_NONE;
258362306a36Sopenharmony_ci		break;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	default: /* do nothing */
258662306a36Sopenharmony_ci		break;
258762306a36Sopenharmony_ci	}
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	/* finally, do the probe with values obtained */
259062306a36Sopenharmony_ci	if (parport_pc_probe_port(port1, port2, irq, dma, &pdev->dev, 0)) {
259162306a36Sopenharmony_ci		pr_info("parport_pc: VIA parallel port: io=0x%X", port1);
259262306a36Sopenharmony_ci		if (irq != PARPORT_IRQ_NONE)
259362306a36Sopenharmony_ci			pr_cont(", irq=%d", irq);
259462306a36Sopenharmony_ci		if (dma != PARPORT_DMA_NONE)
259562306a36Sopenharmony_ci			pr_cont(", dma=%d", dma);
259662306a36Sopenharmony_ci		pr_cont("\n");
259762306a36Sopenharmony_ci		return 1;
259862306a36Sopenharmony_ci	}
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	pr_warn("parport_pc: Strange, can't probe VIA parallel port: io=0x%X, irq=%d, dma=%d\n",
260162306a36Sopenharmony_ci		port1, irq, dma);
260262306a36Sopenharmony_ci	return 0;
260362306a36Sopenharmony_ci}
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_cienum parport_pc_sio_types {
260762306a36Sopenharmony_ci	sio_via_686a = 0,   /* Via VT82C686A motherboard Super I/O */
260862306a36Sopenharmony_ci	sio_via_8231,	    /* Via VT8231 south bridge integrated Super IO */
260962306a36Sopenharmony_ci	sio_ite_8872,
261062306a36Sopenharmony_ci	last_sio
261162306a36Sopenharmony_ci};
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci/* each element directly indexed from enum list, above */
261462306a36Sopenharmony_cistatic struct parport_pc_superio {
261562306a36Sopenharmony_ci	int (*probe) (struct pci_dev *pdev, int autoirq, int autodma,
261662306a36Sopenharmony_ci		      const struct parport_pc_via_data *via);
261762306a36Sopenharmony_ci	const struct parport_pc_via_data *via;
261862306a36Sopenharmony_ci} parport_pc_superio_info[] = {
261962306a36Sopenharmony_ci	{ sio_via_probe, &via_686a_data, },
262062306a36Sopenharmony_ci	{ sio_via_probe, &via_8231_data, },
262162306a36Sopenharmony_ci	{ sio_ite_8872_probe, NULL, },
262262306a36Sopenharmony_ci};
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_cienum parport_pc_pci_cards {
262562306a36Sopenharmony_ci	siig_1p_10x = last_sio,
262662306a36Sopenharmony_ci	siig_2p_10x,
262762306a36Sopenharmony_ci	siig_1p_20x,
262862306a36Sopenharmony_ci	siig_2p_20x,
262962306a36Sopenharmony_ci	lava_parallel,
263062306a36Sopenharmony_ci	lava_parallel_dual_a,
263162306a36Sopenharmony_ci	lava_parallel_dual_b,
263262306a36Sopenharmony_ci	boca_ioppar,
263362306a36Sopenharmony_ci	plx_9050,
263462306a36Sopenharmony_ci	timedia_4006a,
263562306a36Sopenharmony_ci	timedia_4014,
263662306a36Sopenharmony_ci	timedia_4008a,
263762306a36Sopenharmony_ci	timedia_4018,
263862306a36Sopenharmony_ci	timedia_9018a,
263962306a36Sopenharmony_ci	syba_2p_epp,
264062306a36Sopenharmony_ci	syba_1p_ecp,
264162306a36Sopenharmony_ci	titan_010l,
264262306a36Sopenharmony_ci	avlab_1p,
264362306a36Sopenharmony_ci	avlab_2p,
264462306a36Sopenharmony_ci	oxsemi_952,
264562306a36Sopenharmony_ci	oxsemi_954,
264662306a36Sopenharmony_ci	oxsemi_840,
264762306a36Sopenharmony_ci	oxsemi_pcie_pport,
264862306a36Sopenharmony_ci	aks_0100,
264962306a36Sopenharmony_ci	mobility_pp,
265062306a36Sopenharmony_ci	netmos_9900,
265162306a36Sopenharmony_ci	netmos_9705,
265262306a36Sopenharmony_ci	netmos_9715,
265362306a36Sopenharmony_ci	netmos_9755,
265462306a36Sopenharmony_ci	netmos_9805,
265562306a36Sopenharmony_ci	netmos_9815,
265662306a36Sopenharmony_ci	netmos_9901,
265762306a36Sopenharmony_ci	netmos_9865,
265862306a36Sopenharmony_ci	asix_ax99100,
265962306a36Sopenharmony_ci	quatech_sppxp100,
266062306a36Sopenharmony_ci	wch_ch382l,
266162306a36Sopenharmony_ci	brainboxes_uc146,
266262306a36Sopenharmony_ci	brainboxes_px203,
266362306a36Sopenharmony_ci};
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci/* each element directly indexed from enum list, above
266762306a36Sopenharmony_ci * (but offset by last_sio) */
266862306a36Sopenharmony_cistatic struct parport_pc_pci {
266962306a36Sopenharmony_ci	int numports;
267062306a36Sopenharmony_ci	struct { /* BAR (base address registers) numbers in the config
267162306a36Sopenharmony_ci		    space header */
267262306a36Sopenharmony_ci		int lo;
267362306a36Sopenharmony_ci		int hi;
267462306a36Sopenharmony_ci		/* -1 if not there, >6 for offset-method (max BAR is 6) */
267562306a36Sopenharmony_ci	} addr[2];
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ci	/* Bit field of parport modes to exclude. */
267862306a36Sopenharmony_ci	unsigned int mode_mask;
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci	/* If non-zero, sets the bitmask of writable ECR bits.  In that
268162306a36Sopenharmony_ci	 * case additionally bit 0 will be forcibly set on writes. */
268262306a36Sopenharmony_ci	unsigned char ecr_writable;
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	/* If set, this is called immediately after pci_enable_device.
268562306a36Sopenharmony_ci	 * If it returns non-zero, no probing will take place and the
268662306a36Sopenharmony_ci	 * ports will not be used. */
268762306a36Sopenharmony_ci	int (*preinit_hook) (struct pci_dev *pdev, int autoirq, int autodma);
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	/* If set, this is called after probing for ports.  If 'failed'
269062306a36Sopenharmony_ci	 * is non-zero we couldn't use any of the ports. */
269162306a36Sopenharmony_ci	void (*postinit_hook) (struct pci_dev *pdev, int failed);
269262306a36Sopenharmony_ci} cards[] = {
269362306a36Sopenharmony_ci	/* siig_1p_10x */		{ 1, { { 2, 3 }, } },
269462306a36Sopenharmony_ci	/* siig_2p_10x */		{ 2, { { 2, 3 }, { 4, 5 }, } },
269562306a36Sopenharmony_ci	/* siig_1p_20x */		{ 1, { { 0, 1 }, } },
269662306a36Sopenharmony_ci	/* siig_2p_20x */		{ 2, { { 0, 1 }, { 2, 3 }, } },
269762306a36Sopenharmony_ci	/* lava_parallel */		{ 1, { { 0, -1 }, } },
269862306a36Sopenharmony_ci	/* lava_parallel_dual_a */	{ 1, { { 0, -1 }, } },
269962306a36Sopenharmony_ci	/* lava_parallel_dual_b */	{ 1, { { 0, -1 }, } },
270062306a36Sopenharmony_ci	/* boca_ioppar */		{ 1, { { 0, -1 }, } },
270162306a36Sopenharmony_ci	/* plx_9050 */			{ 2, { { 4, -1 }, { 5, -1 }, } },
270262306a36Sopenharmony_ci	/* timedia_4006a */             { 1, { { 0, -1 }, } },
270362306a36Sopenharmony_ci	/* timedia_4014  */             { 2, { { 0, -1 }, { 2, -1 }, } },
270462306a36Sopenharmony_ci	/* timedia_4008a */             { 1, { { 0, 1 }, } },
270562306a36Sopenharmony_ci	/* timedia_4018  */             { 2, { { 0, 1 }, { 2, 3 }, } },
270662306a36Sopenharmony_ci	/* timedia_9018a */             { 2, { { 0, 1 }, { 2, 3 }, } },
270762306a36Sopenharmony_ci					/* SYBA uses fixed offsets in
270862306a36Sopenharmony_ci					   a 1K io window */
270962306a36Sopenharmony_ci	/* syba_2p_epp AP138B */	{ 2, { { 0, 0x078 }, { 0, 0x178 }, } },
271062306a36Sopenharmony_ci	/* syba_1p_ecp W83787 */	{ 1, { { 0, 0x078 }, } },
271162306a36Sopenharmony_ci	/* titan_010l */		{ 1, { { 3, -1 }, } },
271262306a36Sopenharmony_ci	/* avlab_1p		*/	{ 1, { { 0, 1}, } },
271362306a36Sopenharmony_ci	/* avlab_2p		*/	{ 2, { { 0, 1}, { 2, 3 },} },
271462306a36Sopenharmony_ci	/* The Oxford Semi cards are unusual: older variants of 954 don't
271562306a36Sopenharmony_ci	 * support ECP, and 840 locks up if you write 1 to bit 2!  None
271662306a36Sopenharmony_ci	 * implement nFault or service interrupts and all require 00001
271762306a36Sopenharmony_ci	 * bit pattern to be used for bits 4:0 with ECR writes. */
271862306a36Sopenharmony_ci	/* oxsemi_952 */		{ 1, { { 0, 1 }, },
271962306a36Sopenharmony_ci					  PARPORT_MODE_COMPAT, ECR_MODE_MASK },
272062306a36Sopenharmony_ci	/* oxsemi_954 */		{ 1, { { 0, 1 }, },
272162306a36Sopenharmony_ci					  PARPORT_MODE_ECP |
272262306a36Sopenharmony_ci					  PARPORT_MODE_COMPAT, ECR_MODE_MASK },
272362306a36Sopenharmony_ci	/* oxsemi_840 */		{ 1, { { 0, 1 }, },
272462306a36Sopenharmony_ci					  PARPORT_MODE_COMPAT, ECR_MODE_MASK },
272562306a36Sopenharmony_ci	/* oxsemi_pcie_pport */		{ 1, { { 0, 1 }, },
272662306a36Sopenharmony_ci					  PARPORT_MODE_COMPAT, ECR_MODE_MASK },
272762306a36Sopenharmony_ci	/* aks_0100 */                  { 1, { { 0, -1 }, } },
272862306a36Sopenharmony_ci	/* mobility_pp */		{ 1, { { 0, 1 }, } },
272962306a36Sopenharmony_ci	/* netmos_9900 */		{ 1, { { 0, -1 }, } },
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	/* The netmos entries below are untested */
273262306a36Sopenharmony_ci	/* netmos_9705 */               { 1, { { 0, -1 }, } },
273362306a36Sopenharmony_ci	/* netmos_9715 */               { 2, { { 0, 1 }, { 2, 3 },} },
273462306a36Sopenharmony_ci	/* netmos_9755 */               { 2, { { 0, 1 }, { 2, 3 },} },
273562306a36Sopenharmony_ci	/* netmos_9805 */		{ 1, { { 0, 1 }, } },
273662306a36Sopenharmony_ci	/* netmos_9815 */		{ 2, { { 0, 1 }, { 2, 3 }, } },
273762306a36Sopenharmony_ci	/* netmos_9901 */               { 1, { { 0, -1 }, } },
273862306a36Sopenharmony_ci	/* netmos_9865 */               { 1, { { 0, -1 }, } },
273962306a36Sopenharmony_ci	/* asix_ax99100 */		{ 1, { { 0, 1 }, } },
274062306a36Sopenharmony_ci	/* quatech_sppxp100 */		{ 1, { { 0, 1 }, } },
274162306a36Sopenharmony_ci	/* wch_ch382l */		{ 1, { { 2, -1 }, } },
274262306a36Sopenharmony_ci	/* brainboxes_uc146 */	{ 1, { { 3, -1 }, } },
274362306a36Sopenharmony_ci	/* brainboxes_px203 */	{ 1, { { 0, -1 }, } },
274462306a36Sopenharmony_ci};
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_cistatic const struct pci_device_id parport_pc_pci_tbl[] = {
274762306a36Sopenharmony_ci	/* Super-IO onboard chips */
274862306a36Sopenharmony_ci	{ 0x1106, 0x0686, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_686a },
274962306a36Sopenharmony_ci	{ 0x1106, 0x8231, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_8231 },
275062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872,
275162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_ite_8872 },
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci	/* PCI cards */
275462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x,
275562306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_10x },
275662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x,
275762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_10x },
275862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x,
275962306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_20x },
276062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x,
276162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_20x },
276262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL,
276362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel },
276462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A,
276562306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_a },
276662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B,
276762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_b },
276862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR,
276962306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, boca_ioppar },
277062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
277162306a36Sopenharmony_ci	  PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 },
277262306a36Sopenharmony_ci	/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
277362306a36Sopenharmony_ci	{ 0x1409, 0x7268, 0x1409, 0x0101, 0, 0, timedia_4006a },
277462306a36Sopenharmony_ci	{ 0x1409, 0x7268, 0x1409, 0x0102, 0, 0, timedia_4014 },
277562306a36Sopenharmony_ci	{ 0x1409, 0x7268, 0x1409, 0x0103, 0, 0, timedia_4008a },
277662306a36Sopenharmony_ci	{ 0x1409, 0x7268, 0x1409, 0x0104, 0, 0, timedia_4018 },
277762306a36Sopenharmony_ci	{ 0x1409, 0x7268, 0x1409, 0x9018, 0, 0, timedia_9018a },
277862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_2P_EPP,
277962306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_2p_epp },
278062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_1P_ECP,
278162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_1p_ecp },
278262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_010L,
278362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_010l },
278462306a36Sopenharmony_ci	/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
278562306a36Sopenharmony_ci	/* AFAVLAB_TK9902 */
278662306a36Sopenharmony_ci	{ 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p},
278762306a36Sopenharmony_ci	{ 0x14db, 0x2121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2p},
278862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP,
278962306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_952 },
279062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP,
279162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_954 },
279262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840,
279362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_840 },
279462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840,
279562306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
279662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G,
279762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
279862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0,
279962306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
280062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G,
280162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
280262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1,
280362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
280462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G,
280562306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
280662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U,
280762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
280862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU,
280962306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
281062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_AKS, PCI_DEVICE_ID_AKS_ALADDINCARD,
281162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 },
281262306a36Sopenharmony_ci	{ 0x14f2, 0x0121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, mobility_pp },
281362306a36Sopenharmony_ci	/* NetMos communication controllers */
281462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
281562306a36Sopenharmony_ci	  0xA000, 0x2000, 0, 0, netmos_9900 },
281662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705,
281762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 },
281862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715,
281962306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 },
282062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755,
282162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 },
282262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805,
282362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 },
282462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815,
282562306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 },
282662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
282762306a36Sopenharmony_ci	  0xA000, 0x2000, 0, 0, netmos_9901 },
282862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
282962306a36Sopenharmony_ci	  0xA000, 0x1000, 0, 0, netmos_9865 },
283062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
283162306a36Sopenharmony_ci	  0xA000, 0x2000, 0, 0, netmos_9865 },
283262306a36Sopenharmony_ci	/* ASIX AX99100 PCIe to Multi I/O Controller */
283362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ASIX, PCI_DEVICE_ID_ASIX_AX99100,
283462306a36Sopenharmony_ci	  0xA000, 0x2000, 0, 0, asix_ax99100 },
283562306a36Sopenharmony_ci	/* Quatech SPPXP-100 Parallel port PCI ExpressCard */
283662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100,
283762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
283862306a36Sopenharmony_ci	/* WCH CH382L PCI-E single parallel port card */
283962306a36Sopenharmony_ci	{ 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l },
284062306a36Sopenharmony_ci	/* Brainboxes IX-500/550 */
284162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_INTASHIELD, 0x402a,
284262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
284362306a36Sopenharmony_ci	/* Brainboxes UC-146/UC-157 */
284462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_INTASHIELD, 0x0be1,
284562306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 },
284662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_INTASHIELD, 0x0be2,
284762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 },
284862306a36Sopenharmony_ci	/* Brainboxes PX-146/PX-257 */
284962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_INTASHIELD, 0x401c,
285062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
285162306a36Sopenharmony_ci	/* Brainboxes PX-203 */
285262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_INTASHIELD, 0x4007,
285362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px203 },
285462306a36Sopenharmony_ci	/* Brainboxes PX-475 */
285562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_INTASHIELD, 0x401f,
285662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
285762306a36Sopenharmony_ci	{ 0, } /* terminate list */
285862306a36Sopenharmony_ci};
285962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_cistruct pci_parport_data {
286262306a36Sopenharmony_ci	int num;
286362306a36Sopenharmony_ci	struct parport *ports[2];
286462306a36Sopenharmony_ci};
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_cistatic int parport_pc_pci_probe(struct pci_dev *dev,
286762306a36Sopenharmony_ci					   const struct pci_device_id *id)
286862306a36Sopenharmony_ci{
286962306a36Sopenharmony_ci	int err, count, n, i = id->driver_data;
287062306a36Sopenharmony_ci	struct pci_parport_data *data;
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	if (i < last_sio)
287362306a36Sopenharmony_ci		/* This is an onboard Super-IO and has already been probed */
287462306a36Sopenharmony_ci		return 0;
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	/* This is a PCI card */
287762306a36Sopenharmony_ci	i -= last_sio;
287862306a36Sopenharmony_ci	count = 0;
287962306a36Sopenharmony_ci	err = pci_enable_device(dev);
288062306a36Sopenharmony_ci	if (err)
288162306a36Sopenharmony_ci		return err;
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	data = kmalloc(sizeof(struct pci_parport_data), GFP_KERNEL);
288462306a36Sopenharmony_ci	if (!data)
288562306a36Sopenharmony_ci		return -ENOMEM;
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	if (cards[i].preinit_hook &&
288862306a36Sopenharmony_ci	    cards[i].preinit_hook(dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) {
288962306a36Sopenharmony_ci		kfree(data);
289062306a36Sopenharmony_ci		return -ENODEV;
289162306a36Sopenharmony_ci	}
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	for (n = 0; n < cards[i].numports; n++) {
289462306a36Sopenharmony_ci		int lo = cards[i].addr[n].lo;
289562306a36Sopenharmony_ci		int hi = cards[i].addr[n].hi;
289662306a36Sopenharmony_ci		int irq;
289762306a36Sopenharmony_ci		unsigned long io_lo, io_hi;
289862306a36Sopenharmony_ci		io_lo = pci_resource_start(dev, lo);
289962306a36Sopenharmony_ci		io_hi = 0;
290062306a36Sopenharmony_ci		if ((hi >= 0) && (hi <= 6))
290162306a36Sopenharmony_ci			io_hi = pci_resource_start(dev, hi);
290262306a36Sopenharmony_ci		else if (hi > 6)
290362306a36Sopenharmony_ci			io_lo += hi; /* Reinterpret the meaning of
290462306a36Sopenharmony_ci					"hi" as an offset (see SYBA
290562306a36Sopenharmony_ci					def.) */
290662306a36Sopenharmony_ci		/* TODO: test if sharing interrupts works */
290762306a36Sopenharmony_ci		irq = dev->irq;
290862306a36Sopenharmony_ci		if (irq == IRQ_NONE) {
290962306a36Sopenharmony_ci			printk(KERN_DEBUG "PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx)\n",
291062306a36Sopenharmony_ci			       id->vendor, id->device, io_lo, io_hi);
291162306a36Sopenharmony_ci			irq = PARPORT_IRQ_NONE;
291262306a36Sopenharmony_ci		} else {
291362306a36Sopenharmony_ci			printk(KERN_DEBUG "PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx), IRQ %d\n",
291462306a36Sopenharmony_ci			       id->vendor, id->device, io_lo, io_hi, irq);
291562306a36Sopenharmony_ci		}
291662306a36Sopenharmony_ci		data->ports[count] =
291762306a36Sopenharmony_ci			__parport_pc_probe_port(io_lo, io_hi, irq,
291862306a36Sopenharmony_ci						PARPORT_DMA_NONE, &dev->dev,
291962306a36Sopenharmony_ci						IRQF_SHARED,
292062306a36Sopenharmony_ci						cards[i].mode_mask,
292162306a36Sopenharmony_ci						cards[i].ecr_writable);
292262306a36Sopenharmony_ci		if (data->ports[count])
292362306a36Sopenharmony_ci			count++;
292462306a36Sopenharmony_ci	}
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	data->num = count;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	if (cards[i].postinit_hook)
292962306a36Sopenharmony_ci		cards[i].postinit_hook(dev, count == 0);
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci	if (count) {
293262306a36Sopenharmony_ci		pci_set_drvdata(dev, data);
293362306a36Sopenharmony_ci		return 0;
293462306a36Sopenharmony_ci	}
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	kfree(data);
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	return -ENODEV;
293962306a36Sopenharmony_ci}
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_cistatic void parport_pc_pci_remove(struct pci_dev *dev)
294262306a36Sopenharmony_ci{
294362306a36Sopenharmony_ci	struct pci_parport_data *data = pci_get_drvdata(dev);
294462306a36Sopenharmony_ci	int i;
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci	if (data) {
294762306a36Sopenharmony_ci		for (i = data->num - 1; i >= 0; i--)
294862306a36Sopenharmony_ci			parport_pc_unregister_port(data->ports[i]);
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci		kfree(data);
295162306a36Sopenharmony_ci	}
295262306a36Sopenharmony_ci}
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_cistatic struct pci_driver parport_pc_pci_driver = {
295562306a36Sopenharmony_ci	.name		= "parport_pc",
295662306a36Sopenharmony_ci	.id_table	= parport_pc_pci_tbl,
295762306a36Sopenharmony_ci	.probe		= parport_pc_pci_probe,
295862306a36Sopenharmony_ci	.remove		= parport_pc_pci_remove,
295962306a36Sopenharmony_ci};
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_cistatic int __init parport_pc_init_superio(int autoirq, int autodma)
296262306a36Sopenharmony_ci{
296362306a36Sopenharmony_ci	const struct pci_device_id *id;
296462306a36Sopenharmony_ci	struct pci_dev *pdev = NULL;
296562306a36Sopenharmony_ci	int ret = 0;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	for_each_pci_dev(pdev) {
296862306a36Sopenharmony_ci		id = pci_match_id(parport_pc_pci_tbl, pdev);
296962306a36Sopenharmony_ci		if (id == NULL || id->driver_data >= last_sio)
297062306a36Sopenharmony_ci			continue;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci		if (parport_pc_superio_info[id->driver_data].probe(
297362306a36Sopenharmony_ci			pdev, autoirq, autodma,
297462306a36Sopenharmony_ci			parport_pc_superio_info[id->driver_data].via)) {
297562306a36Sopenharmony_ci			ret++;
297662306a36Sopenharmony_ci		}
297762306a36Sopenharmony_ci	}
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	return ret; /* number of devices found */
298062306a36Sopenharmony_ci}
298162306a36Sopenharmony_ci#else
298262306a36Sopenharmony_cistatic struct pci_driver parport_pc_pci_driver;
298362306a36Sopenharmony_cistatic int __init parport_pc_init_superio(int autoirq, int autodma)
298462306a36Sopenharmony_ci{
298562306a36Sopenharmony_ci	return 0;
298662306a36Sopenharmony_ci}
298762306a36Sopenharmony_ci#endif /* CONFIG_PCI */
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci#ifdef CONFIG_PNP
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_cistatic const struct pnp_device_id parport_pc_pnp_tbl[] = {
299262306a36Sopenharmony_ci	/* Standard LPT Printer Port */
299362306a36Sopenharmony_ci	{.id = "PNP0400", .driver_data = 0},
299462306a36Sopenharmony_ci	/* ECP Printer Port */
299562306a36Sopenharmony_ci	{.id = "PNP0401", .driver_data = 0},
299662306a36Sopenharmony_ci	{ }
299762306a36Sopenharmony_ci};
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, parport_pc_pnp_tbl);
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_cistatic int parport_pc_pnp_probe(struct pnp_dev *dev,
300262306a36Sopenharmony_ci						const struct pnp_device_id *id)
300362306a36Sopenharmony_ci{
300462306a36Sopenharmony_ci	struct parport *pdata;
300562306a36Sopenharmony_ci	unsigned long io_lo, io_hi;
300662306a36Sopenharmony_ci	int dma, irq;
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	if (pnp_port_valid(dev, 0) &&
300962306a36Sopenharmony_ci		!(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) {
301062306a36Sopenharmony_ci		io_lo = pnp_port_start(dev, 0);
301162306a36Sopenharmony_ci	} else
301262306a36Sopenharmony_ci		return -EINVAL;
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	if (pnp_port_valid(dev, 1) &&
301562306a36Sopenharmony_ci		!(pnp_port_flags(dev, 1) & IORESOURCE_DISABLED)) {
301662306a36Sopenharmony_ci		io_hi = pnp_port_start(dev, 1);
301762306a36Sopenharmony_ci	} else
301862306a36Sopenharmony_ci		io_hi = 0;
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci	if (pnp_irq_valid(dev, 0) &&
302162306a36Sopenharmony_ci		!(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED)) {
302262306a36Sopenharmony_ci		irq = pnp_irq(dev, 0);
302362306a36Sopenharmony_ci	} else
302462306a36Sopenharmony_ci		irq = PARPORT_IRQ_NONE;
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_ci	if (pnp_dma_valid(dev, 0) &&
302762306a36Sopenharmony_ci		!(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED)) {
302862306a36Sopenharmony_ci		dma = pnp_dma(dev, 0);
302962306a36Sopenharmony_ci	} else
303062306a36Sopenharmony_ci		dma = PARPORT_DMA_NONE;
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	dev_info(&dev->dev, "reported by %s\n", dev->protocol->name);
303362306a36Sopenharmony_ci	pdata = parport_pc_probe_port(io_lo, io_hi, irq, dma, &dev->dev, 0);
303462306a36Sopenharmony_ci	if (pdata == NULL)
303562306a36Sopenharmony_ci		return -ENODEV;
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	pnp_set_drvdata(dev, pdata);
303862306a36Sopenharmony_ci	return 0;
303962306a36Sopenharmony_ci}
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_cistatic void parport_pc_pnp_remove(struct pnp_dev *dev)
304262306a36Sopenharmony_ci{
304362306a36Sopenharmony_ci	struct parport *pdata = (struct parport *)pnp_get_drvdata(dev);
304462306a36Sopenharmony_ci	if (!pdata)
304562306a36Sopenharmony_ci		return;
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci	parport_pc_unregister_port(pdata);
304862306a36Sopenharmony_ci}
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci/* we only need the pnp layer to activate the device, at least for now */
305162306a36Sopenharmony_cistatic struct pnp_driver parport_pc_pnp_driver = {
305262306a36Sopenharmony_ci	.name		= "parport_pc",
305362306a36Sopenharmony_ci	.id_table	= parport_pc_pnp_tbl,
305462306a36Sopenharmony_ci	.probe		= parport_pc_pnp_probe,
305562306a36Sopenharmony_ci	.remove		= parport_pc_pnp_remove,
305662306a36Sopenharmony_ci};
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci#else
305962306a36Sopenharmony_cistatic struct pnp_driver parport_pc_pnp_driver;
306062306a36Sopenharmony_ci#endif /* CONFIG_PNP */
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_cistatic int parport_pc_platform_probe(struct platform_device *pdev)
306362306a36Sopenharmony_ci{
306462306a36Sopenharmony_ci	/* Always succeed, the actual probing is done in
306562306a36Sopenharmony_ci	 * parport_pc_probe_port(). */
306662306a36Sopenharmony_ci	return 0;
306762306a36Sopenharmony_ci}
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_cistatic struct platform_driver parport_pc_platform_driver = {
307062306a36Sopenharmony_ci	.driver = {
307162306a36Sopenharmony_ci		.name	= "parport_pc",
307262306a36Sopenharmony_ci	},
307362306a36Sopenharmony_ci	.probe		= parport_pc_platform_probe,
307462306a36Sopenharmony_ci};
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
307762306a36Sopenharmony_cistatic int __attribute__((unused))
307862306a36Sopenharmony_ciparport_pc_find_isa_ports(int autoirq, int autodma)
307962306a36Sopenharmony_ci{
308062306a36Sopenharmony_ci	int count = 0;
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL, 0))
308362306a36Sopenharmony_ci		count++;
308462306a36Sopenharmony_ci	if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL, 0))
308562306a36Sopenharmony_ci		count++;
308662306a36Sopenharmony_ci	if (parport_pc_probe_port(0x278, 0x678, autoirq, autodma, NULL, 0))
308762306a36Sopenharmony_ci		count++;
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci	return count;
309062306a36Sopenharmony_ci}
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci/* This function is called by parport_pc_init if the user didn't
309362306a36Sopenharmony_ci * specify any ports to probe.  Its job is to find some ports.  Order
309462306a36Sopenharmony_ci * is important here -- we want ISA ports to be registered first,
309562306a36Sopenharmony_ci * followed by PCI cards (for least surprise), but before that we want
309662306a36Sopenharmony_ci * to do chipset-specific tests for some onboard ports that we know
309762306a36Sopenharmony_ci * about.
309862306a36Sopenharmony_ci *
309962306a36Sopenharmony_ci * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
310062306a36Sopenharmony_ci * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
310162306a36Sopenharmony_ci */
310262306a36Sopenharmony_cistatic void __init parport_pc_find_ports(int autoirq, int autodma)
310362306a36Sopenharmony_ci{
310462306a36Sopenharmony_ci	int count = 0, err;
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci#ifdef CONFIG_PARPORT_PC_SUPERIO
310762306a36Sopenharmony_ci	detect_and_report_it87();
310862306a36Sopenharmony_ci	detect_and_report_winbond();
310962306a36Sopenharmony_ci	detect_and_report_smsc();
311062306a36Sopenharmony_ci#endif
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	/* Onboard SuperIO chipsets that show themselves on the PCI bus. */
311362306a36Sopenharmony_ci	count += parport_pc_init_superio(autoirq, autodma);
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_ci	/* PnP ports, skip detection if SuperIO already found them */
311662306a36Sopenharmony_ci	if (!count) {
311762306a36Sopenharmony_ci		err = pnp_register_driver(&parport_pc_pnp_driver);
311862306a36Sopenharmony_ci		if (!err)
311962306a36Sopenharmony_ci			pnp_registered_parport = 1;
312062306a36Sopenharmony_ci	}
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci	/* ISA ports and whatever (see asm/parport.h). */
312362306a36Sopenharmony_ci	parport_pc_find_nonpci_ports(autoirq, autodma);
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	err = pci_register_driver(&parport_pc_pci_driver);
312662306a36Sopenharmony_ci	if (!err)
312762306a36Sopenharmony_ci		pci_registered_parport = 1;
312862306a36Sopenharmony_ci}
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci/*
313162306a36Sopenharmony_ci *	Piles of crap below pretend to be a parser for module and kernel
313262306a36Sopenharmony_ci *	parameters.  Say "thank you" to whoever had come up with that
313362306a36Sopenharmony_ci *	syntax and keep in mind that code below is a cleaned up version.
313462306a36Sopenharmony_ci */
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_cistatic int __initdata io[PARPORT_PC_MAX_PORTS+1] = {
313762306a36Sopenharmony_ci	[0 ... PARPORT_PC_MAX_PORTS] = 0
313862306a36Sopenharmony_ci};
313962306a36Sopenharmony_cistatic int __initdata io_hi[PARPORT_PC_MAX_PORTS+1] = {
314062306a36Sopenharmony_ci	[0 ... PARPORT_PC_MAX_PORTS] = PARPORT_IOHI_AUTO
314162306a36Sopenharmony_ci};
314262306a36Sopenharmony_cistatic int __initdata dmaval[PARPORT_PC_MAX_PORTS] = {
314362306a36Sopenharmony_ci	[0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE
314462306a36Sopenharmony_ci};
314562306a36Sopenharmony_cistatic int __initdata irqval[PARPORT_PC_MAX_PORTS] = {
314662306a36Sopenharmony_ci	[0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY
314762306a36Sopenharmony_ci};
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_cistatic int __init parport_parse_param(const char *s, int *val,
315062306a36Sopenharmony_ci				int automatic, int none, int nofifo)
315162306a36Sopenharmony_ci{
315262306a36Sopenharmony_ci	if (!s)
315362306a36Sopenharmony_ci		return 0;
315462306a36Sopenharmony_ci	if (!strncmp(s, "auto", 4))
315562306a36Sopenharmony_ci		*val = automatic;
315662306a36Sopenharmony_ci	else if (!strncmp(s, "none", 4))
315762306a36Sopenharmony_ci		*val = none;
315862306a36Sopenharmony_ci	else if (nofifo && !strncmp(s, "nofifo", 6))
315962306a36Sopenharmony_ci		*val = nofifo;
316062306a36Sopenharmony_ci	else {
316162306a36Sopenharmony_ci		char *ep;
316262306a36Sopenharmony_ci		unsigned long r = simple_strtoul(s, &ep, 0);
316362306a36Sopenharmony_ci		if (ep != s)
316462306a36Sopenharmony_ci			*val = r;
316562306a36Sopenharmony_ci		else {
316662306a36Sopenharmony_ci			pr_err("parport: bad specifier `%s'\n", s);
316762306a36Sopenharmony_ci			return -1;
316862306a36Sopenharmony_ci		}
316962306a36Sopenharmony_ci	}
317062306a36Sopenharmony_ci	return 0;
317162306a36Sopenharmony_ci}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_cistatic int __init parport_parse_irq(const char *irqstr, int *val)
317462306a36Sopenharmony_ci{
317562306a36Sopenharmony_ci	return parport_parse_param(irqstr, val, PARPORT_IRQ_AUTO,
317662306a36Sopenharmony_ci				     PARPORT_IRQ_NONE, 0);
317762306a36Sopenharmony_ci}
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_cistatic int __init parport_parse_dma(const char *dmastr, int *val)
318062306a36Sopenharmony_ci{
318162306a36Sopenharmony_ci	return parport_parse_param(dmastr, val, PARPORT_DMA_AUTO,
318262306a36Sopenharmony_ci				     PARPORT_DMA_NONE, PARPORT_DMA_NOFIFO);
318362306a36Sopenharmony_ci}
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci#ifdef CONFIG_PCI
318662306a36Sopenharmony_cistatic int __init parport_init_mode_setup(char *str)
318762306a36Sopenharmony_ci{
318862306a36Sopenharmony_ci	printk(KERN_DEBUG "parport_pc.c: Specified parameter parport_init_mode=%s\n",
318962306a36Sopenharmony_ci	       str);
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	if (!strcmp(str, "spp"))
319262306a36Sopenharmony_ci		parport_init_mode = 1;
319362306a36Sopenharmony_ci	if (!strcmp(str, "ps2"))
319462306a36Sopenharmony_ci		parport_init_mode = 2;
319562306a36Sopenharmony_ci	if (!strcmp(str, "epp"))
319662306a36Sopenharmony_ci		parport_init_mode = 3;
319762306a36Sopenharmony_ci	if (!strcmp(str, "ecp"))
319862306a36Sopenharmony_ci		parport_init_mode = 4;
319962306a36Sopenharmony_ci	if (!strcmp(str, "ecpepp"))
320062306a36Sopenharmony_ci		parport_init_mode = 5;
320162306a36Sopenharmony_ci	return 1;
320262306a36Sopenharmony_ci}
320362306a36Sopenharmony_ci#endif
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci#ifdef MODULE
320662306a36Sopenharmony_cistatic char *irq[PARPORT_PC_MAX_PORTS];
320762306a36Sopenharmony_cistatic char *dma[PARPORT_PC_MAX_PORTS];
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ciMODULE_PARM_DESC(io, "Base I/O address (SPP regs)");
321062306a36Sopenharmony_cimodule_param_hw_array(io, int, ioport, NULL, 0);
321162306a36Sopenharmony_ciMODULE_PARM_DESC(io_hi, "Base I/O address (ECR)");
321262306a36Sopenharmony_cimodule_param_hw_array(io_hi, int, ioport, NULL, 0);
321362306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ line");
321462306a36Sopenharmony_cimodule_param_hw_array(irq, charp, irq, NULL, 0);
321562306a36Sopenharmony_ciMODULE_PARM_DESC(dma, "DMA channel");
321662306a36Sopenharmony_cimodule_param_hw_array(dma, charp, dma, NULL, 0);
321762306a36Sopenharmony_ci#if defined(CONFIG_PARPORT_PC_SUPERIO) || \
321862306a36Sopenharmony_ci       (defined(CONFIG_PARPORT_1284) && defined(CONFIG_PARPORT_PC_FIFO))
321962306a36Sopenharmony_ciMODULE_PARM_DESC(verbose_probing, "Log chit-chat during initialisation");
322062306a36Sopenharmony_cimodule_param(verbose_probing, int, 0644);
322162306a36Sopenharmony_ci#endif
322262306a36Sopenharmony_ci#ifdef CONFIG_PCI
322362306a36Sopenharmony_cistatic char *init_mode;
322462306a36Sopenharmony_ciMODULE_PARM_DESC(init_mode,
322562306a36Sopenharmony_ci	"Initialise mode for VIA VT8231 port (spp, ps2, epp, ecp or ecpepp)");
322662306a36Sopenharmony_cimodule_param(init_mode, charp, 0);
322762306a36Sopenharmony_ci#endif
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_cistatic int __init parse_parport_params(void)
323062306a36Sopenharmony_ci{
323162306a36Sopenharmony_ci	unsigned int i;
323262306a36Sopenharmony_ci	int val;
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci#ifdef CONFIG_PCI
323562306a36Sopenharmony_ci	if (init_mode)
323662306a36Sopenharmony_ci		parport_init_mode_setup(init_mode);
323762306a36Sopenharmony_ci#endif
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_ci	for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++) {
324062306a36Sopenharmony_ci		if (parport_parse_irq(irq[i], &val))
324162306a36Sopenharmony_ci			return 1;
324262306a36Sopenharmony_ci		irqval[i] = val;
324362306a36Sopenharmony_ci		if (parport_parse_dma(dma[i], &val))
324462306a36Sopenharmony_ci			return 1;
324562306a36Sopenharmony_ci		dmaval[i] = val;
324662306a36Sopenharmony_ci	}
324762306a36Sopenharmony_ci	if (!io[0]) {
324862306a36Sopenharmony_ci		/* The user can make us use any IRQs or DMAs we find. */
324962306a36Sopenharmony_ci		if (irq[0] && !parport_parse_irq(irq[0], &val))
325062306a36Sopenharmony_ci			switch (val) {
325162306a36Sopenharmony_ci			case PARPORT_IRQ_NONE:
325262306a36Sopenharmony_ci			case PARPORT_IRQ_AUTO:
325362306a36Sopenharmony_ci				irqval[0] = val;
325462306a36Sopenharmony_ci				break;
325562306a36Sopenharmony_ci			default:
325662306a36Sopenharmony_ci				pr_warn("parport_pc: irq specified without base address.  Use 'io=' to specify one\n");
325762306a36Sopenharmony_ci			}
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci		if (dma[0] && !parport_parse_dma(dma[0], &val))
326062306a36Sopenharmony_ci			switch (val) {
326162306a36Sopenharmony_ci			case PARPORT_DMA_NONE:
326262306a36Sopenharmony_ci			case PARPORT_DMA_AUTO:
326362306a36Sopenharmony_ci				dmaval[0] = val;
326462306a36Sopenharmony_ci				break;
326562306a36Sopenharmony_ci			default:
326662306a36Sopenharmony_ci				pr_warn("parport_pc: dma specified without base address.  Use 'io=' to specify one\n");
326762306a36Sopenharmony_ci			}
326862306a36Sopenharmony_ci	}
326962306a36Sopenharmony_ci	return 0;
327062306a36Sopenharmony_ci}
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci#else
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_cistatic int parport_setup_ptr __initdata;
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci/*
327762306a36Sopenharmony_ci * Acceptable parameters:
327862306a36Sopenharmony_ci *
327962306a36Sopenharmony_ci * parport=0
328062306a36Sopenharmony_ci * parport=auto
328162306a36Sopenharmony_ci * parport=0xBASE[,IRQ[,DMA]]
328262306a36Sopenharmony_ci *
328362306a36Sopenharmony_ci * IRQ/DMA may be numeric or 'auto' or 'none'
328462306a36Sopenharmony_ci */
328562306a36Sopenharmony_cistatic int __init parport_setup(char *str)
328662306a36Sopenharmony_ci{
328762306a36Sopenharmony_ci	char *endptr;
328862306a36Sopenharmony_ci	char *sep;
328962306a36Sopenharmony_ci	int val;
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci	if (!str || !*str || (*str == '0' && !*(str+1))) {
329262306a36Sopenharmony_ci		/* Disable parport if "parport=0" in cmdline */
329362306a36Sopenharmony_ci		io[0] = PARPORT_DISABLE;
329462306a36Sopenharmony_ci		return 1;
329562306a36Sopenharmony_ci	}
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ci	if (!strncmp(str, "auto", 4)) {
329862306a36Sopenharmony_ci		irqval[0] = PARPORT_IRQ_AUTO;
329962306a36Sopenharmony_ci		dmaval[0] = PARPORT_DMA_AUTO;
330062306a36Sopenharmony_ci		return 1;
330162306a36Sopenharmony_ci	}
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	val = simple_strtoul(str, &endptr, 0);
330462306a36Sopenharmony_ci	if (endptr == str) {
330562306a36Sopenharmony_ci		pr_warn("parport=%s not understood\n", str);
330662306a36Sopenharmony_ci		return 1;
330762306a36Sopenharmony_ci	}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	if (parport_setup_ptr == PARPORT_PC_MAX_PORTS) {
331062306a36Sopenharmony_ci		pr_err("parport=%s ignored, too many ports\n", str);
331162306a36Sopenharmony_ci		return 1;
331262306a36Sopenharmony_ci	}
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	io[parport_setup_ptr] = val;
331562306a36Sopenharmony_ci	irqval[parport_setup_ptr] = PARPORT_IRQ_NONE;
331662306a36Sopenharmony_ci	dmaval[parport_setup_ptr] = PARPORT_DMA_NONE;
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci	sep = strchr(str, ',');
331962306a36Sopenharmony_ci	if (sep++) {
332062306a36Sopenharmony_ci		if (parport_parse_irq(sep, &val))
332162306a36Sopenharmony_ci			return 1;
332262306a36Sopenharmony_ci		irqval[parport_setup_ptr] = val;
332362306a36Sopenharmony_ci		sep = strchr(sep, ',');
332462306a36Sopenharmony_ci		if (sep++) {
332562306a36Sopenharmony_ci			if (parport_parse_dma(sep, &val))
332662306a36Sopenharmony_ci				return 1;
332762306a36Sopenharmony_ci			dmaval[parport_setup_ptr] = val;
332862306a36Sopenharmony_ci		}
332962306a36Sopenharmony_ci	}
333062306a36Sopenharmony_ci	parport_setup_ptr++;
333162306a36Sopenharmony_ci	return 1;
333262306a36Sopenharmony_ci}
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_cistatic int __init parse_parport_params(void)
333562306a36Sopenharmony_ci{
333662306a36Sopenharmony_ci	return io[0] == PARPORT_DISABLE;
333762306a36Sopenharmony_ci}
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci__setup("parport=", parport_setup);
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci/*
334262306a36Sopenharmony_ci * Acceptable parameters:
334362306a36Sopenharmony_ci *
334462306a36Sopenharmony_ci * parport_init_mode=[spp|ps2|epp|ecp|ecpepp]
334562306a36Sopenharmony_ci */
334662306a36Sopenharmony_ci#ifdef CONFIG_PCI
334762306a36Sopenharmony_ci__setup("parport_init_mode=", parport_init_mode_setup);
334862306a36Sopenharmony_ci#endif
334962306a36Sopenharmony_ci#endif
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci/* "Parser" ends here */
335262306a36Sopenharmony_ci
335362306a36Sopenharmony_cistatic int __init parport_pc_init(void)
335462306a36Sopenharmony_ci{
335562306a36Sopenharmony_ci	int err;
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci	if (parse_parport_params())
335862306a36Sopenharmony_ci		return -EINVAL;
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	err = platform_driver_register(&parport_pc_platform_driver);
336162306a36Sopenharmony_ci	if (err)
336262306a36Sopenharmony_ci		return err;
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	if (io[0]) {
336562306a36Sopenharmony_ci		int i;
336662306a36Sopenharmony_ci		/* Only probe the ports we were given. */
336762306a36Sopenharmony_ci		user_specified = 1;
336862306a36Sopenharmony_ci		for (i = 0; i < PARPORT_PC_MAX_PORTS; i++) {
336962306a36Sopenharmony_ci			if (!io[i])
337062306a36Sopenharmony_ci				break;
337162306a36Sopenharmony_ci			if (io_hi[i] == PARPORT_IOHI_AUTO)
337262306a36Sopenharmony_ci				io_hi[i] = 0x400 + io[i];
337362306a36Sopenharmony_ci			parport_pc_probe_port(io[i], io_hi[i],
337462306a36Sopenharmony_ci					irqval[i], dmaval[i], NULL, 0);
337562306a36Sopenharmony_ci		}
337662306a36Sopenharmony_ci	} else
337762306a36Sopenharmony_ci		parport_pc_find_ports(irqval[0], dmaval[0]);
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci	return 0;
338062306a36Sopenharmony_ci}
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_cistatic void __exit parport_pc_exit(void)
338362306a36Sopenharmony_ci{
338462306a36Sopenharmony_ci	if (pci_registered_parport)
338562306a36Sopenharmony_ci		pci_unregister_driver(&parport_pc_pci_driver);
338662306a36Sopenharmony_ci	if (pnp_registered_parport)
338762306a36Sopenharmony_ci		pnp_unregister_driver(&parport_pc_pnp_driver);
338862306a36Sopenharmony_ci	platform_driver_unregister(&parport_pc_platform_driver);
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	while (!list_empty(&ports_list)) {
339162306a36Sopenharmony_ci		struct parport_pc_private *priv;
339262306a36Sopenharmony_ci		struct parport *port;
339362306a36Sopenharmony_ci		struct device *dev;
339462306a36Sopenharmony_ci		priv = list_entry(ports_list.next,
339562306a36Sopenharmony_ci				  struct parport_pc_private, list);
339662306a36Sopenharmony_ci		port = priv->port;
339762306a36Sopenharmony_ci		dev = port->dev;
339862306a36Sopenharmony_ci		parport_pc_unregister_port(port);
339962306a36Sopenharmony_ci		if (dev && dev->bus == &platform_bus_type)
340062306a36Sopenharmony_ci			platform_device_unregister(to_platform_device(dev));
340162306a36Sopenharmony_ci	}
340262306a36Sopenharmony_ci}
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ciMODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
340562306a36Sopenharmony_ciMODULE_DESCRIPTION("PC-style parallel port driver");
340662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
340762306a36Sopenharmony_cimodule_init(parport_pc_init)
340862306a36Sopenharmony_cimodule_exit(parport_pc_exit)
3409