Bug 16078

Summary: ECP port not detected on IBM Thinkpad i1492
Product: [Retired] Red Hat Linux Reporter: d_rosky
Component: kernelAssignee: Michael K. Johnson <johnsonm>
Status: CLOSED WONTFIX QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: 6.2CC: twaugh
Target Milestone: ---   
Target Release: ---   
Hardware: i386   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2001-06-05 14:47:19 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description d_rosky 2000-08-12 21:04:19 UTC
The ECP parallel port is not detected on my Thinkpad i1492.
The Bios is set to ECP mode, irq 7, DMA 1, io 0x378.

The port is detected as SPP only.  The BIOS seems OK as
Win98 can detect it as an ECP port.  The specs for the Thinkpad i1492
indictate the I/O chip is NS PC97338 Super I/O

Problem exists with both kernel 2.2.14 shipped with RH 6.2 and
2.2.16 upgrade.  

I have tried passing specific io, irq, and dma parameters to 
parport_pc but still no luck.

Comment 1 Michael K. Johnson 2000-08-18 20:44:25 UTC
Tim, any thoughts?

Comment 2 Tim Waugh 2000-08-18 21:17:45 UTC
Please do this:

# dmesg -c
# insmod parport
# insmod parport_pc io=0x378
# dmesg > logfile

and attach the logfile so we can see what the driver says. (Use the 2.2.16-3
kernel.)

Comment 3 d_rosky 2000-08-20 16:57:33 UTC
I have some additional information regarding this bug.  I have been able to
get the National PC97338 parallel port in my Thinkpad to work in ECP mode.  The
problem 
appears to be that the BIOS does not configure the chip properly for ECP mode
when ECP 
mode is selected in the BIOS setup.

I downloaded the PC97338 data sheet from National and eventually figured out
that
although the BIOS was setting the ECP mode bit in the parallel port control
register
(0x04), it was not resetting the EPP mode bit.  Apparently this puts the chip
into an undefined
state which makes it appear on the I/O bus as if it has only SPP capabilities. 
Unfortunately,
I didn't save the direct link to the datasheet download, but it can be found by
navigating
from www.national.com

It is possible to detect the presence of the PC87338/97338 chip and set the
configuration
registers properly even if the BIOS hasn't.  There still is a "bug" in the
current Linux kernel driver 
in that it apparently doesn't try to detect and configure the actual chip - it
depends on correct 
BIOS setup and then detects the port type from the I/O register behavior. 
Windows 98,
on the other hand, apparently detects and configures the chip properly, because
ECP mode
worked OK in Win98.  The PC87338/97338 datasheet has a complete description of
how to 
detect the presence of  a PC87338/97338 by probing for its configuration
registers.

I have not tried any of the 2.3 or 2.4 kernels, so I don't know if these kernels
have
improved I/O chip detection.  If they do, this bug may be moot.  If not, the
problem probably
belongs in the hands of the kernel parport developers.

The following program is a modified version of a program I found from the Linux
parport
mailing list archive.  I fixed a bug and modified it to detect the PC87338/97338
instead
of the older chips it was originally written for.  This program operates a
little differently than
the detection description in the datasheet, but it seems to work and I have put
it at the
end of my /etc/rc.d/rc.sysinit script:

#include <asm/io.h>

#ifndef __KERNEL__
#include <stdio.h>
#include <unistd.h>

#define PPA_PROBE_SPP	0x0001
#define PPA_PROBE_PS2	0x0002
#define PPA_PROBE_ECR	0x0010
#define PPA_PROBE_EPP17	0x0100
#define PPA_PROBE_EPP19	0x0200
#define PPA_ID ""

int pc87338_port(unsigned short base);

void main(void)
{
    if (iopl(3)) {
	fprintf(stderr, "Could not unlock IO ports. Are you superuser?\n");
	exit(1);
    }
    pc87338_port(0x3bc);
    pc87338_port(0x278);
    pc87338_port(0x378);
}
#else
#include <linux/kernel.h>
#define printf printk
#define PPA_ID "ppa: "
#endif

int pc87338_port(unsigned short base)
{
    /* A routine to detect and kludge pc87338 chipsets into the
     * ECP mode...
     */

    /* This is where an pc87338 can hide */
    unsigned short index_addr[4] =
    {
	0x0398, 0x026e, 0x015c, 0x002e
    };

    /* Bits 0&1 of FAR (Function Address Register) which specify where
* the LPT port will show up at.
     */
    unsigned short port_ref[4] =
    {
	0x378, 0x3bc, 0x278, 0xffff
    };

    unsigned char a;
    int loop;

    for (loop = 0; loop < 4; loop++) {
	/* Clear the "wax" out of the pc87338, only needed after hard
	 * reset.
	 */
	inb(index_addr[loop]);
	inb(index_addr[loop]);
	inb(index_addr[loop]);
	inb(index_addr[loop]);

	/* Anyone home ?? */
	outb(0xff, index_addr[loop]);
	a = inb(index_addr[loop]);
	/* printf("0x%02x\n", a); */
	switch (a) {
	case (0x7f):		/* PC87338 */
	    break;
	default:
	    continue;
	}			/* Is this pc87338 on the desired port */
	outb(0x01, index_addr[loop]);
	a = inb(index_addr[loop] + 1);
	if (port_ref[a & 0x03] != base)
	    continue;

	/* Found a pc87338 */
	printf("NatSemi PC87338 (or variant) at 0x%04x\n", base);
	printf("    Config registers at 0x%04x\n", index_addr[loop]);

	/* Try to enable EPP modes
	 * with hardware data direction
	 */
	/* The following has been modified to set ECP mode regardless
	   of the base address */
	if (0) {
	    printf("    Switching to EPP mode\n");
	    /* EPP 1.9 */
	    outb(0x04, index_addr[loop]);
	    a = inb(index_addr[loop] + 1);
	    /* 0x01 for EPP 1.7, 0x03 for EPP 1.9, 0x0c for ECP */
	    a = (a & 0xf0) | 0x03;
	    outb(a, index_addr[loop] + 1);
	    outb(a, index_addr[loop] + 1);

	    /* Software data direction selection */
	    outb(0x02, index_addr[loop]);
	    a = inb(index_addr[loop] + 1);
	    /* 0x80 for software, 0x00 for hardware */
	    a = (a & 0x7f) | 0x80;
	    outb(a, index_addr[loop] + 1);
	    outb(a, index_addr[loop] + 1);
	} else {
	    /* There is not enough address space for the 0x3bc port
	     * to have EPP registers so we will kludge it into an
	     * ECP
	     * port to allow bi-directional byte mode...
	     */
	    printf("    Switching to ECP mode\n");
	    /* ECP */
	    outb(0x04, index_addr[loop]);
	    a = inb(index_addr[loop] + 1);
	    a = (a & 0xfe) | 0x06;
	    outb(a, index_addr[loop] + 1);
	    outb(a, index_addr[loop] + 1);
	}

	outb(0x04, index_addr[loop]);
	a = inb(index_addr[loop] + 1);
	return 1;
    }
    return 0;
}


Comment 4 Tim Waugh 2000-08-29 16:11:01 UTC
Arguably, this is a better approach than getting the kernel to do it.  I'll have
a think about it.

Comment 5 Tim Waugh 2000-08-31 11:22:50 UTC
Come to think of it the code to do this is already in the 2.4 tree, but under a
config option ISTR.  You can't just go blatting at I/O ports like 0x2e since it
can have all kinds of bad effects if you don't have one of these chips.

Comment 6 d_rosky 2000-09-12 18:17:31 UTC
Agreed.  This chip is an ISA device, and as with any ISA device
autodetecting it can be a bit tricky subject to conflicts.  The device
data sheet (downloadable from www.national.com) has some tips
for safely detecting it.  If devices like this can be detected
and configured at boot time, it would provide a measure of protection
against buggy BIOS's.


Comment 7 Tim Waugh 2001-06-05 14:47:13 UTC
Hopefully this will be all sorted out when the PnPBIOS code gets merged into 
2.4.x. (It's in the -ac tree currently.)