mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 13:55:23 +07:00
c8fc074dd3
When the kernel is running in secure boot mode, we lock down the kernel to prevent userspace from modifying the running kernel image. Whilst this includes prohibiting access to things like /dev/mem, it must also prevent access by means of configuring driver modules in such a way as to cause a device to access or modify the kernel image. To this end, annotate module_param* statements that refer to hardware configuration and indicate for future reference what type of parameter they specify. The parameter parser in the core sees this information and can skip such parameters with an error message if the kernel is locked down. The module initialisation then runs as normal, but just sees whatever the default values for those parameters is. Note that we do still need to do the module initialisation because some drivers have viable defaults set in case parameters aren't specified and some drivers support automatic configuration (e.g. PNP or PCI) in addition to manually coded parameters. This patch annotates drivers in drivers/parport/. Suggested-by: Alan Cox <gnomes@lxorguk.ukuu.org.uk> Signed-off-by: David Howells <dhowells@redhat.com> cc: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
3358 lines
86 KiB
C
3358 lines
86 KiB
C
/* Low-level parallel-port routines for 8255-based PC-style hardware.
|
|
*
|
|
* Authors: Phil Blundell <philb@gnu.org>
|
|
* Tim Waugh <tim@cyberelk.demon.co.uk>
|
|
* Jose Renau <renau@acm.org>
|
|
* David Campbell
|
|
* Andrea Arcangeli
|
|
*
|
|
* based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
|
|
*
|
|
* Cleaned up include files - Russell King <linux@arm.uk.linux.org>
|
|
* DMA support - Bert De Jonghe <bert@sophis.be>
|
|
* Many ECP bugs fixed. Fred Barnes & Jamie Lokier, 1999
|
|
* More PCI support now conditional on CONFIG_PCI, 03/2001, Paul G.
|
|
* Various hacks, Fred Barnes, 04/2001
|
|
* Updated probing logic - Adam Belay <ambx1@neo.rr.com>
|
|
*/
|
|
|
|
/* This driver should work with any hardware that is broadly compatible
|
|
* with that in the IBM PC. This applies to the majority of integrated
|
|
* I/O chipsets that are commonly available. The expected register
|
|
* layout is:
|
|
*
|
|
* base+0 data
|
|
* base+1 status
|
|
* base+2 control
|
|
*
|
|
* In addition, there are some optional registers:
|
|
*
|
|
* base+3 EPP address
|
|
* base+4 EPP data
|
|
* base+0x400 ECP config A
|
|
* base+0x401 ECP config B
|
|
* base+0x402 ECP control
|
|
*
|
|
* All registers are 8 bits wide and read/write. If your hardware differs
|
|
* only in register addresses (eg because your registers are on 32-bit
|
|
* word boundaries) then you can alter the constants in parport_pc.h to
|
|
* accommodate this.
|
|
*
|
|
* Note that the ECP registers may not start at offset 0x400 for PCI cards,
|
|
* but rather will start at port->base_hi.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pnp.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/sysctl.h>
|
|
#include <linux/io.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <asm/dma.h>
|
|
|
|
#include <linux/parport.h>
|
|
#include <linux/parport_pc.h>
|
|
#include <linux/via.h>
|
|
#include <asm/parport.h>
|
|
|
|
#define PARPORT_PC_MAX_PORTS PARPORT_MAX
|
|
|
|
#ifdef CONFIG_ISA_DMA_API
|
|
#define HAS_DMA
|
|
#endif
|
|
|
|
/* ECR modes */
|
|
#define ECR_SPP 00
|
|
#define ECR_PS2 01
|
|
#define ECR_PPF 02
|
|
#define ECR_ECP 03
|
|
#define ECR_EPP 04
|
|
#define ECR_VND 05
|
|
#define ECR_TST 06
|
|
#define ECR_CNF 07
|
|
#define ECR_MODE_MASK 0xe0
|
|
#define ECR_WRITE(p, v) frob_econtrol((p), 0xff, (v))
|
|
|
|
#undef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define DPRINTK printk
|
|
#else
|
|
#define DPRINTK(stuff...)
|
|
#endif
|
|
|
|
|
|
#define NR_SUPERIOS 3
|
|
static struct superio_struct { /* For Super-IO chips autodetection */
|
|
int io;
|
|
int irq;
|
|
int dma;
|
|
} superios[NR_SUPERIOS] = { {0,},};
|
|
|
|
static int user_specified;
|
|
#if defined(CONFIG_PARPORT_PC_SUPERIO) || \
|
|
(defined(CONFIG_PARPORT_1284) && defined(CONFIG_PARPORT_PC_FIFO))
|
|
static int verbose_probing;
|
|
#endif
|
|
static int pci_registered_parport;
|
|
static int pnp_registered_parport;
|
|
|
|
/* frob_control, but for ECR */
|
|
static void frob_econtrol(struct parport *pb, unsigned char m,
|
|
unsigned char v)
|
|
{
|
|
unsigned char ectr = 0;
|
|
|
|
if (m != 0xff)
|
|
ectr = inb(ECONTROL(pb));
|
|
|
|
DPRINTK(KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
|
|
m, v, ectr, (ectr & ~m) ^ v);
|
|
|
|
outb((ectr & ~m) ^ v, ECONTROL(pb));
|
|
}
|
|
|
|
static inline void frob_set_mode(struct parport *p, int mode)
|
|
{
|
|
frob_econtrol(p, ECR_MODE_MASK, mode << 5);
|
|
}
|
|
|
|
#ifdef CONFIG_PARPORT_PC_FIFO
|
|
/* Safely change the mode bits in the ECR
|
|
Returns:
|
|
0 : Success
|
|
-EBUSY: Could not drain FIFO in some finite amount of time,
|
|
mode not changed!
|
|
*/
|
|
static int change_mode(struct parport *p, int m)
|
|
{
|
|
const struct parport_pc_private *priv = p->physport->private_data;
|
|
unsigned char oecr;
|
|
int mode;
|
|
|
|
DPRINTK(KERN_INFO "parport change_mode ECP-ISA to mode 0x%02x\n", m);
|
|
|
|
if (!priv->ecr) {
|
|
printk(KERN_DEBUG "change_mode: but there's no ECR!\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Bits <7:5> contain the mode. */
|
|
oecr = inb(ECONTROL(p));
|
|
mode = (oecr >> 5) & 0x7;
|
|
if (mode == m)
|
|
return 0;
|
|
|
|
if (mode >= 2 && !(priv->ctr & 0x20)) {
|
|
/* This mode resets the FIFO, so we may
|
|
* have to wait for it to drain first. */
|
|
unsigned long expire = jiffies + p->physport->cad->timeout;
|
|
int counter;
|
|
switch (mode) {
|
|
case ECR_PPF: /* Parallel Port FIFO mode */
|
|
case ECR_ECP: /* ECP Parallel Port mode */
|
|
/* Busy wait for 200us */
|
|
for (counter = 0; counter < 40; counter++) {
|
|
if (inb(ECONTROL(p)) & 0x01)
|
|
break;
|
|
if (signal_pending(current))
|
|
break;
|
|
udelay(5);
|
|
}
|
|
|
|
/* Poll slowly. */
|
|
while (!(inb(ECONTROL(p)) & 0x01)) {
|
|
if (time_after_eq(jiffies, expire))
|
|
/* The FIFO is stuck. */
|
|
return -EBUSY;
|
|
schedule_timeout_interruptible(
|
|
msecs_to_jiffies(10));
|
|
if (signal_pending(current))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode >= 2 && m >= 2) {
|
|
/* We have to go through mode 001 */
|
|
oecr &= ~(7 << 5);
|
|
oecr |= ECR_PS2 << 5;
|
|
ECR_WRITE(p, oecr);
|
|
}
|
|
|
|
/* Set the mode. */
|
|
oecr &= ~(7 << 5);
|
|
oecr |= m << 5;
|
|
ECR_WRITE(p, oecr);
|
|
return 0;
|
|
}
|
|
#endif /* FIFO support */
|
|
|
|
/*
|
|
* Clear TIMEOUT BIT in EPP MODE
|
|
*
|
|
* This is also used in SPP detection.
|
|
*/
|
|
static int clear_epp_timeout(struct parport *pb)
|
|
{
|
|
unsigned char r;
|
|
|
|
if (!(parport_pc_read_status(pb) & 0x01))
|
|
return 1;
|
|
|
|
/* To clear timeout some chips require double read */
|
|
parport_pc_read_status(pb);
|
|
r = parport_pc_read_status(pb);
|
|
outb(r | 0x01, STATUS(pb)); /* Some reset by writing 1 */
|
|
outb(r & 0xfe, STATUS(pb)); /* Others by writing 0 */
|
|
r = parport_pc_read_status(pb);
|
|
|
|
return !(r & 0x01);
|
|
}
|
|
|
|
/*
|
|
* Access functions.
|
|
*
|
|
* Most of these aren't static because they may be used by the
|
|
* parport_xxx_yyy macros. extern __inline__ versions of several
|
|
* of these are in parport_pc.h.
|
|
*/
|
|
|
|
static void parport_pc_init_state(struct pardevice *dev,
|
|
struct parport_state *s)
|
|
{
|
|
s->u.pc.ctr = 0xc;
|
|
if (dev->irq_func &&
|
|
dev->port->irq != PARPORT_IRQ_NONE)
|
|
/* Set ackIntEn */
|
|
s->u.pc.ctr |= 0x10;
|
|
|
|
s->u.pc.ecr = 0x34; /* NetMos chip can cause problems 0x24;
|
|
* D.Gruszka VScom */
|
|
}
|
|
|
|
static void parport_pc_save_state(struct parport *p, struct parport_state *s)
|
|
{
|
|
const struct parport_pc_private *priv = p->physport->private_data;
|
|
s->u.pc.ctr = priv->ctr;
|
|
if (priv->ecr)
|
|
s->u.pc.ecr = inb(ECONTROL(p));
|
|
}
|
|
|
|
static void parport_pc_restore_state(struct parport *p,
|
|
struct parport_state *s)
|
|
{
|
|
struct parport_pc_private *priv = p->physport->private_data;
|
|
register unsigned char c = s->u.pc.ctr & priv->ctr_writable;
|
|
outb(c, CONTROL(p));
|
|
priv->ctr = c;
|
|
if (priv->ecr)
|
|
ECR_WRITE(p, s->u.pc.ecr);
|
|
}
|
|
|
|
#ifdef CONFIG_PARPORT_1284
|
|
static size_t parport_pc_epp_read_data(struct parport *port, void *buf,
|
|
size_t length, int flags)
|
|
{
|
|
size_t got = 0;
|
|
|
|
if (flags & PARPORT_W91284PIC) {
|
|
unsigned char status;
|
|
size_t left = length;
|
|
|
|
/* use knowledge about data lines..:
|
|
* nFault is 0 if there is at least 1 byte in the Warp's FIFO
|
|
* pError is 1 if there are 16 bytes in the Warp's FIFO
|
|
*/
|
|
status = inb(STATUS(port));
|
|
|
|
while (!(status & 0x08) && got < length) {
|
|
if (left >= 16 && (status & 0x20) && !(status & 0x08)) {
|
|
/* can grab 16 bytes from warp fifo */
|
|
if (!((long)buf & 0x03))
|
|
insl(EPPDATA(port), buf, 4);
|
|
else
|
|
insb(EPPDATA(port), buf, 16);
|
|
buf += 16;
|
|
got += 16;
|
|
left -= 16;
|
|
} else {
|
|
/* grab single byte from the warp fifo */
|
|
*((char *)buf) = inb(EPPDATA(port));
|
|
buf++;
|
|
got++;
|
|
left--;
|
|
}
|
|
status = inb(STATUS(port));
|
|
if (status & 0x01) {
|
|
/* EPP timeout should never occur... */
|
|
printk(KERN_DEBUG
|
|
"%s: EPP timeout occurred while talking to w91284pic (should not have done)\n", port->name);
|
|
clear_epp_timeout(port);
|
|
}
|
|
}
|
|
return got;
|
|
}
|
|
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
|
|
if (!(((long)buf | length) & 0x03))
|
|
insl(EPPDATA(port), buf, (length >> 2));
|
|
else
|
|
insb(EPPDATA(port), buf, length);
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
clear_epp_timeout(port);
|
|
return -EIO;
|
|
}
|
|
return length;
|
|
}
|
|
for (; got < length; got++) {
|
|
*((char *)buf) = inb(EPPDATA(port));
|
|
buf++;
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
/* EPP timeout */
|
|
clear_epp_timeout(port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return got;
|
|
}
|
|
|
|
static size_t parport_pc_epp_write_data(struct parport *port, const void *buf,
|
|
size_t length, int flags)
|
|
{
|
|
size_t written = 0;
|
|
|
|
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
|
|
if (!(((long)buf | length) & 0x03))
|
|
outsl(EPPDATA(port), buf, (length >> 2));
|
|
else
|
|
outsb(EPPDATA(port), buf, length);
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
clear_epp_timeout(port);
|
|
return -EIO;
|
|
}
|
|
return length;
|
|
}
|
|
for (; written < length; written++) {
|
|
outb(*((char *)buf), EPPDATA(port));
|
|
buf++;
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
clear_epp_timeout(port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
static size_t parport_pc_epp_read_addr(struct parport *port, void *buf,
|
|
size_t length, int flags)
|
|
{
|
|
size_t got = 0;
|
|
|
|
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
|
|
insb(EPPADDR(port), buf, length);
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
clear_epp_timeout(port);
|
|
return -EIO;
|
|
}
|
|
return length;
|
|
}
|
|
for (; got < length; got++) {
|
|
*((char *)buf) = inb(EPPADDR(port));
|
|
buf++;
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
clear_epp_timeout(port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return got;
|
|
}
|
|
|
|
static size_t parport_pc_epp_write_addr(struct parport *port,
|
|
const void *buf, size_t length,
|
|
int flags)
|
|
{
|
|
size_t written = 0;
|
|
|
|
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
|
|
outsb(EPPADDR(port), buf, length);
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
clear_epp_timeout(port);
|
|
return -EIO;
|
|
}
|
|
return length;
|
|
}
|
|
for (; written < length; written++) {
|
|
outb(*((char *)buf), EPPADDR(port));
|
|
buf++;
|
|
if (inb(STATUS(port)) & 0x01) {
|
|
clear_epp_timeout(port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
static size_t parport_pc_ecpepp_read_data(struct parport *port, void *buf,
|
|
size_t length, int flags)
|
|
{
|
|
size_t got;
|
|
|
|
frob_set_mode(port, ECR_EPP);
|
|
parport_pc_data_reverse(port);
|
|
parport_pc_write_control(port, 0x4);
|
|
got = parport_pc_epp_read_data(port, buf, length, flags);
|
|
frob_set_mode(port, ECR_PS2);
|
|
|
|
return got;
|
|
}
|
|
|
|
static size_t parport_pc_ecpepp_write_data(struct parport *port,
|
|
const void *buf, size_t length,
|
|
int flags)
|
|
{
|
|
size_t written;
|
|
|
|
frob_set_mode(port, ECR_EPP);
|
|
parport_pc_write_control(port, 0x4);
|
|
parport_pc_data_forward(port);
|
|
written = parport_pc_epp_write_data(port, buf, length, flags);
|
|
frob_set_mode(port, ECR_PS2);
|
|
|
|
return written;
|
|
}
|
|
|
|
static size_t parport_pc_ecpepp_read_addr(struct parport *port, void *buf,
|
|
size_t length, int flags)
|
|
{
|
|
size_t got;
|
|
|
|
frob_set_mode(port, ECR_EPP);
|
|
parport_pc_data_reverse(port);
|
|
parport_pc_write_control(port, 0x4);
|
|
got = parport_pc_epp_read_addr(port, buf, length, flags);
|
|
frob_set_mode(port, ECR_PS2);
|
|
|
|
return got;
|
|
}
|
|
|
|
static size_t parport_pc_ecpepp_write_addr(struct parport *port,
|
|
const void *buf, size_t length,
|
|
int flags)
|
|
{
|
|
size_t written;
|
|
|
|
frob_set_mode(port, ECR_EPP);
|
|
parport_pc_write_control(port, 0x4);
|
|
parport_pc_data_forward(port);
|
|
written = parport_pc_epp_write_addr(port, buf, length, flags);
|
|
frob_set_mode(port, ECR_PS2);
|
|
|
|
return written;
|
|
}
|
|
#endif /* IEEE 1284 support */
|
|
|
|
#ifdef CONFIG_PARPORT_PC_FIFO
|
|
static size_t parport_pc_fifo_write_block_pio(struct parport *port,
|
|
const void *buf, size_t length)
|
|
{
|
|
int ret = 0;
|
|
const unsigned char *bufp = buf;
|
|
size_t left = length;
|
|
unsigned long expire = jiffies + port->physport->cad->timeout;
|
|
const int fifo = FIFO(port);
|
|
int poll_for = 8; /* 80 usecs */
|
|
const struct parport_pc_private *priv = port->physport->private_data;
|
|
const int fifo_depth = priv->fifo_depth;
|
|
|
|
port = port->physport;
|
|
|
|
/* We don't want to be interrupted every character. */
|
|
parport_pc_disable_irq(port);
|
|
/* set nErrIntrEn and serviceIntr */
|
|
frob_econtrol(port, (1<<4) | (1<<2), (1<<4) | (1<<2));
|
|
|
|
/* Forward mode. */
|
|
parport_pc_data_forward(port); /* Must be in PS2 mode */
|
|
|
|
while (left) {
|
|
unsigned char byte;
|
|
unsigned char ecrval = inb(ECONTROL(port));
|
|
int i = 0;
|
|
|
|
if (need_resched() && time_before(jiffies, expire))
|
|
/* Can't yield the port. */
|
|
schedule();
|
|
|
|
/* Anyone else waiting for the port? */
|
|
if (port->waithead) {
|
|
printk(KERN_DEBUG "Somebody wants the port\n");
|
|
break;
|
|
}
|
|
|
|
if (ecrval & 0x02) {
|
|
/* FIFO is full. Wait for interrupt. */
|
|
|
|
/* Clear serviceIntr */
|
|
ECR_WRITE(port, ecrval & ~(1<<2));
|
|
false_alarm:
|
|
ret = parport_wait_event(port, HZ);
|
|
if (ret < 0)
|
|
break;
|
|
ret = 0;
|
|
if (!time_before(jiffies, expire)) {
|
|
/* Timed out. */
|
|
printk(KERN_DEBUG "FIFO write timed out\n");
|
|
break;
|
|
}
|
|
ecrval = inb(ECONTROL(port));
|
|
if (!(ecrval & (1<<2))) {
|
|
if (need_resched() &&
|
|
time_before(jiffies, expire))
|
|
schedule();
|
|
|
|
goto false_alarm;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Can't fail now. */
|
|
expire = jiffies + port->cad->timeout;
|
|
|
|
poll:
|
|
if (signal_pending(current))
|
|
break;
|
|
|
|
if (ecrval & 0x01) {
|
|
/* FIFO is empty. Blast it full. */
|
|
const int n = left < fifo_depth ? left : fifo_depth;
|
|
outsb(fifo, bufp, n);
|
|
bufp += n;
|
|
left -= n;
|
|
|
|
/* Adjust the poll time. */
|
|
if (i < (poll_for - 2))
|
|
poll_for--;
|
|
continue;
|
|
} else if (i++ < poll_for) {
|
|
udelay(10);
|
|
ecrval = inb(ECONTROL(port));
|
|
goto poll;
|
|
}
|
|
|
|
/* Half-full(call me an optimist) */
|
|
byte = *bufp++;
|
|
outb(byte, fifo);
|
|
left--;
|
|
}
|
|
dump_parport_state("leave fifo_write_block_pio", port);
|
|
return length - left;
|
|
}
|
|
|
|
#ifdef HAS_DMA
|
|
static size_t parport_pc_fifo_write_block_dma(struct parport *port,
|
|
const void *buf, size_t length)
|
|
{
|
|
int ret = 0;
|
|
unsigned long dmaflag;
|
|
size_t left = length;
|
|
const struct parport_pc_private *priv = port->physport->private_data;
|
|
struct device *dev = port->physport->dev;
|
|
dma_addr_t dma_addr, dma_handle;
|
|
size_t maxlen = 0x10000; /* max 64k per DMA transfer */
|
|
unsigned long start = (unsigned long) buf;
|
|
unsigned long end = (unsigned long) buf + length - 1;
|
|
|
|
dump_parport_state("enter fifo_write_block_dma", port);
|
|
if (end < MAX_DMA_ADDRESS) {
|
|
/* If it would cross a 64k boundary, cap it at the end. */
|
|
if ((start ^ end) & ~0xffffUL)
|
|
maxlen = 0x10000 - (start & 0xffff);
|
|
|
|
dma_addr = dma_handle = dma_map_single(dev, (void *)buf, length,
|
|
DMA_TO_DEVICE);
|
|
} else {
|
|
/* above 16 MB we use a bounce buffer as ISA-DMA
|
|
is not possible */
|
|
maxlen = PAGE_SIZE; /* sizeof(priv->dma_buf) */
|
|
dma_addr = priv->dma_handle;
|
|
dma_handle = 0;
|
|
}
|
|
|
|
port = port->physport;
|
|
|
|
/* We don't want to be interrupted every character. */
|
|
parport_pc_disable_irq(port);
|
|
/* set nErrIntrEn and serviceIntr */
|
|
frob_econtrol(port, (1<<4) | (1<<2), (1<<4) | (1<<2));
|
|
|
|
/* Forward mode. */
|
|
parport_pc_data_forward(port); /* Must be in PS2 mode */
|
|
|
|
while (left) {
|
|
unsigned long expire = jiffies + port->physport->cad->timeout;
|
|
|
|
size_t count = left;
|
|
|
|
if (count > maxlen)
|
|
count = maxlen;
|
|
|
|
if (!dma_handle) /* bounce buffer ! */
|
|
memcpy(priv->dma_buf, buf, count);
|
|
|
|
dmaflag = claim_dma_lock();
|
|
disable_dma(port->dma);
|
|
clear_dma_ff(port->dma);
|
|
set_dma_mode(port->dma, DMA_MODE_WRITE);
|
|
set_dma_addr(port->dma, dma_addr);
|
|
set_dma_count(port->dma, count);
|
|
|
|
/* Set DMA mode */
|
|
frob_econtrol(port, 1<<3, 1<<3);
|
|
|
|
/* Clear serviceIntr */
|
|
frob_econtrol(port, 1<<2, 0);
|
|
|
|
enable_dma(port->dma);
|
|
release_dma_lock(dmaflag);
|
|
|
|
/* assume DMA will be successful */
|
|
left -= count;
|
|
buf += count;
|
|
if (dma_handle)
|
|
dma_addr += count;
|
|
|
|
/* Wait for interrupt. */
|
|
false_alarm:
|
|
ret = parport_wait_event(port, HZ);
|
|
if (ret < 0)
|
|
break;
|
|
ret = 0;
|
|
if (!time_before(jiffies, expire)) {
|
|
/* Timed out. */
|
|
printk(KERN_DEBUG "DMA write timed out\n");
|
|
break;
|
|
}
|
|
/* Is serviceIntr set? */
|
|
if (!(inb(ECONTROL(port)) & (1<<2))) {
|
|
cond_resched();
|
|
|
|
goto false_alarm;
|
|
}
|
|
|
|
dmaflag = claim_dma_lock();
|
|
disable_dma(port->dma);
|
|
clear_dma_ff(port->dma);
|
|
count = get_dma_residue(port->dma);
|
|
release_dma_lock(dmaflag);
|
|
|
|
cond_resched(); /* Can't yield the port. */
|
|
|
|
/* Anyone else waiting for the port? */
|
|
if (port->waithead) {
|
|
printk(KERN_DEBUG "Somebody wants the port\n");
|
|
break;
|
|
}
|
|
|
|
/* update for possible DMA residue ! */
|
|
buf -= count;
|
|
left += count;
|
|
if (dma_handle)
|
|
dma_addr -= count;
|
|
}
|
|
|
|
/* Maybe got here through break, so adjust for DMA residue! */
|
|
dmaflag = claim_dma_lock();
|
|
disable_dma(port->dma);
|
|
clear_dma_ff(port->dma);
|
|
left += get_dma_residue(port->dma);
|
|
release_dma_lock(dmaflag);
|
|
|
|
/* Turn off DMA mode */
|
|
frob_econtrol(port, 1<<3, 0);
|
|
|
|
if (dma_handle)
|
|
dma_unmap_single(dev, dma_handle, length, DMA_TO_DEVICE);
|
|
|
|
dump_parport_state("leave fifo_write_block_dma", port);
|
|
return length - left;
|
|
}
|
|
#endif
|
|
|
|
static inline size_t parport_pc_fifo_write_block(struct parport *port,
|
|
const void *buf, size_t length)
|
|
{
|
|
#ifdef HAS_DMA
|
|
if (port->dma != PARPORT_DMA_NONE)
|
|
return parport_pc_fifo_write_block_dma(port, buf, length);
|
|
#endif
|
|
return parport_pc_fifo_write_block_pio(port, buf, length);
|
|
}
|
|
|
|
/* Parallel Port FIFO mode (ECP chipsets) */
|
|
static size_t parport_pc_compat_write_block_pio(struct parport *port,
|
|
const void *buf, size_t length,
|
|
int flags)
|
|
{
|
|
size_t written;
|
|
int r;
|
|
unsigned long expire;
|
|
const struct parport_pc_private *priv = port->physport->private_data;
|
|
|
|
/* Special case: a timeout of zero means we cannot call schedule().
|
|
* Also if O_NONBLOCK is set then use the default implementation. */
|
|
if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
|
|
return parport_ieee1284_write_compat(port, buf,
|
|
length, flags);
|
|
|
|
/* Set up parallel port FIFO mode.*/
|
|
parport_pc_data_forward(port); /* Must be in PS2 mode */
|
|
parport_pc_frob_control(port, PARPORT_CONTROL_STROBE, 0);
|
|
r = change_mode(port, ECR_PPF); /* Parallel port FIFO */
|
|
if (r)
|
|
printk(KERN_DEBUG "%s: Warning change_mode ECR_PPF failed\n",
|
|
port->name);
|
|
|
|
port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
|
|
|
/* Write the data to the FIFO. */
|
|
written = parport_pc_fifo_write_block(port, buf, length);
|
|
|
|
/* Finish up. */
|
|
/* For some hardware we don't want to touch the mode until
|
|
* the FIFO is empty, so allow 4 seconds for each position
|
|
* in the fifo.
|
|
*/
|
|
expire = jiffies + (priv->fifo_depth * HZ * 4);
|
|
do {
|
|
/* Wait for the FIFO to empty */
|
|
r = change_mode(port, ECR_PS2);
|
|
if (r != -EBUSY)
|
|
break;
|
|
} while (time_before(jiffies, expire));
|
|
if (r == -EBUSY) {
|
|
|
|
printk(KERN_DEBUG "%s: FIFO is stuck\n", port->name);
|
|
|
|
/* Prevent further data transfer. */
|
|
frob_set_mode(port, ECR_TST);
|
|
|
|
/* Adjust for the contents of the FIFO. */
|
|
for (written -= priv->fifo_depth; ; written++) {
|
|
if (inb(ECONTROL(port)) & 0x2) {
|
|
/* Full up. */
|
|
break;
|
|
}
|
|
outb(0, FIFO(port));
|
|
}
|
|
|
|
/* Reset the FIFO and return to PS2 mode. */
|
|
frob_set_mode(port, ECR_PS2);
|
|
}
|
|
|
|
r = parport_wait_peripheral(port,
|
|
PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY);
|
|
if (r)
|
|
printk(KERN_DEBUG
|
|
"%s: BUSY timeout (%d) in compat_write_block_pio\n",
|
|
port->name, r);
|
|
|
|
port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
|
|
|
return written;
|
|
}
|
|
|
|
/* ECP */
|
|
#ifdef CONFIG_PARPORT_1284
|
|
static size_t parport_pc_ecp_write_block_pio(struct parport *port,
|
|
const void *buf, size_t length,
|
|
int flags)
|
|
{
|
|
size_t written;
|
|
int r;
|
|
unsigned long expire;
|
|
const struct parport_pc_private *priv = port->physport->private_data;
|
|
|
|
/* Special case: a timeout of zero means we cannot call schedule().
|
|
* Also if O_NONBLOCK is set then use the default implementation. */
|
|
if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
|
|
return parport_ieee1284_ecp_write_data(port, buf,
|
|
length, flags);
|
|
|
|
/* Switch to forward mode if necessary. */
|
|
if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
|
|
/* Event 47: Set nInit high. */
|
|
parport_frob_control(port,
|
|
PARPORT_CONTROL_INIT
|
|
| PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_INIT
|
|
| PARPORT_CONTROL_AUTOFD);
|
|
|
|
/* Event 49: PError goes high. */
|
|
r = parport_wait_peripheral(port,
|
|
PARPORT_STATUS_PAPEROUT,
|
|
PARPORT_STATUS_PAPEROUT);
|
|
if (r) {
|
|
printk(KERN_DEBUG "%s: PError timeout (%d) "
|
|
"in ecp_write_block_pio\n", port->name, r);
|
|
}
|
|
}
|
|
|
|
/* Set up ECP parallel port mode.*/
|
|
parport_pc_data_forward(port); /* Must be in PS2 mode */
|
|
parport_pc_frob_control(port,
|
|
PARPORT_CONTROL_STROBE |
|
|
PARPORT_CONTROL_AUTOFD,
|
|
0);
|
|
r = change_mode(port, ECR_ECP); /* ECP FIFO */
|
|
if (r)
|
|
printk(KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n",
|
|
port->name);
|
|
port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
|
|
|
/* Write the data to the FIFO. */
|
|
written = parport_pc_fifo_write_block(port, buf, length);
|
|
|
|
/* Finish up. */
|
|
/* For some hardware we don't want to touch the mode until
|
|
* the FIFO is empty, so allow 4 seconds for each position
|
|
* in the fifo.
|
|
*/
|
|
expire = jiffies + (priv->fifo_depth * (HZ * 4));
|
|
do {
|
|
/* Wait for the FIFO to empty */
|
|
r = change_mode(port, ECR_PS2);
|
|
if (r != -EBUSY)
|
|
break;
|
|
} while (time_before(jiffies, expire));
|
|
if (r == -EBUSY) {
|
|
|
|
printk(KERN_DEBUG "%s: FIFO is stuck\n", port->name);
|
|
|
|
/* Prevent further data transfer. */
|
|
frob_set_mode(port, ECR_TST);
|
|
|
|
/* Adjust for the contents of the FIFO. */
|
|
for (written -= priv->fifo_depth; ; written++) {
|
|
if (inb(ECONTROL(port)) & 0x2) {
|
|
/* Full up. */
|
|
break;
|
|
}
|
|
outb(0, FIFO(port));
|
|
}
|
|
|
|
/* Reset the FIFO and return to PS2 mode. */
|
|
frob_set_mode(port, ECR_PS2);
|
|
|
|
/* Host transfer recovery. */
|
|
parport_pc_data_reverse(port); /* Must be in PS2 mode */
|
|
udelay(5);
|
|
parport_frob_control(port, PARPORT_CONTROL_INIT, 0);
|
|
r = parport_wait_peripheral(port, PARPORT_STATUS_PAPEROUT, 0);
|
|
if (r)
|
|
printk(KERN_DEBUG "%s: PE,1 timeout (%d) "
|
|
"in ecp_write_block_pio\n", port->name, r);
|
|
|
|
parport_frob_control(port,
|
|
PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_INIT);
|
|
r = parport_wait_peripheral(port,
|
|
PARPORT_STATUS_PAPEROUT,
|
|
PARPORT_STATUS_PAPEROUT);
|
|
if (r)
|
|
printk(KERN_DEBUG "%s: PE,2 timeout (%d) "
|
|
"in ecp_write_block_pio\n", port->name, r);
|
|
}
|
|
|
|
r = parport_wait_peripheral(port,
|
|
PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY);
|
|
if (r)
|
|
printk(KERN_DEBUG
|
|
"%s: BUSY timeout (%d) in ecp_write_block_pio\n",
|
|
port->name, r);
|
|
|
|
port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
|
|
|
return written;
|
|
}
|
|
#endif /* IEEE 1284 support */
|
|
#endif /* Allowed to use FIFO/DMA */
|
|
|
|
|
|
/*
|
|
* ******************************************
|
|
* INITIALISATION AND MODULE STUFF BELOW HERE
|
|
* ******************************************
|
|
*/
|
|
|
|
/* GCC is not inlining extern inline function later overwritten to non-inline,
|
|
so we use outlined_ variants here. */
|
|
static const struct parport_operations parport_pc_ops = {
|
|
.write_data = parport_pc_write_data,
|
|
.read_data = parport_pc_read_data,
|
|
|
|
.write_control = parport_pc_write_control,
|
|
.read_control = parport_pc_read_control,
|
|
.frob_control = parport_pc_frob_control,
|
|
|
|
.read_status = parport_pc_read_status,
|
|
|
|
.enable_irq = parport_pc_enable_irq,
|
|
.disable_irq = parport_pc_disable_irq,
|
|
|
|
.data_forward = parport_pc_data_forward,
|
|
.data_reverse = parport_pc_data_reverse,
|
|
|
|
.init_state = parport_pc_init_state,
|
|
.save_state = parport_pc_save_state,
|
|
.restore_state = parport_pc_restore_state,
|
|
|
|
.epp_write_data = parport_ieee1284_epp_write_data,
|
|
.epp_read_data = parport_ieee1284_epp_read_data,
|
|
.epp_write_addr = parport_ieee1284_epp_write_addr,
|
|
.epp_read_addr = parport_ieee1284_epp_read_addr,
|
|
|
|
.ecp_write_data = parport_ieee1284_ecp_write_data,
|
|
.ecp_read_data = parport_ieee1284_ecp_read_data,
|
|
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
|
|
|
|
.compat_write_data = parport_ieee1284_write_compat,
|
|
.nibble_read_data = parport_ieee1284_read_nibble,
|
|
.byte_read_data = parport_ieee1284_read_byte,
|
|
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
#ifdef CONFIG_PARPORT_PC_SUPERIO
|
|
|
|
static struct superio_struct *find_free_superio(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < NR_SUPERIOS; i++)
|
|
if (superios[i].io == 0)
|
|
return &superios[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Super-IO chipset detection, Winbond, SMSC */
|
|
static void show_parconfig_smsc37c669(int io, int key)
|
|
{
|
|
int cr1, cr4, cra, cr23, cr26, cr27;
|
|
struct superio_struct *s;
|
|
|
|
static const char *const modes[] = {
|
|
"SPP and Bidirectional (PS/2)",
|
|
"EPP and SPP",
|
|
"ECP",
|
|
"ECP and EPP" };
|
|
|
|
outb(key, io);
|
|
outb(key, io);
|
|
outb(1, io);
|
|
cr1 = inb(io + 1);
|
|
outb(4, io);
|
|
cr4 = inb(io + 1);
|
|
outb(0x0a, io);
|
|
cra = inb(io + 1);
|
|
outb(0x23, io);
|
|
cr23 = inb(io + 1);
|
|
outb(0x26, io);
|
|
cr26 = inb(io + 1);
|
|
outb(0x27, io);
|
|
cr27 = inb(io + 1);
|
|
outb(0xaa, io);
|
|
|
|
if (verbose_probing) {
|
|
printk(KERN_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",
|
|
cr1, cr4, cra, cr23, cr26, cr27);
|
|
|
|
/* The documentation calls DMA and IRQ-Lines by letters, so
|
|
the board maker can/will wire them
|
|
appropriately/randomly... G=reserved H=IDE-irq, */
|
|
printk(KERN_INFO
|
|
"SMSC LPT Config: io=0x%04x, irq=%c, dma=%c, fifo threshold=%d\n",
|
|
cr23 * 4,
|
|
(cr27 & 0x0f) ? 'A' - 1 + (cr27 & 0x0f) : '-',
|
|
(cr26 & 0x0f) ? 'A' - 1 + (cr26 & 0x0f) : '-',
|
|
cra & 0x0f);
|
|
printk(KERN_INFO "SMSC LPT Config: enabled=%s power=%s\n",
|
|
(cr23 * 4 >= 0x100) ? "yes" : "no",
|
|
(cr1 & 4) ? "yes" : "no");
|
|
printk(KERN_INFO
|
|
"SMSC LPT Config: Port mode=%s, EPP version =%s\n",
|
|
(cr1 & 0x08) ? "Standard mode only (SPP)"
|
|
: modes[cr4 & 0x03],
|
|
(cr4 & 0x40) ? "1.7" : "1.9");
|
|
}
|
|
|
|
/* Heuristics ! BIOS setup for this mainboard device limits
|
|
the choices to standard settings, i.e. io-address and IRQ
|
|
are related, however DMA can be 1 or 3, assume DMA_A=DMA1,
|
|
DMA_C=DMA3 (this is true e.g. for TYAN 1564D Tomcat IV) */
|
|
if (cr23 * 4 >= 0x100) { /* if active */
|
|
s = find_free_superio();
|
|
if (s == NULL)
|
|
printk(KERN_INFO "Super-IO: too many chips!\n");
|
|
else {
|
|
int d;
|
|
switch (cr23 * 4) {
|
|
case 0x3bc:
|
|
s->io = 0x3bc;
|
|
s->irq = 7;
|
|
break;
|
|
case 0x378:
|
|
s->io = 0x378;
|
|
s->irq = 7;
|
|
break;
|
|
case 0x278:
|
|
s->io = 0x278;
|
|
s->irq = 5;
|
|
}
|
|
d = (cr26 & 0x0f);
|
|
if (d == 1 || d == 3)
|
|
s->dma = d;
|
|
else
|
|
s->dma = PARPORT_DMA_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void show_parconfig_winbond(int io, int key)
|
|
{
|
|
int cr30, cr60, cr61, cr70, cr74, crf0;
|
|
struct superio_struct *s;
|
|
static const char *const modes[] = {
|
|
"Standard (SPP) and Bidirectional(PS/2)", /* 0 */
|
|
"EPP-1.9 and SPP",
|
|
"ECP",
|
|
"ECP and EPP-1.9",
|
|
"Standard (SPP)",
|
|
"EPP-1.7 and SPP", /* 5 */
|
|
"undefined!",
|
|
"ECP and EPP-1.7" };
|
|
static char *const irqtypes[] = {
|
|
"pulsed low, high-Z",
|
|
"follows nACK" };
|
|
|
|
/* The registers are called compatible-PnP because the
|
|
register layout is modelled after ISA-PnP, the access
|
|
method is just another ... */
|
|
outb(key, io);
|
|
outb(key, io);
|
|
outb(0x07, io); /* Register 7: Select Logical Device */
|
|
outb(0x01, io + 1); /* LD1 is Parallel Port */
|
|
outb(0x30, io);
|
|
cr30 = inb(io + 1);
|
|
outb(0x60, io);
|
|
cr60 = inb(io + 1);
|
|
outb(0x61, io);
|
|
cr61 = inb(io + 1);
|
|
outb(0x70, io);
|
|
cr70 = inb(io + 1);
|
|
outb(0x74, io);
|
|
cr74 = inb(io + 1);
|
|
outb(0xf0, io);
|
|
crf0 = inb(io + 1);
|
|
outb(0xaa, io);
|
|
|
|
if (verbose_probing) {
|
|
printk(KERN_INFO
|
|
"Winbond LPT Config: cr_30=%02x 60,61=%02x%02x 70=%02x 74=%02x, f0=%02x\n",
|
|
cr30, cr60, cr61, cr70, cr74, crf0);
|
|
printk(KERN_INFO "Winbond LPT Config: active=%s, io=0x%02x%02x irq=%d, ",
|
|
(cr30 & 0x01) ? "yes" : "no", cr60, cr61, cr70 & 0x0f);
|
|
if ((cr74 & 0x07) > 3)
|
|
printk("dma=none\n");
|
|
else
|
|
printk("dma=%d\n", cr74 & 0x07);
|
|
printk(KERN_INFO
|
|
"Winbond LPT Config: irqtype=%s, ECP fifo threshold=%d\n",
|
|
irqtypes[crf0>>7], (crf0>>3)&0x0f);
|
|
printk(KERN_INFO "Winbond LPT Config: Port mode=%s\n",
|
|
modes[crf0 & 0x07]);
|
|
}
|
|
|
|
if (cr30 & 0x01) { /* the settings can be interrogated later ... */
|
|
s = find_free_superio();
|
|
if (s == NULL)
|
|
printk(KERN_INFO "Super-IO: too many chips!\n");
|
|
else {
|
|
s->io = (cr60 << 8) | cr61;
|
|
s->irq = cr70 & 0x0f;
|
|
s->dma = (((cr74 & 0x07) > 3) ?
|
|
PARPORT_DMA_NONE : (cr74 & 0x07));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
|
|
{
|
|
const char *type = "unknown";
|
|
int id, progif = 2;
|
|
|
|
if (devid == devrev)
|
|
/* simple heuristics, we happened to read some
|
|
non-winbond register */
|
|
return;
|
|
|
|
id = (devid << 8) | devrev;
|
|
|
|
/* Values are from public data sheets pdf files, I can just
|
|
confirm 83977TF is correct :-) */
|
|
if (id == 0x9771)
|
|
type = "83977F/AF";
|
|
else if (id == 0x9773)
|
|
type = "83977TF / SMSC 97w33x/97w34x";
|
|
else if (id == 0x9774)
|
|
type = "83977ATF";
|
|
else if ((id & ~0x0f) == 0x5270)
|
|
type = "83977CTF / SMSC 97w36x";
|
|
else if ((id & ~0x0f) == 0x52f0)
|
|
type = "83977EF / SMSC 97w35x";
|
|
else if ((id & ~0x0f) == 0x5210)
|
|
type = "83627";
|
|
else if ((id & ~0x0f) == 0x6010)
|
|
type = "83697HF";
|
|
else if ((oldid & 0x0f) == 0x0a) {
|
|
type = "83877F";
|
|
progif = 1;
|
|
} else if ((oldid & 0x0f) == 0x0b) {
|
|
type = "83877AF";
|
|
progif = 1;
|
|
} else if ((oldid & 0x0f) == 0x0c) {
|
|
type = "83877TF";
|
|
progif = 1;
|
|
} else if ((oldid & 0x0f) == 0x0d) {
|
|
type = "83877ATF";
|
|
progif = 1;
|
|
} else
|
|
progif = 0;
|
|
|
|
if (verbose_probing)
|
|
printk(KERN_INFO "Winbond chip at EFER=0x%x key=0x%02x "
|
|
"devid=%02x devrev=%02x oldid=%02x type=%s\n",
|
|
efer, key, devid, devrev, oldid, type);
|
|
|
|
if (progif == 2)
|
|
show_parconfig_winbond(efer, key);
|
|
}
|
|
|
|
static void decode_smsc(int efer, int key, int devid, int devrev)
|
|
{
|
|
const char *type = "unknown";
|
|
void (*func)(int io, int key);
|
|
int id;
|
|
|
|
if (devid == devrev)
|
|
/* simple heuristics, we happened to read some
|
|
non-smsc register */
|
|
return;
|
|
|
|
func = NULL;
|
|
id = (devid << 8) | devrev;
|
|
|
|
if (id == 0x0302) {
|
|
type = "37c669";
|
|
func = show_parconfig_smsc37c669;
|
|
} else if (id == 0x6582)
|
|
type = "37c665IR";
|
|
else if (devid == 0x65)
|
|
type = "37c665GT";
|
|
else if (devid == 0x66)
|
|
type = "37c666GT";
|
|
|
|
if (verbose_probing)
|
|
printk(KERN_INFO "SMSC chip at EFER=0x%x "
|
|
"key=0x%02x devid=%02x devrev=%02x type=%s\n",
|
|
efer, key, devid, devrev, type);
|
|
|
|
if (func)
|
|
func(efer, key);
|
|
}
|
|
|
|
|
|
static void winbond_check(int io, int key)
|
|
{
|
|
int origval, devid, devrev, oldid, x_devid, x_devrev, x_oldid;
|
|
|
|
if (!request_region(io, 3, __func__))
|
|
return;
|
|
|
|
origval = inb(io); /* Save original value */
|
|
|
|
/* First probe without key */
|
|
outb(0x20, io);
|
|
x_devid = inb(io + 1);
|
|
outb(0x21, io);
|
|
x_devrev = inb(io + 1);
|
|
outb(0x09, io);
|
|
x_oldid = inb(io + 1);
|
|
|
|
outb(key, io);
|
|
outb(key, io); /* Write Magic Sequence to EFER, extended
|
|
function enable register */
|
|
outb(0x20, io); /* Write EFIR, extended function index register */
|
|
devid = inb(io + 1); /* Read EFDR, extended function data register */
|
|
outb(0x21, io);
|
|
devrev = inb(io + 1);
|
|
outb(0x09, io);
|
|
oldid = inb(io + 1);
|
|
outb(0xaa, io); /* Magic Seal */
|
|
|
|
outb(origval, io); /* in case we poked some entirely different hardware */
|
|
|
|
if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid))
|
|
goto out; /* protection against false positives */
|
|
|
|
decode_winbond(io, key, devid, devrev, oldid);
|
|
out:
|
|
release_region(io, 3);
|
|
}
|
|
|
|
static void winbond_check2(int io, int key)
|
|
{
|
|
int origval[3], devid, devrev, oldid, x_devid, x_devrev, x_oldid;
|
|
|
|
if (!request_region(io, 3, __func__))
|
|
return;
|
|
|
|
origval[0] = inb(io); /* Save original values */
|
|
origval[1] = inb(io + 1);
|
|
origval[2] = inb(io + 2);
|
|
|
|
/* First probe without the key */
|
|
outb(0x20, io + 2);
|
|
x_devid = inb(io + 2);
|
|
outb(0x21, io + 1);
|
|
x_devrev = inb(io + 2);
|
|
outb(0x09, io + 1);
|
|
x_oldid = inb(io + 2);
|
|
|
|
outb(key, io); /* Write Magic Byte to EFER, extended
|
|
function enable register */
|
|
outb(0x20, io + 2); /* Write EFIR, extended function index register */
|
|
devid = inb(io + 2); /* Read EFDR, extended function data register */
|
|
outb(0x21, io + 1);
|
|
devrev = inb(io + 2);
|
|
outb(0x09, io + 1);
|
|
oldid = inb(io + 2);
|
|
outb(0xaa, io); /* Magic Seal */
|
|
|
|
outb(origval[0], io); /* in case we poked some entirely different hardware */
|
|
outb(origval[1], io + 1);
|
|
outb(origval[2], io + 2);
|
|
|
|
if (x_devid == devid && x_devrev == devrev && x_oldid == oldid)
|
|
goto out; /* protection against false positives */
|
|
|
|
decode_winbond(io, key, devid, devrev, oldid);
|
|
out:
|
|
release_region(io, 3);
|
|
}
|
|
|
|
static void smsc_check(int io, int key)
|
|
{
|
|
int origval, id, rev, oldid, oldrev, x_id, x_rev, x_oldid, x_oldrev;
|
|
|
|
if (!request_region(io, 3, __func__))
|
|
return;
|
|
|
|
origval = inb(io); /* Save original value */
|
|
|
|
/* First probe without the key */
|
|
outb(0x0d, io);
|
|
x_oldid = inb(io + 1);
|
|
outb(0x0e, io);
|
|
x_oldrev = inb(io + 1);
|
|
outb(0x20, io);
|
|
x_id = inb(io + 1);
|
|
outb(0x21, io);
|
|
x_rev = inb(io + 1);
|
|
|
|
outb(key, io);
|
|
outb(key, io); /* Write Magic Sequence to EFER, extended
|
|
function enable register */
|
|
outb(0x0d, io); /* Write EFIR, extended function index register */
|
|
oldid = inb(io + 1); /* Read EFDR, extended function data register */
|
|
outb(0x0e, io);
|
|
oldrev = inb(io + 1);
|
|
outb(0x20, io);
|
|
id = inb(io + 1);
|
|
outb(0x21, io);
|
|
rev = inb(io + 1);
|
|
outb(0xaa, io); /* Magic Seal */
|
|
|
|
outb(origval, io); /* in case we poked some entirely different hardware */
|
|
|
|
if (x_id == id && x_oldrev == oldrev &&
|
|
x_oldid == oldid && x_rev == rev)
|
|
goto out; /* protection against false positives */
|
|
|
|
decode_smsc(io, key, oldid, oldrev);
|
|
out:
|
|
release_region(io, 3);
|
|
}
|
|
|
|
|
|
static void detect_and_report_winbond(void)
|
|
{
|
|
if (verbose_probing)
|
|
printk(KERN_DEBUG "Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
|
|
winbond_check(0x3f0, 0x87);
|
|
winbond_check(0x370, 0x87);
|
|
winbond_check(0x2e , 0x87);
|
|
winbond_check(0x4e , 0x87);
|
|
winbond_check(0x3f0, 0x86);
|
|
winbond_check2(0x250, 0x88);
|
|
winbond_check2(0x250, 0x89);
|
|
}
|
|
|
|
static void detect_and_report_smsc(void)
|
|
{
|
|
if (verbose_probing)
|
|
printk(KERN_DEBUG "SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
|
|
smsc_check(0x3f0, 0x55);
|
|
smsc_check(0x370, 0x55);
|
|
smsc_check(0x3f0, 0x44);
|
|
smsc_check(0x370, 0x44);
|
|
}
|
|
|
|
static void detect_and_report_it87(void)
|
|
{
|
|
u16 dev;
|
|
u8 origval, r;
|
|
if (verbose_probing)
|
|
printk(KERN_DEBUG "IT8705 Super-IO detection, now testing port 2E ...\n");
|
|
if (!request_muxed_region(0x2e, 2, __func__))
|
|
return;
|
|
origval = inb(0x2e); /* Save original value */
|
|
outb(0x87, 0x2e);
|
|
outb(0x01, 0x2e);
|
|
outb(0x55, 0x2e);
|
|
outb(0x55, 0x2e);
|
|
outb(0x20, 0x2e);
|
|
dev = inb(0x2f) << 8;
|
|
outb(0x21, 0x2e);
|
|
dev |= inb(0x2f);
|
|
if (dev == 0x8712 || dev == 0x8705 || dev == 0x8715 ||
|
|
dev == 0x8716 || dev == 0x8718 || dev == 0x8726) {
|
|
printk(KERN_INFO "IT%04X SuperIO detected.\n", dev);
|
|
outb(0x07, 0x2E); /* Parallel Port */
|
|
outb(0x03, 0x2F);
|
|
outb(0xF0, 0x2E); /* BOOT 0x80 off */
|
|
r = inb(0x2f);
|
|
outb(0xF0, 0x2E);
|
|
outb(r | 8, 0x2F);
|
|
outb(0x02, 0x2E); /* Lock */
|
|
outb(0x02, 0x2F);
|
|
} else {
|
|
outb(origval, 0x2e); /* Oops, sorry to disturb */
|
|
}
|
|
release_region(0x2e, 2);
|
|
}
|
|
#endif /* CONFIG_PARPORT_PC_SUPERIO */
|
|
|
|
static struct superio_struct *find_superio(struct parport *p)
|
|
{
|
|
int i;
|
|
for (i = 0; i < NR_SUPERIOS; i++)
|
|
if (superios[i].io != p->base)
|
|
return &superios[i];
|
|
return NULL;
|
|
}
|
|
|
|
static int get_superio_dma(struct parport *p)
|
|
{
|
|
struct superio_struct *s = find_superio(p);
|
|
if (s)
|
|
return s->dma;
|
|
return PARPORT_DMA_NONE;
|
|
}
|
|
|
|
static int get_superio_irq(struct parport *p)
|
|
{
|
|
struct superio_struct *s = find_superio(p);
|
|
if (s)
|
|
return s->irq;
|
|
return PARPORT_IRQ_NONE;
|
|
}
|
|
|
|
|
|
/* --- Mode detection ------------------------------------- */
|
|
|
|
/*
|
|
* Checks for port existence, all ports support SPP MODE
|
|
* Returns:
|
|
* 0 : No parallel port at this address
|
|
* PARPORT_MODE_PCSPP : SPP port detected
|
|
* (if the user specified an ioport himself,
|
|
* this shall always be the case!)
|
|
*
|
|
*/
|
|
static int parport_SPP_supported(struct parport *pb)
|
|
{
|
|
unsigned char r, w;
|
|
|
|
/*
|
|
* first clear an eventually pending EPP timeout
|
|
* I (sailer@ife.ee.ethz.ch) have an SMSC chipset
|
|
* that does not even respond to SPP cycles if an EPP
|
|
* timeout is pending
|
|
*/
|
|
clear_epp_timeout(pb);
|
|
|
|
/* Do a simple read-write test to make sure the port exists. */
|
|
w = 0xc;
|
|
outb(w, CONTROL(pb));
|
|
|
|
/* Is there a control register that we can read from? Some
|
|
* ports don't allow reads, so read_control just returns a
|
|
* software copy. Some ports _do_ allow reads, so bypass the
|
|
* software copy here. In addition, some bits aren't
|
|
* writable. */
|
|
r = inb(CONTROL(pb));
|
|
if ((r & 0xf) == w) {
|
|
w = 0xe;
|
|
outb(w, CONTROL(pb));
|
|
r = inb(CONTROL(pb));
|
|
outb(0xc, CONTROL(pb));
|
|
if ((r & 0xf) == w)
|
|
return PARPORT_MODE_PCSPP;
|
|
}
|
|
|
|
if (user_specified)
|
|
/* That didn't work, but the user thinks there's a
|
|
* port here. */
|
|
printk(KERN_INFO "parport 0x%lx (WARNING): CTR: "
|
|
"wrote 0x%02x, read 0x%02x\n", pb->base, w, r);
|
|
|
|
/* Try the data register. The data lines aren't tri-stated at
|
|
* this stage, so we expect back what we wrote. */
|
|
w = 0xaa;
|
|
parport_pc_write_data(pb, w);
|
|
r = parport_pc_read_data(pb);
|
|
if (r == w) {
|
|
w = 0x55;
|
|
parport_pc_write_data(pb, w);
|
|
r = parport_pc_read_data(pb);
|
|
if (r == w)
|
|
return PARPORT_MODE_PCSPP;
|
|
}
|
|
|
|
if (user_specified) {
|
|
/* Didn't work, but the user is convinced this is the
|
|
* place. */
|
|
printk(KERN_INFO "parport 0x%lx (WARNING): DATA: "
|
|
"wrote 0x%02x, read 0x%02x\n", pb->base, w, r);
|
|
printk(KERN_INFO "parport 0x%lx: You gave this address, "
|
|
"but there is probably no parallel port there!\n",
|
|
pb->base);
|
|
}
|
|
|
|
/* It's possible that we can't read the control register or
|
|
* the data register. In that case just believe the user. */
|
|
if (user_specified)
|
|
return PARPORT_MODE_PCSPP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Check for ECR
|
|
*
|
|
* Old style XT ports alias io ports every 0x400, hence accessing ECR
|
|
* on these cards actually accesses the CTR.
|
|
*
|
|
* Modern cards don't do this but reading from ECR will return 0xff
|
|
* regardless of what is written here if the card does NOT support
|
|
* ECP.
|
|
*
|
|
* We first check to see if ECR is the same as CTR. If not, the low
|
|
* two bits of ECR aren't writable, so we check by writing ECR and
|
|
* reading it back to see if it's what we expect.
|
|
*/
|
|
static int parport_ECR_present(struct parport *pb)
|
|
{
|
|
struct parport_pc_private *priv = pb->private_data;
|
|
unsigned char r = 0xc;
|
|
|
|
outb(r, CONTROL(pb));
|
|
if ((inb(ECONTROL(pb)) & 0x3) == (r & 0x3)) {
|
|
outb(r ^ 0x2, CONTROL(pb)); /* Toggle bit 1 */
|
|
|
|
r = inb(CONTROL(pb));
|
|
if ((inb(ECONTROL(pb)) & 0x2) == (r & 0x2))
|
|
goto no_reg; /* Sure that no ECR register exists */
|
|
}
|
|
|
|
if ((inb(ECONTROL(pb)) & 0x3) != 0x1)
|
|
goto no_reg;
|
|
|
|
ECR_WRITE(pb, 0x34);
|
|
if (inb(ECONTROL(pb)) != 0x35)
|
|
goto no_reg;
|
|
|
|
priv->ecr = 1;
|
|
outb(0xc, CONTROL(pb));
|
|
|
|
/* Go to mode 000 */
|
|
frob_set_mode(pb, ECR_SPP);
|
|
|
|
return 1;
|
|
|
|
no_reg:
|
|
outb(0xc, CONTROL(pb));
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PARPORT_1284
|
|
/* Detect PS/2 support.
|
|
*
|
|
* Bit 5 (0x20) sets the PS/2 data direction; setting this high
|
|
* allows us to read data from the data lines. In theory we would get back
|
|
* 0xff but any peripheral attached to the port may drag some or all of the
|
|
* lines down to zero. So if we get back anything that isn't the contents
|
|
* of the data register we deem PS/2 support to be present.
|
|
*
|
|
* Some SPP ports have "half PS/2" ability - you can't turn off the line
|
|
* drivers, but an external peripheral with sufficiently beefy drivers of
|
|
* its own can overpower them and assert its own levels onto the bus, from
|
|
* where they can then be read back as normal. Ports with this property
|
|
* and the right type of device attached are likely to fail the SPP test,
|
|
* (as they will appear to have stuck bits) and so the fact that they might
|
|
* be misdetected here is rather academic.
|
|
*/
|
|
|
|
static int parport_PS2_supported(struct parport *pb)
|
|
{
|
|
int ok = 0;
|
|
|
|
clear_epp_timeout(pb);
|
|
|
|
/* try to tri-state the buffer */
|
|
parport_pc_data_reverse(pb);
|
|
|
|
parport_pc_write_data(pb, 0x55);
|
|
if (parport_pc_read_data(pb) != 0x55)
|
|
ok++;
|
|
|
|
parport_pc_write_data(pb, 0xaa);
|
|
if (parport_pc_read_data(pb) != 0xaa)
|
|
ok++;
|
|
|
|
/* cancel input mode */
|
|
parport_pc_data_forward(pb);
|
|
|
|
if (ok) {
|
|
pb->modes |= PARPORT_MODE_TRISTATE;
|
|
} else {
|
|
struct parport_pc_private *priv = pb->private_data;
|
|
priv->ctr_writable &= ~0x20;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
#ifdef CONFIG_PARPORT_PC_FIFO
|
|
static int parport_ECP_supported(struct parport *pb)
|
|
{
|
|
int i;
|
|
int config, configb;
|
|
int pword;
|
|
struct parport_pc_private *priv = pb->private_data;
|
|
/* Translate ECP intrLine to ISA irq value */
|
|
static const int intrline[] = { 0, 7, 9, 10, 11, 14, 15, 5 };
|
|
|
|
/* If there is no ECR, we have no hope of supporting ECP. */
|
|
if (!priv->ecr)
|
|
return 0;
|
|
|
|
/* Find out FIFO depth */
|
|
ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
|
|
ECR_WRITE(pb, ECR_TST << 5); /* TEST FIFO */
|
|
for (i = 0; i < 1024 && !(inb(ECONTROL(pb)) & 0x02); i++)
|
|
outb(0xaa, FIFO(pb));
|
|
|
|
/*
|
|
* Using LGS chipset it uses ECR register, but
|
|
* it doesn't support ECP or FIFO MODE
|
|
*/
|
|
if (i == 1024) {
|
|
ECR_WRITE(pb, ECR_SPP << 5);
|
|
return 0;
|
|
}
|
|
|
|
priv->fifo_depth = i;
|
|
if (verbose_probing)
|
|
printk(KERN_DEBUG "0x%lx: FIFO is %d bytes\n", pb->base, i);
|
|
|
|
/* Find out writeIntrThreshold */
|
|
frob_econtrol(pb, 1<<2, 1<<2);
|
|
frob_econtrol(pb, 1<<2, 0);
|
|
for (i = 1; i <= priv->fifo_depth; i++) {
|
|
inb(FIFO(pb));
|
|
udelay(50);
|
|
if (inb(ECONTROL(pb)) & (1<<2))
|
|
break;
|
|
}
|
|
|
|
if (i <= priv->fifo_depth) {
|
|
if (verbose_probing)
|
|
printk(KERN_DEBUG "0x%lx: writeIntrThreshold is %d\n",
|
|
pb->base, i);
|
|
} else
|
|
/* Number of bytes we know we can write if we get an
|
|
interrupt. */
|
|
i = 0;
|
|
|
|
priv->writeIntrThreshold = i;
|
|
|
|
/* Find out readIntrThreshold */
|
|
frob_set_mode(pb, ECR_PS2); /* Reset FIFO and enable PS2 */
|
|
parport_pc_data_reverse(pb); /* Must be in PS2 mode */
|
|
frob_set_mode(pb, ECR_TST); /* Test FIFO */
|
|
frob_econtrol(pb, 1<<2, 1<<2);
|
|
frob_econtrol(pb, 1<<2, 0);
|
|
for (i = 1; i <= priv->fifo_depth; i++) {
|
|
outb(0xaa, FIFO(pb));
|
|
if (inb(ECONTROL(pb)) & (1<<2))
|
|
break;
|
|
}
|
|
|
|
if (i <= priv->fifo_depth) {
|
|
if (verbose_probing)
|
|
printk(KERN_INFO "0x%lx: readIntrThreshold is %d\n",
|
|
pb->base, i);
|
|
} else
|
|
/* Number of bytes we can read if we get an interrupt. */
|
|
i = 0;
|
|
|
|
priv->readIntrThreshold = i;
|
|
|
|
ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
|
|
ECR_WRITE(pb, 0xf4); /* Configuration mode */
|
|
config = inb(CONFIGA(pb));
|
|
pword = (config >> 4) & 0x7;
|
|
switch (pword) {
|
|
case 0:
|
|
pword = 2;
|
|
printk(KERN_WARNING "0x%lx: Unsupported pword size!\n",
|
|
pb->base);
|
|
break;
|
|
case 2:
|
|
pword = 4;
|
|
printk(KERN_WARNING "0x%lx: Unsupported pword size!\n",
|
|
pb->base);
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING "0x%lx: Unknown implementation ID\n",
|
|
pb->base);
|
|
/* Assume 1 */
|
|
case 1:
|
|
pword = 1;
|
|
}
|
|
priv->pword = pword;
|
|
|
|
if (verbose_probing) {
|
|
printk(KERN_DEBUG "0x%lx: PWord is %d bits\n",
|
|
pb->base, 8 * pword);
|
|
|
|
printk(KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n", pb->base,
|
|
config & 0x80 ? "Level" : "Pulses");
|
|
|
|
configb = inb(CONFIGB(pb));
|
|
printk(KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
|
|
pb->base, config, configb);
|
|
printk(KERN_DEBUG "0x%lx: ECP settings irq=", pb->base);
|
|
if ((configb >> 3) & 0x07)
|
|
printk("%d", intrline[(configb >> 3) & 0x07]);
|
|
else
|
|
printk("<none or set by other means>");
|
|
printk(" dma=");
|
|
if ((configb & 0x03) == 0x00)
|
|
printk("<none or set by other means>\n");
|
|
else
|
|
printk("%d\n", configb & 0x07);
|
|
}
|
|
|
|
/* Go back to mode 000 */
|
|
frob_set_mode(pb, ECR_SPP);
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_X86_32
|
|
static int intel_bug_present_check_epp(struct parport *pb)
|
|
{
|
|
const struct parport_pc_private *priv = pb->private_data;
|
|
int bug_present = 0;
|
|
|
|
if (priv->ecr) {
|
|
/* store value of ECR */
|
|
unsigned char ecr = inb(ECONTROL(pb));
|
|
unsigned char i;
|
|
for (i = 0x00; i < 0x80; i += 0x20) {
|
|
ECR_WRITE(pb, i);
|
|
if (clear_epp_timeout(pb)) {
|
|
/* Phony EPP in ECP. */
|
|
bug_present = 1;
|
|
break;
|
|
}
|
|
}
|
|
/* return ECR into the inital state */
|
|
ECR_WRITE(pb, ecr);
|
|
}
|
|
|
|
return bug_present;
|
|
}
|
|
static int intel_bug_present(struct parport *pb)
|
|
{
|
|
/* Check whether the device is legacy, not PCI or PCMCIA. Only legacy is known to be affected. */
|
|
if (pb->dev != NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return intel_bug_present_check_epp(pb);
|
|
}
|
|
#else
|
|
static int intel_bug_present(struct parport *pb)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_X86_32 */
|
|
|
|
static int parport_ECPPS2_supported(struct parport *pb)
|
|
{
|
|
const struct parport_pc_private *priv = pb->private_data;
|
|
int result;
|
|
unsigned char oecr;
|
|
|
|
if (!priv->ecr)
|
|
return 0;
|
|
|
|
oecr = inb(ECONTROL(pb));
|
|
ECR_WRITE(pb, ECR_PS2 << 5);
|
|
result = parport_PS2_supported(pb);
|
|
ECR_WRITE(pb, oecr);
|
|
return result;
|
|
}
|
|
|
|
/* EPP mode detection */
|
|
|
|
static int parport_EPP_supported(struct parport *pb)
|
|
{
|
|
/*
|
|
* Theory:
|
|
* Bit 0 of STR is the EPP timeout bit, this bit is 0
|
|
* when EPP is possible and is set high when an EPP timeout
|
|
* occurs (EPP uses the HALT line to stop the CPU while it does
|
|
* the byte transfer, an EPP timeout occurs if the attached
|
|
* device fails to respond after 10 micro seconds).
|
|
*
|
|
* This bit is cleared by either reading it (National Semi)
|
|
* or writing a 1 to the bit (SMC, UMC, WinBond), others ???
|
|
* This bit is always high in non EPP modes.
|
|
*/
|
|
|
|
/* If EPP timeout bit clear then EPP available */
|
|
if (!clear_epp_timeout(pb))
|
|
return 0; /* No way to clear timeout */
|
|
|
|
/* Check for Intel bug. */
|
|
if (intel_bug_present(pb))
|
|
return 0;
|
|
|
|
pb->modes |= PARPORT_MODE_EPP;
|
|
|
|
/* Set up access functions to use EPP hardware. */
|
|
pb->ops->epp_read_data = parport_pc_epp_read_data;
|
|
pb->ops->epp_write_data = parport_pc_epp_write_data;
|
|
pb->ops->epp_read_addr = parport_pc_epp_read_addr;
|
|
pb->ops->epp_write_addr = parport_pc_epp_write_addr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int parport_ECPEPP_supported(struct parport *pb)
|
|
{
|
|
struct parport_pc_private *priv = pb->private_data;
|
|
int result;
|
|
unsigned char oecr;
|
|
|
|
if (!priv->ecr)
|
|
return 0;
|
|
|
|
oecr = inb(ECONTROL(pb));
|
|
/* Search for SMC style EPP+ECP mode */
|
|
ECR_WRITE(pb, 0x80);
|
|
outb(0x04, CONTROL(pb));
|
|
result = parport_EPP_supported(pb);
|
|
|
|
ECR_WRITE(pb, oecr);
|
|
|
|
if (result) {
|
|
/* Set up access functions to use ECP+EPP hardware. */
|
|
pb->ops->epp_read_data = parport_pc_ecpepp_read_data;
|
|
pb->ops->epp_write_data = parport_pc_ecpepp_write_data;
|
|
pb->ops->epp_read_addr = parport_pc_ecpepp_read_addr;
|
|
pb->ops->epp_write_addr = parport_pc_ecpepp_write_addr;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#else /* No IEEE 1284 support */
|
|
|
|
/* Don't bother probing for modes we know we won't use. */
|
|
static int parport_PS2_supported(struct parport *pb) { return 0; }
|
|
#ifdef CONFIG_PARPORT_PC_FIFO
|
|
static int parport_ECP_supported(struct parport *pb)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
static int parport_EPP_supported(struct parport *pb)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int parport_ECPEPP_supported(struct parport *pb)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int parport_ECPPS2_supported(struct parport *pb)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif /* No IEEE 1284 support */
|
|
|
|
/* --- IRQ detection -------------------------------------- */
|
|
|
|
/* Only if supports ECP mode */
|
|
static int programmable_irq_support(struct parport *pb)
|
|
{
|
|
int irq, intrLine;
|
|
unsigned char oecr = inb(ECONTROL(pb));
|
|
static const int lookup[8] = {
|
|
PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
|
|
};
|
|
|
|
ECR_WRITE(pb, ECR_CNF << 5); /* Configuration MODE */
|
|
|
|
intrLine = (inb(CONFIGB(pb)) >> 3) & 0x07;
|
|
irq = lookup[intrLine];
|
|
|
|
ECR_WRITE(pb, oecr);
|
|
return irq;
|
|
}
|
|
|
|
static int irq_probe_ECP(struct parport *pb)
|
|
{
|
|
int i;
|
|
unsigned long irqs;
|
|
|
|
irqs = probe_irq_on();
|
|
|
|
ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
|
|
ECR_WRITE(pb, (ECR_TST << 5) | 0x04);
|
|
ECR_WRITE(pb, ECR_TST << 5);
|
|
|
|
/* If Full FIFO sure that writeIntrThreshold is generated */
|
|
for (i = 0; i < 1024 && !(inb(ECONTROL(pb)) & 0x02) ; i++)
|
|
outb(0xaa, FIFO(pb));
|
|
|
|
pb->irq = probe_irq_off(irqs);
|
|
ECR_WRITE(pb, ECR_SPP << 5);
|
|
|
|
if (pb->irq <= 0)
|
|
pb->irq = PARPORT_IRQ_NONE;
|
|
|
|
return pb->irq;
|
|
}
|
|
|
|
/*
|
|
* This detection seems that only works in National Semiconductors
|
|
* This doesn't work in SMC, LGS, and Winbond
|
|
*/
|
|
static int irq_probe_EPP(struct parport *pb)
|
|
{
|
|
#ifndef ADVANCED_DETECT
|
|
return PARPORT_IRQ_NONE;
|
|
#else
|
|
int irqs;
|
|
unsigned char oecr;
|
|
|
|
if (pb->modes & PARPORT_MODE_PCECR)
|
|
oecr = inb(ECONTROL(pb));
|
|
|
|
irqs = probe_irq_on();
|
|
|
|
if (pb->modes & PARPORT_MODE_PCECR)
|
|
frob_econtrol(pb, 0x10, 0x10);
|
|
|
|
clear_epp_timeout(pb);
|
|
parport_pc_frob_control(pb, 0x20, 0x20);
|
|
parport_pc_frob_control(pb, 0x10, 0x10);
|
|
clear_epp_timeout(pb);
|
|
|
|
/* Device isn't expecting an EPP read
|
|
* and generates an IRQ.
|
|
*/
|
|
parport_pc_read_epp(pb);
|
|
udelay(20);
|
|
|
|
pb->irq = probe_irq_off(irqs);
|
|
if (pb->modes & PARPORT_MODE_PCECR)
|
|
ECR_WRITE(pb, oecr);
|
|
parport_pc_write_control(pb, 0xc);
|
|
|
|
if (pb->irq <= 0)
|
|
pb->irq = PARPORT_IRQ_NONE;
|
|
|
|
return pb->irq;
|
|
#endif /* Advanced detection */
|
|
}
|
|
|
|
static int irq_probe_SPP(struct parport *pb)
|
|
{
|
|
/* Don't even try to do this. */
|
|
return PARPORT_IRQ_NONE;
|
|
}
|
|
|
|
/* We will attempt to share interrupt requests since other devices
|
|
* such as sound cards and network cards seem to like using the
|
|
* printer IRQs.
|
|
*
|
|
* When ECP is available we can autoprobe for IRQs.
|
|
* NOTE: If we can autoprobe it, we can register the IRQ.
|
|
*/
|
|
static int parport_irq_probe(struct parport *pb)
|
|
{
|
|
struct parport_pc_private *priv = pb->private_data;
|
|
|
|
if (priv->ecr) {
|
|
pb->irq = programmable_irq_support(pb);
|
|
|
|
if (pb->irq == PARPORT_IRQ_NONE)
|
|
pb->irq = irq_probe_ECP(pb);
|
|
}
|
|
|
|
if ((pb->irq == PARPORT_IRQ_NONE) && priv->ecr &&
|
|
(pb->modes & PARPORT_MODE_EPP))
|
|
pb->irq = irq_probe_EPP(pb);
|
|
|
|
clear_epp_timeout(pb);
|
|
|
|
if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_EPP))
|
|
pb->irq = irq_probe_EPP(pb);
|
|
|
|
clear_epp_timeout(pb);
|
|
|
|
if (pb->irq == PARPORT_IRQ_NONE)
|
|
pb->irq = irq_probe_SPP(pb);
|
|
|
|
if (pb->irq == PARPORT_IRQ_NONE)
|
|
pb->irq = get_superio_irq(pb);
|
|
|
|
return pb->irq;
|
|
}
|
|
|
|
/* --- DMA detection -------------------------------------- */
|
|
|
|
/* Only if chipset conforms to ECP ISA Interface Standard */
|
|
static int programmable_dma_support(struct parport *p)
|
|
{
|
|
unsigned char oecr = inb(ECONTROL(p));
|
|
int dma;
|
|
|
|
frob_set_mode(p, ECR_CNF);
|
|
|
|
dma = inb(CONFIGB(p)) & 0x07;
|
|
/* 000: Indicates jumpered 8-bit DMA if read-only.
|
|
100: Indicates jumpered 16-bit DMA if read-only. */
|
|
if ((dma & 0x03) == 0)
|
|
dma = PARPORT_DMA_NONE;
|
|
|
|
ECR_WRITE(p, oecr);
|
|
return dma;
|
|
}
|
|
|
|
static int parport_dma_probe(struct parport *p)
|
|
{
|
|
const struct parport_pc_private *priv = p->private_data;
|
|
if (priv->ecr) /* ask ECP chipset first */
|
|
p->dma = programmable_dma_support(p);
|
|
if (p->dma == PARPORT_DMA_NONE) {
|
|
/* ask known Super-IO chips proper, although these
|
|
claim ECP compatible, some don't report their DMA
|
|
conforming to ECP standards */
|
|
p->dma = get_superio_dma(p);
|
|
}
|
|
|
|
return p->dma;
|
|
}
|
|
|
|
/* --- Initialisation code -------------------------------- */
|
|
|
|
static LIST_HEAD(ports_list);
|
|
static DEFINE_SPINLOCK(ports_lock);
|
|
|
|
struct parport *parport_pc_probe_port(unsigned long int base,
|
|
unsigned long int base_hi,
|
|
int irq, int dma,
|
|
struct device *dev,
|
|
int irqflags)
|
|
{
|
|
struct parport_pc_private *priv;
|
|
struct parport_operations *ops;
|
|
struct parport *p;
|
|
int probedirq = PARPORT_IRQ_NONE;
|
|
struct resource *base_res;
|
|
struct resource *ECR_res = NULL;
|
|
struct resource *EPP_res = NULL;
|
|
struct platform_device *pdev = NULL;
|
|
int ret;
|
|
|
|
if (!dev) {
|
|
/* We need a physical device to attach to, but none was
|
|
* provided. Create our own. */
|
|
pdev = platform_device_register_simple("parport_pc",
|
|
base, NULL, 0);
|
|
if (IS_ERR(pdev))
|
|
return NULL;
|
|
dev = &pdev->dev;
|
|
|
|
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(24));
|
|
if (ret) {
|
|
dev_err(dev, "Unable to set coherent dma mask: disabling DMA\n");
|
|
dma = PARPORT_DMA_NONE;
|
|
}
|
|
}
|
|
|
|
ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL);
|
|
if (!ops)
|
|
goto out1;
|
|
|
|
priv = kmalloc(sizeof(struct parport_pc_private), GFP_KERNEL);
|
|
if (!priv)
|
|
goto out2;
|
|
|
|
/* a misnomer, actually - it's allocate and reserve parport number */
|
|
p = parport_register_port(base, irq, dma, ops);
|
|
if (!p)
|
|
goto out3;
|
|
|
|
base_res = request_region(base, 3, p->name);
|
|
if (!base_res)
|
|
goto out4;
|
|
|
|
memcpy(ops, &parport_pc_ops, sizeof(struct parport_operations));
|
|
priv->ctr = 0xc;
|
|
priv->ctr_writable = ~0x10;
|
|
priv->ecr = 0;
|
|
priv->fifo_depth = 0;
|
|
priv->dma_buf = NULL;
|
|
priv->dma_handle = 0;
|
|
INIT_LIST_HEAD(&priv->list);
|
|
priv->port = p;
|
|
|
|
p->dev = dev;
|
|
p->base_hi = base_hi;
|
|
p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT;
|
|
p->private_data = priv;
|
|
|
|
if (base_hi) {
|
|
ECR_res = request_region(base_hi, 3, p->name);
|
|
if (ECR_res)
|
|
parport_ECR_present(p);
|
|
}
|
|
|
|
if (base != 0x3bc) {
|
|
EPP_res = request_region(base+0x3, 5, p->name);
|
|
if (EPP_res)
|
|
if (!parport_EPP_supported(p))
|
|
parport_ECPEPP_supported(p);
|
|
}
|
|
if (!parport_SPP_supported(p))
|
|
/* No port. */
|
|
goto out5;
|
|
if (priv->ecr)
|
|
parport_ECPPS2_supported(p);
|
|
else
|
|
parport_PS2_supported(p);
|
|
|
|
p->size = (p->modes & PARPORT_MODE_EPP) ? 8 : 3;
|
|
|
|
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
|
|
if (p->base_hi && priv->ecr)
|
|
printk(KERN_CONT " (0x%lx)", p->base_hi);
|
|
if (p->irq == PARPORT_IRQ_AUTO) {
|
|
p->irq = PARPORT_IRQ_NONE;
|
|
parport_irq_probe(p);
|
|
} else if (p->irq == PARPORT_IRQ_PROBEONLY) {
|
|
p->irq = PARPORT_IRQ_NONE;
|
|
parport_irq_probe(p);
|
|
probedirq = p->irq;
|
|
p->irq = PARPORT_IRQ_NONE;
|
|
}
|
|
if (p->irq != PARPORT_IRQ_NONE) {
|
|
printk(KERN_CONT ", irq %d", p->irq);
|
|
priv->ctr_writable |= 0x10;
|
|
|
|
if (p->dma == PARPORT_DMA_AUTO) {
|
|
p->dma = PARPORT_DMA_NONE;
|
|
parport_dma_probe(p);
|
|
}
|
|
}
|
|
if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
|
|
is mandatory (see above) */
|
|
p->dma = PARPORT_DMA_NONE;
|
|
|
|
#ifdef CONFIG_PARPORT_PC_FIFO
|
|
if (parport_ECP_supported(p) &&
|
|
p->dma != PARPORT_DMA_NOFIFO &&
|
|
priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) {
|
|
p->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
|
|
p->ops->compat_write_data = parport_pc_compat_write_block_pio;
|
|
#ifdef CONFIG_PARPORT_1284
|
|
p->ops->ecp_write_data = parport_pc_ecp_write_block_pio;
|
|
/* currently broken, but working on it.. (FB) */
|
|
/* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio; */
|
|
#endif /* IEEE 1284 support */
|
|
if (p->dma != PARPORT_DMA_NONE) {
|
|
printk(KERN_CONT ", dma %d", p->dma);
|
|
p->modes |= PARPORT_MODE_DMA;
|
|
} else
|
|
printk(KERN_CONT ", using FIFO");
|
|
} else
|
|
/* We can't use the DMA channel after all. */
|
|
p->dma = PARPORT_DMA_NONE;
|
|
#endif /* Allowed to use FIFO/DMA */
|
|
|
|
printk(KERN_CONT " [");
|
|
|
|
#define printmode(x) \
|
|
{\
|
|
if (p->modes & PARPORT_MODE_##x) {\
|
|
printk(KERN_CONT "%s%s", f ? "," : "", #x);\
|
|
f++;\
|
|
} \
|
|
}
|
|
|
|
{
|
|
int f = 0;
|
|
printmode(PCSPP);
|
|
printmode(TRISTATE);
|
|
printmode(COMPAT)
|
|
printmode(EPP);
|
|
printmode(ECP);
|
|
printmode(DMA);
|
|
}
|
|
#undef printmode
|
|
#ifndef CONFIG_PARPORT_1284
|
|
printk(KERN_CONT "(,...)");
|
|
#endif /* CONFIG_PARPORT_1284 */
|
|
printk(KERN_CONT "]\n");
|
|
if (probedirq != PARPORT_IRQ_NONE)
|
|
printk(KERN_INFO "%s: irq %d detected\n", p->name, probedirq);
|
|
|
|
/* If No ECP release the ports grabbed above. */
|
|
if (ECR_res && (p->modes & PARPORT_MODE_ECP) == 0) {
|
|
release_region(base_hi, 3);
|
|
ECR_res = NULL;
|
|
}
|
|
/* Likewise for EEP ports */
|
|
if (EPP_res && (p->modes & PARPORT_MODE_EPP) == 0) {
|
|
release_region(base+3, 5);
|
|
EPP_res = NULL;
|
|
}
|
|
if (p->irq != PARPORT_IRQ_NONE) {
|
|
if (request_irq(p->irq, parport_irq_handler,
|
|
irqflags, p->name, p)) {
|
|
printk(KERN_WARNING "%s: irq %d in use, "
|
|
"resorting to polled operation\n",
|
|
p->name, p->irq);
|
|
p->irq = PARPORT_IRQ_NONE;
|
|
p->dma = PARPORT_DMA_NONE;
|
|
}
|
|
|
|
#ifdef CONFIG_PARPORT_PC_FIFO
|
|
#ifdef HAS_DMA
|
|
if (p->dma != PARPORT_DMA_NONE) {
|
|
if (request_dma(p->dma, p->name)) {
|
|
printk(KERN_WARNING "%s: dma %d in use, "
|
|
"resorting to PIO operation\n",
|
|
p->name, p->dma);
|
|
p->dma = PARPORT_DMA_NONE;
|
|
} else {
|
|
priv->dma_buf =
|
|
dma_alloc_coherent(dev,
|
|
PAGE_SIZE,
|
|
&priv->dma_handle,
|
|
GFP_KERNEL);
|
|
if (!priv->dma_buf) {
|
|
printk(KERN_WARNING "%s: "
|
|
"cannot get buffer for DMA, "
|
|
"resorting to PIO operation\n",
|
|
p->name);
|
|
free_dma(p->dma);
|
|
p->dma = PARPORT_DMA_NONE;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/* Done probing. Now put the port into a sensible start-up state. */
|
|
if (priv->ecr)
|
|
/*
|
|
* Put the ECP detected port in PS2 mode.
|
|
* Do this also for ports that have ECR but don't do ECP.
|
|
*/
|
|
ECR_WRITE(p, 0x34);
|
|
|
|
parport_pc_write_data(p, 0);
|
|
parport_pc_data_forward(p);
|
|
|
|
/* Now that we've told the sharing engine about the port, and
|
|
found out its characteristics, let the high-level drivers
|
|
know about it. */
|
|
spin_lock(&ports_lock);
|
|
list_add(&priv->list, &ports_list);
|
|
spin_unlock(&ports_lock);
|
|
parport_announce_port(p);
|
|
|
|
return p;
|
|
|
|
out5:
|
|
if (ECR_res)
|
|
release_region(base_hi, 3);
|
|
if (EPP_res)
|
|
release_region(base+0x3, 5);
|
|
release_region(base, 3);
|
|
out4:
|
|
parport_del_port(p);
|
|
out3:
|
|
kfree(priv);
|
|
out2:
|
|
kfree(ops);
|
|
out1:
|
|
if (pdev)
|
|
platform_device_unregister(pdev);
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(parport_pc_probe_port);
|
|
|
|
void parport_pc_unregister_port(struct parport *p)
|
|
{
|
|
struct parport_pc_private *priv = p->private_data;
|
|
struct parport_operations *ops = p->ops;
|
|
|
|
parport_remove_port(p);
|
|
spin_lock(&ports_lock);
|
|
list_del_init(&priv->list);
|
|
spin_unlock(&ports_lock);
|
|
#if defined(CONFIG_PARPORT_PC_FIFO) && defined(HAS_DMA)
|
|
if (p->dma != PARPORT_DMA_NONE)
|
|
free_dma(p->dma);
|
|
#endif
|
|
if (p->irq != PARPORT_IRQ_NONE)
|
|
free_irq(p->irq, p);
|
|
release_region(p->base, 3);
|
|
if (p->size > 3)
|
|
release_region(p->base + 3, p->size - 3);
|
|
if (p->modes & PARPORT_MODE_ECP)
|
|
release_region(p->base_hi, 3);
|
|
#if defined(CONFIG_PARPORT_PC_FIFO) && defined(HAS_DMA)
|
|
if (priv->dma_buf)
|
|
dma_free_coherent(p->physport->dev, PAGE_SIZE,
|
|
priv->dma_buf,
|
|
priv->dma_handle);
|
|
#endif
|
|
kfree(p->private_data);
|
|
parport_del_port(p);
|
|
kfree(ops); /* hope no-one cached it */
|
|
}
|
|
EXPORT_SYMBOL(parport_pc_unregister_port);
|
|
|
|
#ifdef CONFIG_PCI
|
|
|
|
/* ITE support maintained by Rich Liu <richliu@poorman.org> */
|
|
static int sio_ite_8872_probe(struct pci_dev *pdev, int autoirq, int autodma,
|
|
const struct parport_pc_via_data *via)
|
|
{
|
|
short inta_addr[6] = { 0x2A0, 0x2C0, 0x220, 0x240, 0x1E0 };
|
|
u32 ite8872set;
|
|
u32 ite8872_lpt, ite8872_lpthi;
|
|
u8 ite8872_irq, type;
|
|
int irq;
|
|
int i;
|
|
|
|
DPRINTK(KERN_DEBUG "sio_ite_8872_probe()\n");
|
|
|
|
/* make sure which one chip */
|
|
for (i = 0; i < 5; i++) {
|
|
if (request_region(inta_addr[i], 32, "it887x")) {
|
|
int test;
|
|
pci_write_config_dword(pdev, 0x60,
|
|
0xe5000000 | inta_addr[i]);
|
|
pci_write_config_dword(pdev, 0x78,
|
|
0x00000000 | inta_addr[i]);
|
|
test = inb(inta_addr[i]);
|
|
if (test != 0xff)
|
|
break;
|
|
release_region(inta_addr[i], 32);
|
|
}
|
|
}
|
|
if (i >= 5) {
|
|
printk(KERN_INFO "parport_pc: cannot find ITE8872 INTA\n");
|
|
return 0;
|
|
}
|
|
|
|
type = inb(inta_addr[i] + 0x18);
|
|
type &= 0x0f;
|
|
|
|
switch (type) {
|
|
case 0x2:
|
|
printk(KERN_INFO "parport_pc: ITE8871 found (1P)\n");
|
|
ite8872set = 0x64200000;
|
|
break;
|
|
case 0xa:
|
|
printk(KERN_INFO "parport_pc: ITE8875 found (1P)\n");
|
|
ite8872set = 0x64200000;
|
|
break;
|
|
case 0xe:
|
|
printk(KERN_INFO "parport_pc: ITE8872 found (2S1P)\n");
|
|
ite8872set = 0x64e00000;
|
|
break;
|
|
case 0x6:
|
|
printk(KERN_INFO "parport_pc: ITE8873 found (1S)\n");
|
|
release_region(inta_addr[i], 32);
|
|
return 0;
|
|
case 0x8:
|
|
printk(KERN_INFO "parport_pc: ITE8874 found (2S)\n");
|
|
release_region(inta_addr[i], 32);
|
|
return 0;
|
|
default:
|
|
printk(KERN_INFO "parport_pc: unknown ITE887x\n");
|
|
printk(KERN_INFO "parport_pc: please mail 'lspci -nvv' "
|
|
"output to Rich.Liu@ite.com.tw\n");
|
|
release_region(inta_addr[i], 32);
|
|
return 0;
|
|
}
|
|
|
|
pci_read_config_byte(pdev, 0x3c, &ite8872_irq);
|
|
pci_read_config_dword(pdev, 0x1c, &ite8872_lpt);
|
|
ite8872_lpt &= 0x0000ff00;
|
|
pci_read_config_dword(pdev, 0x20, &ite8872_lpthi);
|
|
ite8872_lpthi &= 0x0000ff00;
|
|
pci_write_config_dword(pdev, 0x6c, 0xe3000000 | ite8872_lpt);
|
|
pci_write_config_dword(pdev, 0x70, 0xe3000000 | ite8872_lpthi);
|
|
pci_write_config_dword(pdev, 0x80, (ite8872_lpthi<<16) | ite8872_lpt);
|
|
/* SET SPP&EPP , Parallel Port NO DMA , Enable All Function */
|
|
/* SET Parallel IRQ */
|
|
pci_write_config_dword(pdev, 0x9c,
|
|
ite8872set | (ite8872_irq * 0x11111));
|
|
|
|
DPRINTK(KERN_DEBUG "ITE887x: The IRQ is %d.\n", ite8872_irq);
|
|
DPRINTK(KERN_DEBUG "ITE887x: The PARALLEL I/O port is 0x%x.\n",
|
|
ite8872_lpt);
|
|
DPRINTK(KERN_DEBUG "ITE887x: The PARALLEL I/O porthi is 0x%x.\n",
|
|
ite8872_lpthi);
|
|
|
|
/* Let the user (or defaults) steer us away from interrupts */
|
|
irq = ite8872_irq;
|
|
if (autoirq != PARPORT_IRQ_AUTO)
|
|
irq = PARPORT_IRQ_NONE;
|
|
|
|
/*
|
|
* Release the resource so that parport_pc_probe_port can get it.
|
|
*/
|
|
release_region(inta_addr[i], 32);
|
|
if (parport_pc_probe_port(ite8872_lpt, ite8872_lpthi,
|
|
irq, PARPORT_DMA_NONE, &pdev->dev, 0)) {
|
|
printk(KERN_INFO
|
|
"parport_pc: ITE 8872 parallel port: io=0x%X",
|
|
ite8872_lpt);
|
|
if (irq != PARPORT_IRQ_NONE)
|
|
printk(", irq=%d", irq);
|
|
printk("\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* VIA 8231 support by Pavel Fedin <sonic_amiga@rambler.ru>
|
|
based on VIA 686a support code by Jeff Garzik <jgarzik@pobox.com> */
|
|
static int parport_init_mode;
|
|
|
|
/* Data for two known VIA chips */
|
|
static struct parport_pc_via_data via_686a_data = {
|
|
0x51,
|
|
0x50,
|
|
0x85,
|
|
0x02,
|
|
0xE2,
|
|
0xF0,
|
|
0xE6
|
|
};
|
|
static struct parport_pc_via_data via_8231_data = {
|
|
0x45,
|
|
0x44,
|
|
0x50,
|
|
0x04,
|
|
0xF2,
|
|
0xFA,
|
|
0xF6
|
|
};
|
|
|
|
static int sio_via_probe(struct pci_dev *pdev, int autoirq, int autodma,
|
|
const struct parport_pc_via_data *via)
|
|
{
|
|
u8 tmp, tmp2, siofunc;
|
|
u8 ppcontrol = 0;
|
|
int dma, irq;
|
|
unsigned port1, port2;
|
|
unsigned have_epp = 0;
|
|
|
|
printk(KERN_DEBUG "parport_pc: VIA 686A/8231 detected\n");
|
|
|
|
switch (parport_init_mode) {
|
|
case 1:
|
|
printk(KERN_DEBUG "parport_pc: setting SPP mode\n");
|
|
siofunc = VIA_FUNCTION_PARPORT_SPP;
|
|
break;
|
|
case 2:
|
|
printk(KERN_DEBUG "parport_pc: setting PS/2 mode\n");
|
|
siofunc = VIA_FUNCTION_PARPORT_SPP;
|
|
ppcontrol = VIA_PARPORT_BIDIR;
|
|
break;
|
|
case 3:
|
|
printk(KERN_DEBUG "parport_pc: setting EPP mode\n");
|
|
siofunc = VIA_FUNCTION_PARPORT_EPP;
|
|
ppcontrol = VIA_PARPORT_BIDIR;
|
|
have_epp = 1;
|
|
break;
|
|
case 4:
|
|
printk(KERN_DEBUG "parport_pc: setting ECP mode\n");
|
|
siofunc = VIA_FUNCTION_PARPORT_ECP;
|
|
ppcontrol = VIA_PARPORT_BIDIR;
|
|
break;
|
|
case 5:
|
|
printk(KERN_DEBUG "parport_pc: setting EPP+ECP mode\n");
|
|
siofunc = VIA_FUNCTION_PARPORT_ECP;
|
|
ppcontrol = VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP;
|
|
have_epp = 1;
|
|
break;
|
|
default:
|
|
printk(KERN_DEBUG
|
|
"parport_pc: probing current configuration\n");
|
|
siofunc = VIA_FUNCTION_PROBE;
|
|
break;
|
|
}
|
|
/*
|
|
* unlock super i/o configuration
|
|
*/
|
|
pci_read_config_byte(pdev, via->via_pci_superio_config_reg, &tmp);
|
|
tmp |= via->via_pci_superio_config_data;
|
|
pci_write_config_byte(pdev, via->via_pci_superio_config_reg, tmp);
|
|
|
|
/* Bits 1-0: Parallel Port Mode / Enable */
|
|
outb(via->viacfg_function, VIA_CONFIG_INDEX);
|
|
tmp = inb(VIA_CONFIG_DATA);
|
|
/* Bit 5: EPP+ECP enable; bit 7: PS/2 bidirectional port enable */
|
|
outb(via->viacfg_parport_control, VIA_CONFIG_INDEX);
|
|
tmp2 = inb(VIA_CONFIG_DATA);
|
|
if (siofunc == VIA_FUNCTION_PROBE) {
|
|
siofunc = tmp & VIA_FUNCTION_PARPORT_DISABLE;
|
|
ppcontrol = tmp2;
|
|
} else {
|
|
tmp &= ~VIA_FUNCTION_PARPORT_DISABLE;
|
|
tmp |= siofunc;
|
|
outb(via->viacfg_function, VIA_CONFIG_INDEX);
|
|
outb(tmp, VIA_CONFIG_DATA);
|
|
tmp2 &= ~(VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP);
|
|
tmp2 |= ppcontrol;
|
|
outb(via->viacfg_parport_control, VIA_CONFIG_INDEX);
|
|
outb(tmp2, VIA_CONFIG_DATA);
|
|
}
|
|
|
|
/* Parallel Port I/O Base Address, bits 9-2 */
|
|
outb(via->viacfg_parport_base, VIA_CONFIG_INDEX);
|
|
port1 = inb(VIA_CONFIG_DATA) << 2;
|
|
|
|
printk(KERN_DEBUG "parport_pc: Current parallel port base: 0x%X\n",
|
|
port1);
|
|
if (port1 == 0x3BC && have_epp) {
|
|
outb(via->viacfg_parport_base, VIA_CONFIG_INDEX);
|
|
outb((0x378 >> 2), VIA_CONFIG_DATA);
|
|
printk(KERN_DEBUG
|
|
"parport_pc: Parallel port base changed to 0x378\n");
|
|
port1 = 0x378;
|
|
}
|
|
|
|
/*
|
|
* lock super i/o configuration
|
|
*/
|
|
pci_read_config_byte(pdev, via->via_pci_superio_config_reg, &tmp);
|
|
tmp &= ~via->via_pci_superio_config_data;
|
|
pci_write_config_byte(pdev, via->via_pci_superio_config_reg, tmp);
|
|
|
|
if (siofunc == VIA_FUNCTION_PARPORT_DISABLE) {
|
|
printk(KERN_INFO "parport_pc: VIA parallel port disabled in BIOS\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Bits 7-4: PnP Routing for Parallel Port IRQ */
|
|
pci_read_config_byte(pdev, via->via_pci_parport_irq_reg, &tmp);
|
|
irq = ((tmp & VIA_IRQCONTROL_PARALLEL) >> 4);
|
|
|
|
if (siofunc == VIA_FUNCTION_PARPORT_ECP) {
|
|
/* Bits 3-2: PnP Routing for Parallel Port DMA */
|
|
pci_read_config_byte(pdev, via->via_pci_parport_dma_reg, &tmp);
|
|
dma = ((tmp & VIA_DMACONTROL_PARALLEL) >> 2);
|
|
} else
|
|
/* if ECP not enabled, DMA is not enabled, assumed
|
|
bogus 'dma' value */
|
|
dma = PARPORT_DMA_NONE;
|
|
|
|
/* Let the user (or defaults) steer us away from interrupts and DMA */
|
|
if (autoirq == PARPORT_IRQ_NONE) {
|
|
irq = PARPORT_IRQ_NONE;
|
|
dma = PARPORT_DMA_NONE;
|
|
}
|
|
if (autodma == PARPORT_DMA_NONE)
|
|
dma = PARPORT_DMA_NONE;
|
|
|
|
switch (port1) {
|
|
case 0x3bc:
|
|
port2 = 0x7bc; break;
|
|
case 0x378:
|
|
port2 = 0x778; break;
|
|
case 0x278:
|
|
port2 = 0x678; break;
|
|
default:
|
|
printk(KERN_INFO
|
|
"parport_pc: Weird VIA parport base 0x%X, ignoring\n",
|
|
port1);
|
|
return 0;
|
|
}
|
|
|
|
/* filter bogus IRQs */
|
|
switch (irq) {
|
|
case 0:
|
|
case 2:
|
|
case 8:
|
|
case 13:
|
|
irq = PARPORT_IRQ_NONE;
|
|
break;
|
|
|
|
default: /* do nothing */
|
|
break;
|
|
}
|
|
|
|
/* finally, do the probe with values obtained */
|
|
if (parport_pc_probe_port(port1, port2, irq, dma, &pdev->dev, 0)) {
|
|
printk(KERN_INFO
|
|
"parport_pc: VIA parallel port: io=0x%X", port1);
|
|
if (irq != PARPORT_IRQ_NONE)
|
|
printk(", irq=%d", irq);
|
|
if (dma != PARPORT_DMA_NONE)
|
|
printk(", dma=%d", dma);
|
|
printk("\n");
|
|
return 1;
|
|
}
|
|
|
|
printk(KERN_WARNING "parport_pc: Strange, can't probe VIA parallel port: io=0x%X, irq=%d, dma=%d\n",
|
|
port1, irq, dma);
|
|
return 0;
|
|
}
|
|
|
|
|
|
enum parport_pc_sio_types {
|
|
sio_via_686a = 0, /* Via VT82C686A motherboard Super I/O */
|
|
sio_via_8231, /* Via VT8231 south bridge integrated Super IO */
|
|
sio_ite_8872,
|
|
last_sio
|
|
};
|
|
|
|
/* each element directly indexed from enum list, above */
|
|
static struct parport_pc_superio {
|
|
int (*probe) (struct pci_dev *pdev, int autoirq, int autodma,
|
|
const struct parport_pc_via_data *via);
|
|
const struct parport_pc_via_data *via;
|
|
} parport_pc_superio_info[] = {
|
|
{ sio_via_probe, &via_686a_data, },
|
|
{ sio_via_probe, &via_8231_data, },
|
|
{ sio_ite_8872_probe, NULL, },
|
|
};
|
|
|
|
enum parport_pc_pci_cards {
|
|
siig_1p_10x = last_sio,
|
|
siig_2p_10x,
|
|
siig_1p_20x,
|
|
siig_2p_20x,
|
|
lava_parallel,
|
|
lava_parallel_dual_a,
|
|
lava_parallel_dual_b,
|
|
boca_ioppar,
|
|
plx_9050,
|
|
timedia_4006a,
|
|
timedia_4014,
|
|
timedia_4008a,
|
|
timedia_4018,
|
|
timedia_9018a,
|
|
syba_2p_epp,
|
|
syba_1p_ecp,
|
|
titan_010l,
|
|
avlab_1p,
|
|
avlab_2p,
|
|
oxsemi_952,
|
|
oxsemi_954,
|
|
oxsemi_840,
|
|
oxsemi_pcie_pport,
|
|
aks_0100,
|
|
mobility_pp,
|
|
netmos_9705,
|
|
netmos_9715,
|
|
netmos_9755,
|
|
netmos_9805,
|
|
netmos_9815,
|
|
netmos_9901,
|
|
netmos_9865,
|
|
quatech_sppxp100,
|
|
};
|
|
|
|
|
|
/* each element directly indexed from enum list, above
|
|
* (but offset by last_sio) */
|
|
static struct parport_pc_pci {
|
|
int numports;
|
|
struct { /* BAR (base address registers) numbers in the config
|
|
space header */
|
|
int lo;
|
|
int hi;
|
|
/* -1 if not there, >6 for offset-method (max BAR is 6) */
|
|
} addr[4];
|
|
|
|
/* If set, this is called immediately after pci_enable_device.
|
|
* If it returns non-zero, no probing will take place and the
|
|
* ports will not be used. */
|
|
int (*preinit_hook) (struct pci_dev *pdev, int autoirq, int autodma);
|
|
|
|
/* If set, this is called after probing for ports. If 'failed'
|
|
* is non-zero we couldn't use any of the ports. */
|
|
void (*postinit_hook) (struct pci_dev *pdev, int failed);
|
|
} cards[] = {
|
|
/* siig_1p_10x */ { 1, { { 2, 3 }, } },
|
|
/* siig_2p_10x */ { 2, { { 2, 3 }, { 4, 5 }, } },
|
|
/* siig_1p_20x */ { 1, { { 0, 1 }, } },
|
|
/* siig_2p_20x */ { 2, { { 0, 1 }, { 2, 3 }, } },
|
|
/* lava_parallel */ { 1, { { 0, -1 }, } },
|
|
/* lava_parallel_dual_a */ { 1, { { 0, -1 }, } },
|
|
/* lava_parallel_dual_b */ { 1, { { 0, -1 }, } },
|
|
/* boca_ioppar */ { 1, { { 0, -1 }, } },
|
|
/* plx_9050 */ { 2, { { 4, -1 }, { 5, -1 }, } },
|
|
/* timedia_4006a */ { 1, { { 0, -1 }, } },
|
|
/* timedia_4014 */ { 2, { { 0, -1 }, { 2, -1 }, } },
|
|
/* timedia_4008a */ { 1, { { 0, 1 }, } },
|
|
/* timedia_4018 */ { 2, { { 0, 1 }, { 2, 3 }, } },
|
|
/* timedia_9018a */ { 2, { { 0, 1 }, { 2, 3 }, } },
|
|
/* SYBA uses fixed offsets in
|
|
a 1K io window */
|
|
/* syba_2p_epp AP138B */ { 2, { { 0, 0x078 }, { 0, 0x178 }, } },
|
|
/* syba_1p_ecp W83787 */ { 1, { { 0, 0x078 }, } },
|
|
/* titan_010l */ { 1, { { 3, -1 }, } },
|
|
/* avlab_1p */ { 1, { { 0, 1}, } },
|
|
/* avlab_2p */ { 2, { { 0, 1}, { 2, 3 },} },
|
|
/* The Oxford Semi cards are unusual: 954 doesn't support ECP,
|
|
* and 840 locks up if you write 1 to bit 2! */
|
|
/* oxsemi_952 */ { 1, { { 0, 1 }, } },
|
|
/* oxsemi_954 */ { 1, { { 0, -1 }, } },
|
|
/* oxsemi_840 */ { 1, { { 0, 1 }, } },
|
|
/* oxsemi_pcie_pport */ { 1, { { 0, 1 }, } },
|
|
/* aks_0100 */ { 1, { { 0, -1 }, } },
|
|
/* mobility_pp */ { 1, { { 0, 1 }, } },
|
|
|
|
/* The netmos entries below are untested */
|
|
/* netmos_9705 */ { 1, { { 0, -1 }, } },
|
|
/* netmos_9715 */ { 2, { { 0, 1 }, { 2, 3 },} },
|
|
/* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} },
|
|
/* netmos_9805 */ { 1, { { 0, 1 }, } },
|
|
/* netmos_9815 */ { 2, { { 0, 1 }, { 2, 3 }, } },
|
|
/* netmos_9901 */ { 1, { { 0, -1 }, } },
|
|
/* netmos_9865 */ { 1, { { 0, -1 }, } },
|
|
/* quatech_sppxp100 */ { 1, { { 0, 1 }, } },
|
|
};
|
|
|
|
static const struct pci_device_id parport_pc_pci_tbl[] = {
|
|
/* Super-IO onboard chips */
|
|
{ 0x1106, 0x0686, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_686a },
|
|
{ 0x1106, 0x8231, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_8231 },
|
|
{ PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_ite_8872 },
|
|
|
|
/* PCI cards */
|
|
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_10x },
|
|
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_10x },
|
|
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_20x },
|
|
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_20x },
|
|
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel },
|
|
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_a },
|
|
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_b },
|
|
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, boca_ioppar },
|
|
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
|
|
PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 },
|
|
/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
|
|
{ 0x1409, 0x7268, 0x1409, 0x0101, 0, 0, timedia_4006a },
|
|
{ 0x1409, 0x7268, 0x1409, 0x0102, 0, 0, timedia_4014 },
|
|
{ 0x1409, 0x7268, 0x1409, 0x0103, 0, 0, timedia_4008a },
|
|
{ 0x1409, 0x7268, 0x1409, 0x0104, 0, 0, timedia_4018 },
|
|
{ 0x1409, 0x7268, 0x1409, 0x9018, 0, 0, timedia_9018a },
|
|
{ PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_2P_EPP,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_2p_epp },
|
|
{ PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_1P_ECP,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_1p_ecp },
|
|
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_010L,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_010l },
|
|
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
|
|
/* AFAVLAB_TK9902 */
|
|
{ 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p},
|
|
{ 0x14db, 0x2121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2p},
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_952 },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_954 },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_840 },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
|
|
{ PCI_VENDOR_ID_AKS, PCI_DEVICE_ID_AKS_ALADDINCARD,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 },
|
|
{ 0x14f2, 0x0121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, mobility_pp },
|
|
/* NetMos communication controllers */
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 },
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 },
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 },
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 },
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 },
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
|
|
0xA000, 0x2000, 0, 0, netmos_9901 },
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
|
|
0xA000, 0x1000, 0, 0, netmos_9865 },
|
|
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
|
|
0xA000, 0x2000, 0, 0, netmos_9865 },
|
|
/* Quatech SPPXP-100 Parallel port PCI ExpressCard */
|
|
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
|
|
{ 0, } /* terminate list */
|
|
};
|
|
MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
|
|
|
|
struct pci_parport_data {
|
|
int num;
|
|
struct parport *ports[2];
|
|
};
|
|
|
|
static int parport_pc_pci_probe(struct pci_dev *dev,
|
|
const struct pci_device_id *id)
|
|
{
|
|
int err, count, n, i = id->driver_data;
|
|
struct pci_parport_data *data;
|
|
|
|
if (i < last_sio)
|
|
/* This is an onboard Super-IO and has already been probed */
|
|
return 0;
|
|
|
|
/* This is a PCI card */
|
|
i -= last_sio;
|
|
count = 0;
|
|
err = pci_enable_device(dev);
|
|
if (err)
|
|
return err;
|
|
|
|
data = kmalloc(sizeof(struct pci_parport_data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
if (cards[i].preinit_hook &&
|
|
cards[i].preinit_hook(dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) {
|
|
kfree(data);
|
|
return -ENODEV;
|
|
}
|
|
|
|
for (n = 0; n < cards[i].numports; n++) {
|
|
int lo = cards[i].addr[n].lo;
|
|
int hi = cards[i].addr[n].hi;
|
|
int irq;
|
|
unsigned long io_lo, io_hi;
|
|
io_lo = pci_resource_start(dev, lo);
|
|
io_hi = 0;
|
|
if ((hi >= 0) && (hi <= 6))
|
|
io_hi = pci_resource_start(dev, hi);
|
|
else if (hi > 6)
|
|
io_lo += hi; /* Reinterpret the meaning of
|
|
"hi" as an offset (see SYBA
|
|
def.) */
|
|
/* TODO: test if sharing interrupts works */
|
|
irq = dev->irq;
|
|
if (irq == IRQ_NONE) {
|
|
printk(KERN_DEBUG
|
|
"PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx)\n",
|
|
id->vendor, id->device, io_lo, io_hi);
|
|
irq = PARPORT_IRQ_NONE;
|
|
} else {
|
|
printk(KERN_DEBUG
|
|
"PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx), IRQ %d\n",
|
|
id->vendor, id->device, io_lo, io_hi, irq);
|
|
}
|
|
data->ports[count] =
|
|
parport_pc_probe_port(io_lo, io_hi, irq,
|
|
PARPORT_DMA_NONE, &dev->dev,
|
|
IRQF_SHARED);
|
|
if (data->ports[count])
|
|
count++;
|
|
}
|
|
|
|
data->num = count;
|
|
|
|
if (cards[i].postinit_hook)
|
|
cards[i].postinit_hook(dev, count == 0);
|
|
|
|
if (count) {
|
|
pci_set_drvdata(dev, data);
|
|
return 0;
|
|
}
|
|
|
|
kfree(data);
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
static void parport_pc_pci_remove(struct pci_dev *dev)
|
|
{
|
|
struct pci_parport_data *data = pci_get_drvdata(dev);
|
|
int i;
|
|
|
|
if (data) {
|
|
for (i = data->num - 1; i >= 0; i--)
|
|
parport_pc_unregister_port(data->ports[i]);
|
|
|
|
kfree(data);
|
|
}
|
|
}
|
|
|
|
static struct pci_driver parport_pc_pci_driver = {
|
|
.name = "parport_pc",
|
|
.id_table = parport_pc_pci_tbl,
|
|
.probe = parport_pc_pci_probe,
|
|
.remove = parport_pc_pci_remove,
|
|
};
|
|
|
|
static int __init parport_pc_init_superio(int autoirq, int autodma)
|
|
{
|
|
const struct pci_device_id *id;
|
|
struct pci_dev *pdev = NULL;
|
|
int ret = 0;
|
|
|
|
for_each_pci_dev(pdev) {
|
|
id = pci_match_id(parport_pc_pci_tbl, pdev);
|
|
if (id == NULL || id->driver_data >= last_sio)
|
|
continue;
|
|
|
|
if (parport_pc_superio_info[id->driver_data].probe(
|
|
pdev, autoirq, autodma,
|
|
parport_pc_superio_info[id->driver_data].via)) {
|
|
ret++;
|
|
}
|
|
}
|
|
|
|
return ret; /* number of devices found */
|
|
}
|
|
#else
|
|
static struct pci_driver parport_pc_pci_driver;
|
|
static int __init parport_pc_init_superio(int autoirq, int autodma)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PCI */
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
static const struct pnp_device_id parport_pc_pnp_tbl[] = {
|
|
/* Standard LPT Printer Port */
|
|
{.id = "PNP0400", .driver_data = 0},
|
|
/* ECP Printer Port */
|
|
{.id = "PNP0401", .driver_data = 0},
|
|
{ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pnp, parport_pc_pnp_tbl);
|
|
|
|
static int parport_pc_pnp_probe(struct pnp_dev *dev,
|
|
const struct pnp_device_id *id)
|
|
{
|
|
struct parport *pdata;
|
|
unsigned long io_lo, io_hi;
|
|
int dma, irq;
|
|
|
|
if (pnp_port_valid(dev, 0) &&
|
|
!(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) {
|
|
io_lo = pnp_port_start(dev, 0);
|
|
} else
|
|
return -EINVAL;
|
|
|
|
if (pnp_port_valid(dev, 1) &&
|
|
!(pnp_port_flags(dev, 1) & IORESOURCE_DISABLED)) {
|
|
io_hi = pnp_port_start(dev, 1);
|
|
} else
|
|
io_hi = 0;
|
|
|
|
if (pnp_irq_valid(dev, 0) &&
|
|
!(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED)) {
|
|
irq = pnp_irq(dev, 0);
|
|
} else
|
|
irq = PARPORT_IRQ_NONE;
|
|
|
|
if (pnp_dma_valid(dev, 0) &&
|
|
!(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED)) {
|
|
dma = pnp_dma(dev, 0);
|
|
} else
|
|
dma = PARPORT_DMA_NONE;
|
|
|
|
dev_info(&dev->dev, "reported by %s\n", dev->protocol->name);
|
|
pdata = parport_pc_probe_port(io_lo, io_hi, irq, dma, &dev->dev, 0);
|
|
if (pdata == NULL)
|
|
return -ENODEV;
|
|
|
|
pnp_set_drvdata(dev, pdata);
|
|
return 0;
|
|
}
|
|
|
|
static void parport_pc_pnp_remove(struct pnp_dev *dev)
|
|
{
|
|
struct parport *pdata = (struct parport *)pnp_get_drvdata(dev);
|
|
if (!pdata)
|
|
return;
|
|
|
|
parport_pc_unregister_port(pdata);
|
|
}
|
|
|
|
/* we only need the pnp layer to activate the device, at least for now */
|
|
static struct pnp_driver parport_pc_pnp_driver = {
|
|
.name = "parport_pc",
|
|
.id_table = parport_pc_pnp_tbl,
|
|
.probe = parport_pc_pnp_probe,
|
|
.remove = parport_pc_pnp_remove,
|
|
};
|
|
|
|
#else
|
|
static struct pnp_driver parport_pc_pnp_driver;
|
|
#endif /* CONFIG_PNP */
|
|
|
|
static int parport_pc_platform_probe(struct platform_device *pdev)
|
|
{
|
|
/* Always succeed, the actual probing is done in
|
|
* parport_pc_probe_port(). */
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver parport_pc_platform_driver = {
|
|
.driver = {
|
|
.name = "parport_pc",
|
|
},
|
|
.probe = parport_pc_platform_probe,
|
|
};
|
|
|
|
/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
|
|
static int __attribute__((unused))
|
|
parport_pc_find_isa_ports(int autoirq, int autodma)
|
|
{
|
|
int count = 0;
|
|
|
|
if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL, 0))
|
|
count++;
|
|
if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL, 0))
|
|
count++;
|
|
if (parport_pc_probe_port(0x278, 0x678, autoirq, autodma, NULL, 0))
|
|
count++;
|
|
|
|
return count;
|
|
}
|
|
|
|
/* This function is called by parport_pc_init if the user didn't
|
|
* specify any ports to probe. Its job is to find some ports. Order
|
|
* is important here -- we want ISA ports to be registered first,
|
|
* followed by PCI cards (for least surprise), but before that we want
|
|
* to do chipset-specific tests for some onboard ports that we know
|
|
* about.
|
|
*
|
|
* autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
|
|
* autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
|
|
*/
|
|
static void __init parport_pc_find_ports(int autoirq, int autodma)
|
|
{
|
|
int count = 0, err;
|
|
|
|
#ifdef CONFIG_PARPORT_PC_SUPERIO
|
|
detect_and_report_it87();
|
|
detect_and_report_winbond();
|
|
detect_and_report_smsc();
|
|
#endif
|
|
|
|
/* Onboard SuperIO chipsets that show themselves on the PCI bus. */
|
|
count += parport_pc_init_superio(autoirq, autodma);
|
|
|
|
/* PnP ports, skip detection if SuperIO already found them */
|
|
if (!count) {
|
|
err = pnp_register_driver(&parport_pc_pnp_driver);
|
|
if (!err)
|
|
pnp_registered_parport = 1;
|
|
}
|
|
|
|
/* ISA ports and whatever (see asm/parport.h). */
|
|
parport_pc_find_nonpci_ports(autoirq, autodma);
|
|
|
|
err = pci_register_driver(&parport_pc_pci_driver);
|
|
if (!err)
|
|
pci_registered_parport = 1;
|
|
}
|
|
|
|
/*
|
|
* Piles of crap below pretend to be a parser for module and kernel
|
|
* parameters. Say "thank you" to whoever had come up with that
|
|
* syntax and keep in mind that code below is a cleaned up version.
|
|
*/
|
|
|
|
static int __initdata io[PARPORT_PC_MAX_PORTS+1] = {
|
|
[0 ... PARPORT_PC_MAX_PORTS] = 0
|
|
};
|
|
static int __initdata io_hi[PARPORT_PC_MAX_PORTS+1] = {
|
|
[0 ... PARPORT_PC_MAX_PORTS] = PARPORT_IOHI_AUTO
|
|
};
|
|
static int __initdata dmaval[PARPORT_PC_MAX_PORTS] = {
|
|
[0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE
|
|
};
|
|
static int __initdata irqval[PARPORT_PC_MAX_PORTS] = {
|
|
[0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY
|
|
};
|
|
|
|
static int __init parport_parse_param(const char *s, int *val,
|
|
int automatic, int none, int nofifo)
|
|
{
|
|
if (!s)
|
|
return 0;
|
|
if (!strncmp(s, "auto", 4))
|
|
*val = automatic;
|
|
else if (!strncmp(s, "none", 4))
|
|
*val = none;
|
|
else if (nofifo && !strncmp(s, "nofifo", 6))
|
|
*val = nofifo;
|
|
else {
|
|
char *ep;
|
|
unsigned long r = simple_strtoul(s, &ep, 0);
|
|
if (ep != s)
|
|
*val = r;
|
|
else {
|
|
printk(KERN_ERR "parport: bad specifier `%s'\n", s);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int __init parport_parse_irq(const char *irqstr, int *val)
|
|
{
|
|
return parport_parse_param(irqstr, val, PARPORT_IRQ_AUTO,
|
|
PARPORT_IRQ_NONE, 0);
|
|
}
|
|
|
|
static int __init parport_parse_dma(const char *dmastr, int *val)
|
|
{
|
|
return parport_parse_param(dmastr, val, PARPORT_DMA_AUTO,
|
|
PARPORT_DMA_NONE, PARPORT_DMA_NOFIFO);
|
|
}
|
|
|
|
#ifdef CONFIG_PCI
|
|
static int __init parport_init_mode_setup(char *str)
|
|
{
|
|
printk(KERN_DEBUG
|
|
"parport_pc.c: Specified parameter parport_init_mode=%s\n", str);
|
|
|
|
if (!strcmp(str, "spp"))
|
|
parport_init_mode = 1;
|
|
if (!strcmp(str, "ps2"))
|
|
parport_init_mode = 2;
|
|
if (!strcmp(str, "epp"))
|
|
parport_init_mode = 3;
|
|
if (!strcmp(str, "ecp"))
|
|
parport_init_mode = 4;
|
|
if (!strcmp(str, "ecpepp"))
|
|
parport_init_mode = 5;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MODULE
|
|
static char *irq[PARPORT_PC_MAX_PORTS];
|
|
static char *dma[PARPORT_PC_MAX_PORTS];
|
|
|
|
MODULE_PARM_DESC(io, "Base I/O address (SPP regs)");
|
|
module_param_hw_array(io, int, ioport, NULL, 0);
|
|
MODULE_PARM_DESC(io_hi, "Base I/O address (ECR)");
|
|
module_param_hw_array(io_hi, int, ioport, NULL, 0);
|
|
MODULE_PARM_DESC(irq, "IRQ line");
|
|
module_param_hw_array(irq, charp, irq, NULL, 0);
|
|
MODULE_PARM_DESC(dma, "DMA channel");
|
|
module_param_hw_array(dma, charp, dma, NULL, 0);
|
|
#if defined(CONFIG_PARPORT_PC_SUPERIO) || \
|
|
(defined(CONFIG_PARPORT_1284) && defined(CONFIG_PARPORT_PC_FIFO))
|
|
MODULE_PARM_DESC(verbose_probing, "Log chit-chat during initialisation");
|
|
module_param(verbose_probing, int, 0644);
|
|
#endif
|
|
#ifdef CONFIG_PCI
|
|
static char *init_mode;
|
|
MODULE_PARM_DESC(init_mode,
|
|
"Initialise mode for VIA VT8231 port (spp, ps2, epp, ecp or ecpepp)");
|
|
module_param(init_mode, charp, 0);
|
|
#endif
|
|
|
|
static int __init parse_parport_params(void)
|
|
{
|
|
unsigned int i;
|
|
int val;
|
|
|
|
#ifdef CONFIG_PCI
|
|
if (init_mode)
|
|
parport_init_mode_setup(init_mode);
|
|
#endif
|
|
|
|
for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++) {
|
|
if (parport_parse_irq(irq[i], &val))
|
|
return 1;
|
|
irqval[i] = val;
|
|
if (parport_parse_dma(dma[i], &val))
|
|
return 1;
|
|
dmaval[i] = val;
|
|
}
|
|
if (!io[0]) {
|
|
/* The user can make us use any IRQs or DMAs we find. */
|
|
if (irq[0] && !parport_parse_irq(irq[0], &val))
|
|
switch (val) {
|
|
case PARPORT_IRQ_NONE:
|
|
case PARPORT_IRQ_AUTO:
|
|
irqval[0] = val;
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING
|
|
"parport_pc: irq specified "
|
|
"without base address. Use 'io=' "
|
|
"to specify one\n");
|
|
}
|
|
|
|
if (dma[0] && !parport_parse_dma(dma[0], &val))
|
|
switch (val) {
|
|
case PARPORT_DMA_NONE:
|
|
case PARPORT_DMA_AUTO:
|
|
dmaval[0] = val;
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING
|
|
"parport_pc: dma specified "
|
|
"without base address. Use 'io=' "
|
|
"to specify one\n");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
static int parport_setup_ptr __initdata;
|
|
|
|
/*
|
|
* Acceptable parameters:
|
|
*
|
|
* parport=0
|
|
* parport=auto
|
|
* parport=0xBASE[,IRQ[,DMA]]
|
|
*
|
|
* IRQ/DMA may be numeric or 'auto' or 'none'
|
|
*/
|
|
static int __init parport_setup(char *str)
|
|
{
|
|
char *endptr;
|
|
char *sep;
|
|
int val;
|
|
|
|
if (!str || !*str || (*str == '0' && !*(str+1))) {
|
|
/* Disable parport if "parport=0" in cmdline */
|
|
io[0] = PARPORT_DISABLE;
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(str, "auto", 4)) {
|
|
irqval[0] = PARPORT_IRQ_AUTO;
|
|
dmaval[0] = PARPORT_DMA_AUTO;
|
|
return 1;
|
|
}
|
|
|
|
val = simple_strtoul(str, &endptr, 0);
|
|
if (endptr == str) {
|
|
printk(KERN_WARNING "parport=%s not understood\n", str);
|
|
return 1;
|
|
}
|
|
|
|
if (parport_setup_ptr == PARPORT_PC_MAX_PORTS) {
|
|
printk(KERN_ERR "parport=%s ignored, too many ports\n", str);
|
|
return 1;
|
|
}
|
|
|
|
io[parport_setup_ptr] = val;
|
|
irqval[parport_setup_ptr] = PARPORT_IRQ_NONE;
|
|
dmaval[parport_setup_ptr] = PARPORT_DMA_NONE;
|
|
|
|
sep = strchr(str, ',');
|
|
if (sep++) {
|
|
if (parport_parse_irq(sep, &val))
|
|
return 1;
|
|
irqval[parport_setup_ptr] = val;
|
|
sep = strchr(sep, ',');
|
|
if (sep++) {
|
|
if (parport_parse_dma(sep, &val))
|
|
return 1;
|
|
dmaval[parport_setup_ptr] = val;
|
|
}
|
|
}
|
|
parport_setup_ptr++;
|
|
return 1;
|
|
}
|
|
|
|
static int __init parse_parport_params(void)
|
|
{
|
|
return io[0] == PARPORT_DISABLE;
|
|
}
|
|
|
|
__setup("parport=", parport_setup);
|
|
|
|
/*
|
|
* Acceptable parameters:
|
|
*
|
|
* parport_init_mode=[spp|ps2|epp|ecp|ecpepp]
|
|
*/
|
|
#ifdef CONFIG_PCI
|
|
__setup("parport_init_mode=", parport_init_mode_setup);
|
|
#endif
|
|
#endif
|
|
|
|
/* "Parser" ends here */
|
|
|
|
static int __init parport_pc_init(void)
|
|
{
|
|
int err;
|
|
|
|
if (parse_parport_params())
|
|
return -EINVAL;
|
|
|
|
err = platform_driver_register(&parport_pc_platform_driver);
|
|
if (err)
|
|
return err;
|
|
|
|
if (io[0]) {
|
|
int i;
|
|
/* Only probe the ports we were given. */
|
|
user_specified = 1;
|
|
for (i = 0; i < PARPORT_PC_MAX_PORTS; i++) {
|
|
if (!io[i])
|
|
break;
|
|
if (io_hi[i] == PARPORT_IOHI_AUTO)
|
|
io_hi[i] = 0x400 + io[i];
|
|
parport_pc_probe_port(io[i], io_hi[i],
|
|
irqval[i], dmaval[i], NULL, 0);
|
|
}
|
|
} else
|
|
parport_pc_find_ports(irqval[0], dmaval[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit parport_pc_exit(void)
|
|
{
|
|
if (pci_registered_parport)
|
|
pci_unregister_driver(&parport_pc_pci_driver);
|
|
if (pnp_registered_parport)
|
|
pnp_unregister_driver(&parport_pc_pnp_driver);
|
|
platform_driver_unregister(&parport_pc_platform_driver);
|
|
|
|
while (!list_empty(&ports_list)) {
|
|
struct parport_pc_private *priv;
|
|
struct parport *port;
|
|
struct device *dev;
|
|
priv = list_entry(ports_list.next,
|
|
struct parport_pc_private, list);
|
|
port = priv->port;
|
|
dev = port->dev;
|
|
parport_pc_unregister_port(port);
|
|
if (dev && dev->bus == &platform_bus_type)
|
|
platform_device_unregister(to_platform_device(dev));
|
|
}
|
|
}
|
|
|
|
MODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
|
|
MODULE_DESCRIPTION("PC-style parallel port driver");
|
|
MODULE_LICENSE("GPL");
|
|
module_init(parport_pc_init)
|
|
module_exit(parport_pc_exit)
|