linux-stable/drivers/parport/parport_pc.c

3383 lines
87 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/* 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
#define NR_SUPERIOS 3
static struct superio_struct { /* For Super-IO chips autodetection */
int io;
int irq;
int dma;
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
} 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)
{
const struct parport_pc_private *priv = pb->physport->private_data;
unsigned char ecr_writable = priv->ecr_writable;
unsigned char ectr = 0;
unsigned char new;
if (m != 0xff)
ectr = inb(ECONTROL(pb));
new = (ectr & ~m) ^ v;
if (ecr_writable)
/* All known users of the ECR mask require bit 0 to be set. */
new = (new & ecr_writable) | 1;
pr_debug("frob_econtrol(%02x,%02x): %02x -> %02x\n", m, v, ectr, new);
outb(new, 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;
pr_debug("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 ((length > 1) && ((flags & PARPORT_EPP_FAST_32)
|| flags & PARPORT_EPP_FAST_16
|| flags & PARPORT_EPP_FAST_8)) {
if ((flags & PARPORT_EPP_FAST_32)
&& !(((long)buf | length) & 0x03))
insl(EPPDATA(port), buf, (length >> 2));
else if ((flags & PARPORT_EPP_FAST_16)
&& !(((long)buf | length) & 0x01))
insw(EPPDATA(port), buf, length >> 1);
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 ((length > 1) && ((flags & PARPORT_EPP_FAST_32)
|| flags & PARPORT_EPP_FAST_16
|| flags & PARPORT_EPP_FAST_8)) {
if ((flags & PARPORT_EPP_FAST_32)
&& !(((long)buf | length) & 0x03))
outsl(EPPDATA(port), buf, (length >> 2));
else if ((flags & PARPORT_EPP_FAST_16)
&& !(((long)buf | length) & 0x01))
outsw(EPPDATA(port), buf, length >> 1);
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 unsigned long 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) {
pr_info("SMSC 37c669 LPT Config: cr_1=0x%02x, 4=0x%02x, A=0x%2x, 23=0x%02x, 26=0x%02x, 27=0x%02x\n",
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, */
pr_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);
pr_info("SMSC LPT Config: enabled=%s power=%s\n",
(cr23 * 4 >= 0x100) ? "yes" : "no",
(cr1 & 4) ? "yes" : "no");
pr_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)
pr_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) {
pr_info("Winbond LPT Config: cr_30=%02x 60,61=%02x%02x 70=%02x 74=%02x, f0=%02x\n",
cr30, cr60, cr61, cr70, cr74, crf0);
pr_info("Winbond LPT Config: active=%s, io=0x%02x%02x irq=%d, ",
(cr30 & 0x01) ? "yes" : "no", cr60, cr61, cr70 & 0x0f);
if ((cr74 & 0x07) > 3)
pr_cont("dma=none\n");
else
pr_cont("dma=%d\n", cr74 & 0x07);
pr_info("Winbond LPT Config: irqtype=%s, ECP fifo threshold=%d\n",
irqtypes[crf0 >> 7], (crf0 >> 3) & 0x0f);
pr_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)
pr_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)
pr_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)
pr_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) {
pr_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!)
*
*/
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
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. */
pr_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. */
pr_info("parport 0x%lx (WARNING): DATA: wrote 0x%02x, read 0x%02x\n",
pb->base, w, r);
pr_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.
*/
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
static int parport_ECR_present(struct parport *pb)
{
struct parport_pc_private *priv = pb->private_data;
unsigned char r = 0xc;
if (!priv->ecr_writable) {
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))
/* Sure that no ECR register exists */
goto no_reg;
}
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.
*/
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
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)
pr_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;
pr_warn("0x%lx: Unsupported pword size!\n", pb->base);
break;
case 2:
pword = 4;
pr_warn("0x%lx: Unsupported pword size!\n", pb->base);
break;
default:
pr_warn("0x%lx: Unknown implementation ID\n", pb->base);
fallthrough; /* 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)
pr_cont("%d", intrline[(configb >> 3) & 0x07]);
else
pr_cont("<none or set by other means>");
pr_cont(" dma=");
if ((configb & 0x03) == 0x00)
pr_cont("<none or set by other means>\n");
else
pr_cont("%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 */
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
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 */
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
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;
}
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
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.
*/
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
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);
static struct parport *__parport_pc_probe_port(unsigned long int base,
unsigned long int base_hi,
int irq, int dma,
struct device *dev,
int irqflags,
unsigned int mode_mask,
unsigned char ecr_writable)
{
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->ecr_writable = ecr_writable;
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;
pr_info("%s: PC-style at 0x%lx", p->name, p->base);
if (p->base_hi && priv->ecr)
pr_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) {
pr_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;
if (p->dma != PARPORT_DMA_NONE)
p->modes |= PARPORT_MODE_DMA;
} else
/* We can't use the DMA channel after all. */
p->dma = PARPORT_DMA_NONE;
#endif /* Allowed to use FIFO/DMA */
p->modes &= ~mode_mask;
#ifdef CONFIG_PARPORT_PC_FIFO
if ((p->modes & PARPORT_MODE_COMPAT) != 0)
p->ops->compat_write_data = parport_pc_compat_write_block_pio;
#ifdef CONFIG_PARPORT_1284
if ((p->modes & PARPORT_MODE_ECP) != 0)
p->ops->ecp_write_data = parport_pc_ecp_write_block_pio;
#endif
if ((p->modes & (PARPORT_MODE_ECP | PARPORT_MODE_COMPAT)) != 0) {
if ((p->modes & PARPORT_MODE_DMA) != 0)
pr_cont(", dma %d", p->dma);
else
pr_cont(", using FIFO");
}
#endif /* Allowed to use FIFO/DMA */
pr_cont(" [");
#define printmode(x) \
do { \
if (p->modes & PARPORT_MODE_##x) \
pr_cont("%s%s", f++ ? "," : "", #x); \
} while (0)
{
int f = 0;
printmode(PCSPP);
printmode(TRISTATE);
printmode(COMPAT);
printmode(EPP);
printmode(ECP);
printmode(DMA);
}
#undef printmode
#ifndef CONFIG_PARPORT_1284
pr_cont("(,...)");
#endif /* CONFIG_PARPORT_1284 */
pr_cont("]\n");
if (probedirq != PARPORT_IRQ_NONE)
pr_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)) {
pr_warn("%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)) {
pr_warn("%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) {
pr_warn("%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;
}
struct parport *parport_pc_probe_port(unsigned long int base,
unsigned long int base_hi,
int irq, int dma,
struct device *dev,
int irqflags)
{
return __parport_pc_probe_port(base, base_hi, irq, dma,
dev, irqflags, 0, 0);
}
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;
pr_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) {
pr_info("parport_pc: cannot find ITE8872 INTA\n");
return 0;
}
type = inb(inta_addr[i] + 0x18);
type &= 0x0f;
switch (type) {
case 0x2:
pr_info("parport_pc: ITE8871 found (1P)\n");
ite8872set = 0x64200000;
break;
case 0xa:
pr_info("parport_pc: ITE8875 found (1P)\n");
ite8872set = 0x64200000;
break;
case 0xe:
pr_info("parport_pc: ITE8872 found (2S1P)\n");
ite8872set = 0x64e00000;
break;
case 0x6:
pr_info("parport_pc: ITE8873 found (1S)\n");
release_region(inta_addr[i], 32);
return 0;
case 0x8:
pr_info("parport_pc: ITE8874 found (2S)\n");
release_region(inta_addr[i], 32);
return 0;
default:
pr_info("parport_pc: unknown ITE887x\n");
pr_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));
pr_debug("ITE887x: The IRQ is %d\n", ite8872_irq);
pr_debug("ITE887x: The PARALLEL I/O port is 0x%x\n", ite8872_lpt);
pr_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)) {
pr_info("parport_pc: ITE 8872 parallel port: io=0x%X",
ite8872_lpt);
if (irq != PARPORT_IRQ_NONE)
pr_cont(", irq=%d", irq);
pr_cont("\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) {
pr_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:
pr_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)) {
pr_info("parport_pc: VIA parallel port: io=0x%X", port1);
if (irq != PARPORT_IRQ_NONE)
pr_cont(", irq=%d", irq);
if (dma != PARPORT_DMA_NONE)
pr_cont(", dma=%d", dma);
pr_cont("\n");
return 1;
}
pr_warn("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_9900,
netmos_9705,
netmos_9715,
netmos_9755,
netmos_9805,
netmos_9815,
parport/serial: add support for NetMos 9901 Multi-IO card Add support for the PCI-Express NetMos 9901 Multi-IO card. 0001:06:00.0 Serial controller [0700]: NetMos Technology Device [9710:9901] (prog-if 02 [16550]) Subsystem: Device [a000:1000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin A routed to IRQ 65 Region 0: I/O ports at 0030 [size=8] Region 1: Memory at 80105000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80104000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: serial Kernel modules: 8250_pci 0001:06:00.1 Serial controller [0700]: NetMos Technology Device [9710:9901] (prog-if 02 [16550]) Subsystem: Device [a000:1000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin B routed to IRQ 65 Region 0: I/O ports at 0020 [size=8] Region 1: Memory at 80103000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80102000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: serial Kernel modules: 8250_pci 0001:06:00.2 Parallel controller [0701]: NetMos Technology Device [9710:9901] (prog-if 03 [IEEE1284]) Subsystem: Device [a000:2000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin C routed to IRQ 65 Region 0: I/O ports at 0010 [size=8] Region 1: I/O ports at <unassigned> Region 2: Memory at 80101000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80100000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: parport_pc Kernel modules: parport_pc [ 16.760181] PCI parallel port detected: 416c:0100, I/O at 0x812010(0x0), IRQ 65 [ 16.760225] parport0: PC-style at 0x812010, irq 65 [PCSPP,TRISTATE,EPP] [ 16.851842] serial 0001:06:00.0: enabling device (0004 -> 0007) [ 16.883776] 0001:06:00.0: ttyS0 at I/O 0x812030 (irq = 65) is a ST16650V2 [ 16.893832] serial 0001:06:00.1: enabling device (0004 -> 0007) [ 16.926537] 0001:06:00.1: ttyS1 at I/O 0x812020 (irq = 65) is a ST16650V2 Signed-off-by: Michael Buesch <mb@bu3sch.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-30 18:41:21 +00:00
netmos_9901,
netmos_9865,
quatech_sppxp100,
wch_ch382l,
};
/* 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[2];
/* Bit field of parport modes to exclude. */
unsigned int mode_mask;
/* If non-zero, sets the bitmask of writable ECR bits. In that
* case additionally bit 0 will be forcibly set on writes. */
unsigned char ecr_writable;
/* 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);
[PATCH] parport_pc: fix section mismatch warnings (v2) From: Randy Dunlap <rdunlap@xenotime.net> Fix all modpost section mismatch warnings in parport_pc: WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x230) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x283) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x3e6) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x400) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x463) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.text: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x488) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:superios from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x54c) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_probe_port after 'parport_pc_probe_port' (at offset 0x56a) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x67) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x9f) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0xa7) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data:cards from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x132) WARNING: drivers/parport/parport_pc.o - Section mismatch: reference to .init.data: from .text.parport_pc_pci_probe after 'parport_pc_pci_probe' (at offset 0x142) Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-19 05:21:57 +00:00
} 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 },} },
parport_pc: Set up mode and ECR masks for Oxford Semiconductor devices No Oxford Semiconductor PCI or PCIe parallel port device supports the Parallel Port FIFO mode. All support the PS/2 Parallel Port mode and the Enhanced Parallel Port mode via the ECR register. The original 5V PCI OX16PCI954 device does not support the Extended Capabilities Port mode, the Test mode or the Configuration mode, but all the other OxSemi devices do, including in particular the 3.3V PCI OXmPCI954 device and the universal voltage PCI OXuPCI954 device. All the unsupported modes are marked reserved in the relevant datasheets. Accordingly enable the `base_hi' BAR for the 954 devices to enable PS2 and EPP mode support via the ECR register, however mask the COMPAT mode and, until we have a way to determine what chip variant it is that we poke at, also the ECP mode, and mask the COMPAT mode only for all the remaining OxSemi devices, fixing errors like: parport0: FIFO is stuck FIFO write timed out and a non-functional port when the Parallel Port FIFO mode is selected. Complementing the fix apply an ECR mask for all these devices, which are documented to only permit writing to the mode field of the ECR register with a bit pattern of 00001 required to be written to bits 4:0 on mode field writes. No nFault or service interrupts are implemented, which will therefore never have to be enabled, though bit 2 does report the FIFO threshold status to be polled for in the ECP mode where supported. We have a documented case of writing 1 to bit 2 causing a lock-up with at least one OX12PCI840 device (from old drivers/parport/ChangeLog): 2001-10-10 Tim Waugh <twaugh@redhat.com> * parport_pc.c: Support for OX12PCI840 PCI card (reported by mk@daveg.com). Lock-ups diagnosed by Ronnie Arosa (and now we just don't trust its ECR). which commit adbd321a17cc ("parport_pc: add base_hi BAR for oxsemi_840") must have broken and by applying an ECR mask here we prevent the lock-up from triggering. This could have been the reason for requiring 00001 to be written to bits 4:0 of ECR. Update the inline comment accordingly; it has come from Linux 2.4.12 back in 2001 and predates the introduction of OXmPCI954 and OXuPCI954 devices that do support ECP. References: [1] "OX16PCI954 Integrated Quad UART and PCI interface", Oxford Semiconductor Ltd., Data Sheet Revision 1.3, Feb. 1999, Chapter 9 "Bidirectional Parallel Port", pp. 53-55 [2] "OX16PCI952 Data Sheet, Integrated High Performance Dual UARTs, Parallel Port and 5.0v PCI interface", Oxford Semiconductor Ltd., DS_B008A_00, Datasheet rev 1.1, June 2001, Chapter 8 "Bi-directional Parallel Port", pp. 52-56 [3] "OXmPCI954 DATA SHEET Integrated High Performance Quad UARTs, 8-bit Local Bus/Parallel Port. 3.3v PCI/miniPCI interface.", Oxford Semiconductor Ltd., DS-0019, June 2005, Chapter 10 "Bidirectional Parallel Port", pp. 86-90 [4] "OXmPCI952 Data Sheet, Integrated High Performance Dual UARTs, 8-bit Local Bus/Parallel Port. 3.3v PCI/miniPCI interface.", Oxford Semiconductor Ltd., DS-0020, June 2005, Chapter 8 "Bidirectional Parallel Port", pp. 73-77 [5] "OX12PCI840 Integrated Parallel Port and PCI interface", Oxford Semiconductor Ltd., DS-0021, Jun 2005, Chapter 5 "Bi-directional Parallel Port", pp. 18-21 [6] "OXPCIe952 PCI Express Bridge to Dual Serial & Parallel Port", Oxford Semiconductor, Inc., DS-0046, Mar 06 08, Chapter "Parallel Port Function", pp. 59-62 [7] "OXPCIe840 PCI Express Bridge to Parallel Port", Oxford Semiconductor, Inc., DS-0049, Mar 06 08, Chapter "Parallel Port Function", pp. 15-18 [8] "OXuPCI954 Data Sheet, Integrated High Performance Quad UARTs, 8-bit Local Bus/Parallel Port, 3.3 V and 5 V (Universal Voltage) PCI Interface.", Oxford Semiconductor, Inc., DS-0058, 26 Jan 2009, Chapter 8 "Bidirectional Parallel Port", pp. 62-65 [9] "OXuPCI952 Data Sheet, Integrated High Performance Dual UARTs, 8-bit Local Bus/Parallel Port, 3.3 V and 5.0 V Universal Voltage PCI Interface.", Oxford Semiconductor, Inc., DS-0059, Sep 2007, Chapter 8 "Bidirectional Parallel Port", pp. 61-64 Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk> Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Link: https://lore.kernel.org/r/20230108215656.6433-6-sudipm.mukherjee@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-01-08 21:56:55 +00:00
/* The Oxford Semi cards are unusual: older variants of 954 don't
* support ECP, and 840 locks up if you write 1 to bit 2! None
* implement nFault or service interrupts and all require 00001
* bit pattern to be used for bits 4:0 with ECR writes. */
/* oxsemi_952 */ { 1, { { 0, 1 }, },
PARPORT_MODE_COMPAT, ECR_MODE_MASK },
/* oxsemi_954 */ { 1, { { 0, 1 }, },
PARPORT_MODE_ECP |
PARPORT_MODE_COMPAT, ECR_MODE_MASK },
/* oxsemi_840 */ { 1, { { 0, 1 }, },
PARPORT_MODE_COMPAT, ECR_MODE_MASK },
/* oxsemi_pcie_pport */ { 1, { { 0, 1 }, },
PARPORT_MODE_COMPAT, ECR_MODE_MASK },
/* aks_0100 */ { 1, { { 0, -1 }, } },
/* mobility_pp */ { 1, { { 0, 1 }, } },
/* netmos_9900 */ { 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 }, } },
parport/serial: add support for NetMos 9901 Multi-IO card Add support for the PCI-Express NetMos 9901 Multi-IO card. 0001:06:00.0 Serial controller [0700]: NetMos Technology Device [9710:9901] (prog-if 02 [16550]) Subsystem: Device [a000:1000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin A routed to IRQ 65 Region 0: I/O ports at 0030 [size=8] Region 1: Memory at 80105000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80104000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: serial Kernel modules: 8250_pci 0001:06:00.1 Serial controller [0700]: NetMos Technology Device [9710:9901] (prog-if 02 [16550]) Subsystem: Device [a000:1000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin B routed to IRQ 65 Region 0: I/O ports at 0020 [size=8] Region 1: Memory at 80103000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80102000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: serial Kernel modules: 8250_pci 0001:06:00.2 Parallel controller [0701]: NetMos Technology Device [9710:9901] (prog-if 03 [IEEE1284]) Subsystem: Device [a000:2000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin C routed to IRQ 65 Region 0: I/O ports at 0010 [size=8] Region 1: I/O ports at <unassigned> Region 2: Memory at 80101000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80100000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: parport_pc Kernel modules: parport_pc [ 16.760181] PCI parallel port detected: 416c:0100, I/O at 0x812010(0x0), IRQ 65 [ 16.760225] parport0: PC-style at 0x812010, irq 65 [PCSPP,TRISTATE,EPP] [ 16.851842] serial 0001:06:00.0: enabling device (0004 -> 0007) [ 16.883776] 0001:06:00.0: ttyS0 at I/O 0x812030 (irq = 65) is a ST16650V2 [ 16.893832] serial 0001:06:00.1: enabling device (0004 -> 0007) [ 16.926537] 0001:06:00.1: ttyS1 at I/O 0x812020 (irq = 65) is a ST16650V2 Signed-off-by: Michael Buesch <mb@bu3sch.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-30 18:41:21 +00:00
/* netmos_9901 */ { 1, { { 0, -1 }, } },
/* netmos_9865 */ { 1, { { 0, -1 }, } },
/* quatech_sppxp100 */ { 1, { { 0, 1 }, } },
/* wch_ch382l */ { 1, { { 2, -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_9900,
0xA000, 0x2000, 0, 0, netmos_9900 },
{ 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 },
parport/serial: add support for NetMos 9901 Multi-IO card Add support for the PCI-Express NetMos 9901 Multi-IO card. 0001:06:00.0 Serial controller [0700]: NetMos Technology Device [9710:9901] (prog-if 02 [16550]) Subsystem: Device [a000:1000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin A routed to IRQ 65 Region 0: I/O ports at 0030 [size=8] Region 1: Memory at 80105000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80104000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: serial Kernel modules: 8250_pci 0001:06:00.1 Serial controller [0700]: NetMos Technology Device [9710:9901] (prog-if 02 [16550]) Subsystem: Device [a000:1000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin B routed to IRQ 65 Region 0: I/O ports at 0020 [size=8] Region 1: Memory at 80103000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80102000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: serial Kernel modules: 8250_pci 0001:06:00.2 Parallel controller [0701]: NetMos Technology Device [9710:9901] (prog-if 03 [IEEE1284]) Subsystem: Device [a000:2000] Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin C routed to IRQ 65 Region 0: I/O ports at 0010 [size=8] Region 1: I/O ports at <unassigned> Region 2: Memory at 80101000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 80100000 (32-bit, non-prefetchable) [size=4K] Capabilities: <access denied> Kernel driver in use: parport_pc Kernel modules: parport_pc [ 16.760181] PCI parallel port detected: 416c:0100, I/O at 0x812010(0x0), IRQ 65 [ 16.760225] parport0: PC-style at 0x812010, irq 65 [PCSPP,TRISTATE,EPP] [ 16.851842] serial 0001:06:00.0: enabling device (0004 -> 0007) [ 16.883776] 0001:06:00.0: ttyS0 at I/O 0x812030 (irq = 65) is a ST16650V2 [ 16.893832] serial 0001:06:00.1: enabling device (0004 -> 0007) [ 16.926537] 0001:06:00.1: ttyS1 at I/O 0x812020 (irq = 65) is a ST16650V2 Signed-off-by: Michael Buesch <mb@bu3sch.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-30 18:41:21 +00:00
{ 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 },
/* WCH CH382L PCI-E single parallel port card */
{ 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l },
{ 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,
cards[i].mode_mask,
cards[i].ecr_writable);
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 {
pr_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:
pr_warn("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:
pr_warn("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) {
pr_warn("parport=%s not understood\n", str);
return 1;
}
if (parport_setup_ptr == PARPORT_PC_MAX_PORTS) {
pr_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;
parport: parport_pc, do not remove parent devices early When the parport_pc module is removed from the system, all parport devices are iterated in parport_pc_exit and removed by a call to parport_pc_unregister_port. Note that some parport devices have its 'struct device' parent, known as port->dev. And when port->dev is a platform device, it is destroyed in parport_pc_exit too. Now, when parport_pc_unregister_port is called for a going port, drv->detach(port) is called for every parport driver in the system. ppdev can be one of them. ppdev's detach() tears down its per-port sysfs directory, which established port->dev as a parent earlier. But since parport_pc_exit kills port->dev parents before unregisters ports proper, ppdev's sysfs directory has no living parent anymore. This results in the following warning: WARNING: CPU: 1 PID: 785 at fs/sysfs/group.c:219 sysfs_remove_group+0x9b/0xa0 sysfs group ffffffff81c69e20 not found for kobject 'parport1' Modules linked in: parport_pc(E-) ppdev(E) [last unloaded: ppdev] CPU: 1 PID: 785 Comm: rmmod Tainted: G W E 3.18.0-rc5-next-20141120+ #824 ... Call Trace: ... [<ffffffff810aff76>] warn_slowpath_fmt+0x46/0x50 [<ffffffff8123d81b>] sysfs_remove_group+0x9b/0xa0 [<ffffffff814c27e7>] dpm_sysfs_remove+0x57/0x60 [<ffffffff814b6ac9>] device_del+0x49/0x240 [<ffffffff814b6ce2>] device_unregister+0x22/0x70 [<ffffffff814b6dac>] device_destroy+0x3c/0x50 [<ffffffffc012209a>] pp_detach+0x4a/0x60 [ppdev] [<ffffffff814b32dd>] parport_remove_port+0x11d/0x150 [<ffffffffc0137328>] parport_pc_unregister_port+0x28/0xf0 [parport_pc] [<ffffffffc0138c0e>] parport_pc_exit+0x76/0x468 [parport_pc] [<ffffffff81128dbc>] SyS_delete_module+0x18c/0x230 It is also easily reproducible on qemu with two dummy ports '-parallel /dev/null -parallel /dev/null'. So switch the order of killing the two structures. But since port is freed by parport_pc_unregister_port, we have to remember port->dev in a local variable. Perhaps nothing worse than the warning happens thanks to the device refcounting. We *should* be on the safe side. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Reviewed-by: Takashi Iwai <tiwai@suse.de> Tested-by: Martin Pluskal <mpluskal@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-11-21 09:05:09 +00:00
struct device *dev;
priv = list_entry(ports_list.next,
struct parport_pc_private, list);
port = priv->port;
parport: parport_pc, do not remove parent devices early When the parport_pc module is removed from the system, all parport devices are iterated in parport_pc_exit and removed by a call to parport_pc_unregister_port. Note that some parport devices have its 'struct device' parent, known as port->dev. And when port->dev is a platform device, it is destroyed in parport_pc_exit too. Now, when parport_pc_unregister_port is called for a going port, drv->detach(port) is called for every parport driver in the system. ppdev can be one of them. ppdev's detach() tears down its per-port sysfs directory, which established port->dev as a parent earlier. But since parport_pc_exit kills port->dev parents before unregisters ports proper, ppdev's sysfs directory has no living parent anymore. This results in the following warning: WARNING: CPU: 1 PID: 785 at fs/sysfs/group.c:219 sysfs_remove_group+0x9b/0xa0 sysfs group ffffffff81c69e20 not found for kobject 'parport1' Modules linked in: parport_pc(E-) ppdev(E) [last unloaded: ppdev] CPU: 1 PID: 785 Comm: rmmod Tainted: G W E 3.18.0-rc5-next-20141120+ #824 ... Call Trace: ... [<ffffffff810aff76>] warn_slowpath_fmt+0x46/0x50 [<ffffffff8123d81b>] sysfs_remove_group+0x9b/0xa0 [<ffffffff814c27e7>] dpm_sysfs_remove+0x57/0x60 [<ffffffff814b6ac9>] device_del+0x49/0x240 [<ffffffff814b6ce2>] device_unregister+0x22/0x70 [<ffffffff814b6dac>] device_destroy+0x3c/0x50 [<ffffffffc012209a>] pp_detach+0x4a/0x60 [ppdev] [<ffffffff814b32dd>] parport_remove_port+0x11d/0x150 [<ffffffffc0137328>] parport_pc_unregister_port+0x28/0xf0 [parport_pc] [<ffffffffc0138c0e>] parport_pc_exit+0x76/0x468 [parport_pc] [<ffffffff81128dbc>] SyS_delete_module+0x18c/0x230 It is also easily reproducible on qemu with two dummy ports '-parallel /dev/null -parallel /dev/null'. So switch the order of killing the two structures. But since port is freed by parport_pc_unregister_port, we have to remember port->dev in a local variable. Perhaps nothing worse than the warning happens thanks to the device refcounting. We *should* be on the safe side. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Reviewed-by: Takashi Iwai <tiwai@suse.de> Tested-by: Martin Pluskal <mpluskal@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-11-21 09:05:09 +00:00
dev = port->dev;
parport_pc_unregister_port(port);
parport: parport_pc, do not remove parent devices early When the parport_pc module is removed from the system, all parport devices are iterated in parport_pc_exit and removed by a call to parport_pc_unregister_port. Note that some parport devices have its 'struct device' parent, known as port->dev. And when port->dev is a platform device, it is destroyed in parport_pc_exit too. Now, when parport_pc_unregister_port is called for a going port, drv->detach(port) is called for every parport driver in the system. ppdev can be one of them. ppdev's detach() tears down its per-port sysfs directory, which established port->dev as a parent earlier. But since parport_pc_exit kills port->dev parents before unregisters ports proper, ppdev's sysfs directory has no living parent anymore. This results in the following warning: WARNING: CPU: 1 PID: 785 at fs/sysfs/group.c:219 sysfs_remove_group+0x9b/0xa0 sysfs group ffffffff81c69e20 not found for kobject 'parport1' Modules linked in: parport_pc(E-) ppdev(E) [last unloaded: ppdev] CPU: 1 PID: 785 Comm: rmmod Tainted: G W E 3.18.0-rc5-next-20141120+ #824 ... Call Trace: ... [<ffffffff810aff76>] warn_slowpath_fmt+0x46/0x50 [<ffffffff8123d81b>] sysfs_remove_group+0x9b/0xa0 [<ffffffff814c27e7>] dpm_sysfs_remove+0x57/0x60 [<ffffffff814b6ac9>] device_del+0x49/0x240 [<ffffffff814b6ce2>] device_unregister+0x22/0x70 [<ffffffff814b6dac>] device_destroy+0x3c/0x50 [<ffffffffc012209a>] pp_detach+0x4a/0x60 [ppdev] [<ffffffff814b32dd>] parport_remove_port+0x11d/0x150 [<ffffffffc0137328>] parport_pc_unregister_port+0x28/0xf0 [parport_pc] [<ffffffffc0138c0e>] parport_pc_exit+0x76/0x468 [parport_pc] [<ffffffff81128dbc>] SyS_delete_module+0x18c/0x230 It is also easily reproducible on qemu with two dummy ports '-parallel /dev/null -parallel /dev/null'. So switch the order of killing the two structures. But since port is freed by parport_pc_unregister_port, we have to remember port->dev in a local variable. Perhaps nothing worse than the warning happens thanks to the device refcounting. We *should* be on the safe side. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Reviewed-by: Takashi Iwai <tiwai@suse.de> Tested-by: Martin Pluskal <mpluskal@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-11-21 09:05:09 +00:00
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)