[PARPORT] Patch: 2.3.99p3 SuperIO#3 for parport_pc

From: Gunther Mayer (gunther.mayer@braunschweig.netsurf.de)
Date: Sun Apr 02 2000 - 13:38:01 EDT

  • Next message: Erik Inge Bolsų: "[PARPORT] Re: New queueing code"

    Hi,
    this is the super-io patch#3 for 2.3.99pre3:
    - show configuration for W83877 and NS87306
    - more SMSC types
    - detect National Semiconductor.
    This is just the last patch relative to 2.3.50pre adopted to the current
    kernel.

    Apply, test and report if you like.

    Feedback for correctly recognized chips could encourage the
    yet missing autodetect routines for IRQ and DMA for these chips :-)

    Regards, Gunther


    --- linux-2.3.99p3/drivers/parport/parport_pc.c Sun Apr 2 18:30:09 2000
    +++ linux/drivers/parport/parport_pc.c Sun Apr 2 18:27:38 2000
    @@ -1020,7 +1020,145 @@
     };
     
     #ifdef CONFIG_PARPORT_PC_SUPERIO
    -/* Super-IO chipset detection, Winbond, SMSC */
    +/* Super-IO chipset detection, Winbond, SMSC National Semiconductor (EXPERIMENTAL)
    +
    + Once upon a time there were single purpose chips or even dicrete TTL logic (e.g.
    + the parallel port) on ISA add-on cards. The register interface and functionality
    + was dictated by compatibilty to the de facto IBM PC-Standard. Many manufacturers
    + built these and some even improved functionality (e.g. serial FIFO) only to become
    + itself a de facto standard (e.g. 16550A compatible). Others introduced their own
    + hardware bugs or flaws, unfortunately.
    +
    + Soon some of these were integrated on a single chip. These Multi I/O chips
    + were jumper-configurable for IO, IRQ, etc. The Winbond 83757,777,778 is a popular example.
    + Configuration was error prone and uncomfortable for the user.
    +
    + The Super I/O chips were destined for mainboard integration and are fully software
    + configurable (although many can start with default compatible settings). Many aspects can
    + be configured by the BIOS, but the quality of support depends on the BIOS vendor.
    +
    + The configuration had to be done via IO accesses and there was no common method
    + (like ISA PnP) at these days. So vendors invented their own method: choose two
    + ports that don't conflict with legacy systems and use these as index and data register.
    + The ports chosen were e.g. Read-Only in legacy systems (e.g. 3F0, 370, ...), or otherwise
    + chosen (26E, 398, ...). (Note: the same applies to onboard integrated chipsets of these days
    + (e.g. Opti, Eteq, UMC, SiS, C&T, these used port 0x22,34,24, etc).
    + The configuration regoster layout was proprietary but since some years is oriented
    + on the ISA PnP standard (called mainboard PnP). Some newer chips have real ISAPnP modes.
    + Optionally you have to write a magic sequence of bytes to "open" and "close" configuration
    + mode.
    +
    + To protect against accidential access you can "lock" and hide the configuration by setting
    + some bits, or you can relocate the configuration register pair at your will. If this
    + is done by your BIOS there is no way to detect these...
    +
    + Astonishing it seems possible to identify these parts despite there was no cooperative
    + effort by the manufacturers and many BIOSes do not lock access or relocate:-)
    +
    + This probe currently does it's best being untested on most parts !!!
    + Currently it probes for ALL chips, although you should stop after having detected ONE super I/O
    + chipset. This shall help to identify false detections (negative and positive) and further
    + finetune the routines ...
    +
    + The parallel port suffered in early days by vendor dependend hardware flaws and features,
    + so it is necessary to identify these chips to improve quality of bug reports and further
    + developing. Now you can identify your chip without opening your case! And you are even
    + given further information on configuration aspects that could not be derived by other means.
    +
    + The right forum to discuss issues with this code is the Parport Mailing List on
    + <linux-parport@torque.net> 01apr2000 gm.
    +
    + Another effect for your comfort is improved autodetection of IO/IRQ/DMA and mode of your
    + parallel port. It is unclear if EPP-BIOS would be helpful as it is unknown how wide-spread
    + it's implementation in ROM-BIOS is. However EPP-BIOS would give IO, IRQ and port mode,
    + but it misses DMA channel.
    +
    + Notes:
    + (1) EPP-BIOS has nothing to to with EPP-mode, neither IEEE1284(EPP1.9) nor pre-IEEE1284(1.7),
    + Of course this BIOS supports IEEE1284-ECP mode in most cases.
    + (2) ECP is used with two different meanings:
    + a) ECP as IEEE1284-ECP mode.
    + A physical/electrical transfer protocol over the parallel port as defined by IEEE1284.
    + You can speak ECP mode even over a SPP (Standard Paralllel Port), although slower in
    + comparison to some chipsets that support this in hardware.
    + b) ECP as a ISA-ECP.
    + A register set standard for the ISA bus, as defined by Microsoft et al.
    + Of course ISA-ECP supports the IEEE1284-ECP mode :-)
    + This defines how to let your hardware speak ECP natively.
    +
    + Offtopic:
    + As another side effect other drivers could benefit from this detections as you can get
    + plenty of information about FDC, Serial Port, Infrared Support, Power Management (Legacy,
    + APM and ACPI), KBC, X-Bus, etc. Some newer chips integrate Hardware Monitoring, a watchdog
    + or even a smart card reader interface...
    + Hint: People with buggy APM-BIOS could use this to soft power off their machine directly by
    + fiddling with the right bits (don't ask, I never tried). This is the right place to dive into
    + SMM/SMI BIOS/chipset interactions for the adventureous hacker, too :-)
    + */
    +
    +static void __devinit show_parconfig_winbond_877(int io, int key, int oldid)
    +{
    + int cr0,cr3,cr4,cr5,cr9,cr17,cr23,cr26,cr27,cr31=0,cr32=0;
    + int efir,efdr;
    + char *modes[]= { "reserved",
    + "EPP+SPP",
    + "ECP",
    + "ECP+EPP"};
    + if(io==0x3f0) {
    + outb(key,io);
    + outb(key,io);
    + efir=io;
    + efdr=io+1;
    + } else { /* 0x250 */
    + outb(key,io);
    + efir=io+1;
    + efdr=io+2;
    + }
    +#define sreg(cr,x) { outb(x,efir); cr=inb(efdr); }
    + sreg(cr0,0);
    + sreg(cr3,3);
    + sreg(cr4,4);
    + sreg(cr5,5);
    + sreg(cr9,9);
    + sreg(cr17,0x17);
    + sreg(cr23,0x23);
    + sreg(cr26,0x26);
    + sreg(cr27,0x27);
    + if(oldid>0x0b) {
    + sreg(cr31,0x31);
    + sreg(cr32,0x32);
    + }
    +#undef sreg
    + outb(0xaa,io);
    +
    + printk("Winbond 83877xx LPT Config cr0=%02x cr3=%02x"
    + "cr4=%02x cr5=%02x cr9=%02x cr17=%02x cr23=%02x"
    + " cr26=%02x cr27=%02x cr31=%02x",
    + cr0,cr3,cr4,cr5,cr9,cr17,cr23,cr26,cr27,cr31);
    +
    + printk ("W83877 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): '-', cr5 & 0x0f);
    + printk("W83877 LPT Config: enabled=%s power=%s\n",
    + (cr23*4 >=0x100) ?"yes":"no", (cr4 & 0x80) ? "yes" : "no");
    + printk("W83877 LPT Config: Port mode=%s, EPP version =%s\n",
    + (cr9 & 0x80 ) ? modes[(cr0>>2) & 0x03] :
    + (((cr0>>2) & 0x03)?"non parallel": "SPP"),
    + (cr3 & 0x10) ? "1.7" : "1.9");
    + printk("W83877 LPT Config: IRQ line is totem pole in %s",
    + cr17 & 0x10 ? "all modes": "SPP mode/open drain in ECP/EPP");
    + printk("W83877 LPT Config: IRQ %ss AckIntEnable Bit\n",
    + cr17 &0x04 ? "ignore" : "obey");
    +
    + if(oldid>0x0b) {
    + if( cr31 &0x04) printk("W83877 in serial PCI-IRQ mode\n");
    + printk("W83877 Power Management=%sabled,"
    + "LPT auto power management=%sabled\n",
    + (cr32&0x80) ? "en": "dis", (cr32&0x08)? "en": "dis");
    + }
    +}
    +
     static void __devinit show_parconfig_smsc37c669(int io, int key)
     {
             int cr1,cr4,cra,cr23,cr26,cr27,i=0;
    @@ -1095,6 +1233,67 @@
              }
     }
     
    +void show_parconfig_pc87306(int io)
    +{ int a=0,i=0,cr0,cr1,cr3,cr4,cr7,cr18,cr19,cr1b;
    +
    +#define sreg(cr,x) { outb(x,io); cr=inb(io+1); }
    + sreg(cr0,0);
    + sreg(cr1,1)
    + sreg(cr3,3);
    + sreg(cr4,4);
    + sreg(cr7,7);
    + sreg(cr18,0x18);
    + sreg(cr19,0x19);
    + sreg(cr1b,0x1b);
    +#undef sreg
    +
    + switch (cr1&0x03) {
    + case 0x00:
    + a=0x378; i=5;
    + if(cr1b & 0x20 ) i=7; /* see datasheet NOTE 1 */
    + break;
    + case 0x01:
    + a=0x3bc; i=7;
    + if(cr1b&0x40) a=cr19*4; /* see datasheet NOTE 2 */
    + break;
    + case 0x02: a=0x278; i=5; break;
    + case 0x03: a=0; break; /* reserved */
    + }
    +
    + if(cr1b & 0x10) { /* this overrides address dependent irq selection */
    + if(cr1b & 0x20 )
    + i=7;
    + else
    + i=5;
    + }
    +
    + printk("PC87306 LPT enabled=%s io=0x%04x irq=%d dmaLine=PDRQ%d (see ECPcnfgDMA)\n",
    + cr0&1? "yes":"no", a,i,cr18&0x08 ? 1:0);
    + /* This chip is EPP XOR ECP. Can select between 2 DMA Lines, but wiring up to vendor */
    + printk("PC87306 EPP=%s%s ECP=%s Bidir=%s\n",
    + cr4&0x01?"yes":"no" , cr4&0x01 ? ( cr4&0x02 ? "(1.9)": "(1.7)") : "",
    + cr4&0x04?"yes":"no" , cr3&0x80?"yes":"no");
    + printk("PC83706 IRQ polarity=%s, IRQ line=%s, ECPcnfgDMA=%d\n EPPtimeoutIRQ=%s",
    + cr4&0x10 ? "highLevel/negativePulse": "inverted(huh!?)",
    + cr4&0x20 ? "totem pole":"open drain", cr18&0x03,
    + cr7&0x04 ? "enabled":"masked");
    + /* set the hints for the normal autodetect code */
    + if(cr0 & 0x01) { /* the settings can be interrogated later ... */
    + while((superios[i].io!= 0) && (i<NR_SUPERIOS))
    + i++;
    + if(i==NR_SUPERIOS)
    + printk("Super-IO: too many chips!\n");
    + else {
    + superios[i].io = a;
    + superios[i].irq = i;
    + superios[i].dma = PARPORT_DMA_NONE;
    + /* arbitrary wiring. Hopefully BIOS sets ECPcnfgDMA correctly */
    + }
    + }
    +
    +}
    +
    +
     
     static void __devinit show_parconfig_winbond(int io, int key)
     {
    @@ -1185,36 +1384,58 @@
             else if ((oldid &0x0f ) == 0x0d) { type="83877ATF"; progif=1;}
             else progif=0;
     
    - if(type==NULL)
    - printk("Winbond unknown chip type\n");
    - else
    - printk("Winbond chip type %s\n",type);
    + if(type==NULL)
    + printk("Winbond unknown chip type\n");
    + else {
    + printk("Winbond chip type %s\n",type);
    + if(progif==2)
    + show_parconfig_winbond(efer,key);
    + else /* progif==1 */
    + show_parconfig_winbond_877(efer,key,oldid);
    + }
     
    - if(progif==2)
    - show_parconfig_winbond(efer,key);
             return;
     }
     
    -static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
    +static void __devinit decode_smsc(int efer, int key, int oldid, int oldrev, int id, int rev)
     {
             char *type=NULL;
    - void (*func)(int io, int key);
    - int id;
    -
    - if (devid == devrev)
    - /* simple heuristics, we happened to read some
    - non-winbond register */
    - return;
    + void (*func)(int io, int key)=NULL;
    + int idrev,oldidrev;
     
    - func=NULL;
    - printk("SMSC chip at EFER=0x%x key=0x%02x devid=%02x devrev=%02x\n",
    - efer,key,devid,devrev);
    - id=(devid<<8) | devrev;
    -
    - if (id==0x0302) {type="37c669"; func=show_parconfig_smsc37c669;}
    - else if (id==0x6582) type="37c665IR";
    - else if ((id==0x6502) && (key==0x44)) type="37c665GT";
    - else if ((id==0x6502) && (key==0x55)) type="37c666GT";
    + if ((oldid == oldrev) && (id == rev))
    + /* simple heuristics, we happened to read some
    + non-smsc register */
    + return;
    +
    + printk("SMSC chip at EFER=0x%x key=0x%02x id=%02x rev=%02x\n"
    + "oldid=0x%02x oldrev=0x%02x\n",
    + efer,key,id,rev,oldid,oldrev);
    + oldidrev=(oldid<<8) | oldrev;
    + idrev=(id<<8)|rev;
    +
    + if (oldidrev==0x0302) {type="37c669"; func=show_parconfig_smsc37c669;}
    + else if (oldid==0x40) type="37c669FR";
    + else if (oldidrev==0x6582) type="37c665IR";
    + else if ((oldidrev==0x6502) && (key==0x44)) type="37c665GT";
    + else if ((oldidrev==0x6502) && (key==0x55)) type="37c666GT";
    + else if (oldid==0x28) type="37n869"; /* "n" notifies notebook ? */
    + else if (oldid==0x29) type="37n769";
    + else if (oldid==0x78) type="FDC37c78"; /* Floppy disk only */
    + else if (idrev==0x0201) type="37c93x";
    + else if (idrev==0x0301) type="37c83xFR";
    + else if (idrev==0x3001) type="37c93xAPM";
    + else if (id==0x09) type="37n958";
    + else if (id==0x09) type="37n971";
    + else if (id==0x0a) type="37n972";
    + else if (id==0x40) type="37c67";
    + else if (id==0x42) type="37m70 / 37b80";
    + else if (id==0x43) type="37b77";
    + else if (id==0x44) type="37b78";
    + else if (id==0x47) type="37m60";
    + else if (id==0x48) type="37c68";
    + else if (id==0x4c) type="37b72";
    + else if (id==0x4d) type="37m81";
     
             if(type==NULL)
                     printk("SMSC unknown chip type\n");
    @@ -1255,7 +1476,7 @@
             decode_winbond(io,key,devid,devrev,oldid);
     }
     
    -static void __devinit winbond_check2(int io,int key)
    +static void __devinit winbond_check1(int io,int key)
     {
             int devid,devrev,oldid,x_devid,x_devrev,x_oldid;
     
    @@ -1314,7 +1535,79 @@
                 (x_oldid == oldid) && (x_rev == rev))
                     return; /* protection against false positives */
     
    - decode_smsc(io,key,oldid,oldrev);
    + decode_smsc(io,key,oldid,oldrev,id,rev);
    +}
    +
    +static void ns_check(int io) /* No key !? */
    +{
    + int id,cr8,cr21,cr27,r;
    + char *type=NULL;
    + void (*func)(int io)=NULL;
    +
    + outb(0x20,io); /* Write EFIR, extended function index register */
    + r=inb(io);
    + if (r!=0x20) return; /* read back, all bits must be writable */
    + id=inb(io+1);
    + outb(0x21,io); /* ID for PnP chips */
    + cr21=inb(io+1);
    + outb(0x27,io); /* chip revision */
    + cr27=inb(io+1);
    + outb(0x08,io); /* ID for older chips */
    + cr8=inb(io+1);
    +
    + if ((id == cr21) && (id==cr8))
    + /* simple heuristics, we happened to read some
    + non-NS register */
    + return;
    +
    + printk("National Semiconductor chip at EFER=0x%x id=0x%02x cr21=0x%02x cr27=0x%02x cr8=0x%02x",
    + io,id,cr21,cr27,cr8);
    + switch(id) {
    + case 0xc0: type="PC87307"; break;
    + case 0xcf: type="PC97307"; break;
    + case 0xd0: type="PC87317"; break;
    + case 0xdf: type="PC97317"; break;
    + case 0xe0: type="PC87309"; break;
    + case 0xe1: type="PC87360"; break;
    + case 0xe2: type="PC87351"; break;
    + case 0xe4: type="PC87364"; break;
    + case 0xe8: type="PC87363"; break;
    + case 0xe9: type="PC87366"; break;
    + }
    + if(id==0) {
    + switch(cr8&0xf0) { /* bits 0-2/3 for Revision */
    + case 0x10: type="PC87332"; break;
    + case 0x30: type="PC87303"; break;
    + case 0x50: type="PC87334"; break;
    + case 0x70: type="PC87306"; func=show_parconfig_pc87306; break;
    + case 0xb0: type="PC87338"; break;
    + }
    + }
    +
    + if(type)
    + printk("National Semiconductor chip type %s\n",type);
    + else
    + printk("National Semiconductor unknown chip type\n");
    + if(func) (func)(io);
    +
    +}
    +
    +static void ns_check94(int io)
    +{
    + int r;
    + char *type="unknown";
    +
    + outb(0xff,io);
    + r=inb(io);
    +
    + if(r==0xff)
    + return;
    +
    + if(r==0x07) type="perhaps PC87322"; /* Bits 3-7 read back as zero */
    + if(r==0x03) type="perhaps PC87311/312"; /* Bits 2-7 read back as zero */
    +
    + printk("National Semiconductor chip at EFER=0x%x readback=0x%02x type %s\n"
    + ,io,r,type);
     }
     
     
    @@ -1327,8 +1620,8 @@
             winbond_check(0x2e ,0x87);
             winbond_check(0x4e ,0x87);
             winbond_check(0x3f0,0x86);
    - winbond_check2(0x250,0x88);
    - winbond_check2(0x250,0x89);
    + winbond_check1(0x250,0x88);
    + winbond_check1(0x250,0x89);
     }
     
     static void __devinit detect_and_report_smsc (void)
    @@ -1339,6 +1632,19 @@
             smsc_check(0x3f0,0x44);
             smsc_check(0x370,0x44);
     }
    +static void detect_and_report_natsemi (void)
    +{
    + printk("National Semiconductor Super-IO detection,now testing port 2E,4E,15C,26E,398 ...\n");
    + ns_check(0x2e);
    + ns_check(0x4e); /* PC8736x */
    + ns_check(0x15c);
    + ns_check(0x26e);
    + ns_check(0x398); /* PC8733x /8731x !? */
    + ns_check94(0x26e); /* these port were used until 1994 ? */
    + ns_check94(0x398);
    + /* ns_check_87310(0x3f3); their first Super IO. */
    +}
    +
     #endif /* CONFIG_PARPORT_PC_SUPERIO */
     
     static int __devinit get_superio_dma (struct parport *p)
    @@ -2433,6 +2739,7 @@
     #ifdef CONFIG_PARPORT_PC_SUPERIO
             detect_and_report_winbond ();
             detect_and_report_smsc ();
    + detect_and_report_natsemi();
     #endif
     
             /* Onboard SuperIO chipsets that show themselves on the PCI bus. */

    -- To unsubscribe, send mail to: linux-parport-request@torque.net --
    -- with the single word "unsubscribe" in the body of the message. --



    This archive was generated by hypermail 2b29 : Sun Apr 02 2000 - 14:47:25 EDT