From: Pavel Fedin This patch allows the kernel to configure port's mode without help of BIOS. It is needed on my machine because its firmware simply ignores the parallel port leaving in unidirectional SPP mode. Notes on usage: To configure the port you need to: - if parport_pc driver is compiled as a module: specify parport_pc init_mode=[spp|ps2|epp|ecp|ecpepp] in /etc/modules file (for Debian Linux). - if the driver is linked statically with the kernel: specify parport_init_mode=[spp|ps2|epp|ecp|ecpepp] in kernel's arguments. This patch is intended for use primarily on Pegasos machines but it will work on any computer with VIA8231 south bridge. In this case it will override BIOS setting if you tell the driver to configure the port. BIOS setting will be honored if you omit init_mode parameter. Technical details: 1. On Pegasos standard port settings (set by OpenFirmware) are: IRQ=7, DMA=3, base address=0x3BC, mode is unidirectional SPP. 2. The patch changes only port mode, all other settings are preserved. So if you have a PC with VIA8231 and use driver to configure the port you'll still be able to change IRQ, DMA and base address in BIOS and these settings will be kept. 3. One exclusion: if you tell the driver to turn on EPP mode (specify epp or ecpepp value) and if default base address is 0x3BC (this is the case on Pegasos) the port will be moved to 0x378. EPP port can't have 0x3BC as the base. Known problems: 1. ECP+EPP mode is strange, it doesn't work even on several PC's i tested with Linux. ECP mode is detected and EPP is not. So if you get the same thing, it's not my fault. I just have no time to deal with it, current result is enough for me. 2. Not all devices work on Pegasos, i tried my Genuis-HR6 parallel port scanner and parallel ZIP drive and both of them don't work. ZIP drive is detected properly but then suddenly gets offline. Scanner does not answer any command at all. Without the patch the same thing happens. Obviously Pegasos has some more problems which are not discovered and not fixed yet. Signed-off-by: Andrew Morton --- 25-akpm/Documentation/kernel-parameters.txt | 10 + 25-akpm/drivers/parport/parport_pc.c | 246 ++++++++++++++++++++-------- 25-akpm/include/linux/parport_pc.h | 17 + 25-akpm/include/linux/via.h | 22 ++ 4 files changed, 229 insertions(+), 66 deletions(-) diff -puN Documentation/kernel-parameters.txt~via8231-support-for-parallel-port-driver Documentation/kernel-parameters.txt --- 25/Documentation/kernel-parameters.txt~via8231-support-for-parallel-port-driver Thu Oct 28 15:22:49 2004 +++ 25-akpm/Documentation/kernel-parameters.txt Thu Oct 28 15:22:49 2004 @@ -865,6 +865,16 @@ running once the system is up. order they are specified on the command line, starting with parport0. + parport_init_mode= + [HW,PPT] Configure VIA parallel port to + operate in specific mode. This is + necessary on Pegasos computer where + firmware has no options for setting up + parallel port mode and sets it to + spp. Currently this function knows + 686a and 8231 chips. + Format: [spp|ps2|epp|ecp|ecpepp] + pas2= [HW,OSS] Format: ,,,,,,, diff -puN drivers/parport/parport_pc.c~via8231-support-for-parallel-port-driver drivers/parport/parport_pc.c --- 25/drivers/parport/parport_pc.c~via8231-support-for-parallel-port-driver Thu Oct 28 15:22:49 2004 +++ 25-akpm/drivers/parport/parport_pc.c Thu Oct 28 15:22:49 2004 @@ -62,6 +62,7 @@ #include #include +#include #include #define PARPORT_PC_MAX_PORTS PARPORT_MAX @@ -2378,7 +2379,7 @@ EXPORT_SYMBOL (parport_pc_unregister_por /* ITE support maintained by Rich Liu */ static int __devinit sio_ite_8872_probe (struct pci_dev *pdev, int autoirq, - int autodma) + int autodma, struct parport_pc_via_data *via) { short inta_addr[6] = { 0x2A0, 0x2C0, 0x220, 0x240, 0x1E0 }; struct resource *base_res; @@ -2481,71 +2482,161 @@ static int __devinit sio_ite_8872_probe return 0; } -/* Via support maintained by Jeff Garzik */ -static int __devinit sio_via_686a_probe (struct pci_dev *pdev, int autoirq, - int autodma) +/* VIA 8231 support by Pavel Fedin + based on VIA 686a support code by Jeff Garzik */ +static int __initdata parport_init_mode = 0; + +/* Data for two known VIA chips */ +static struct parport_pc_via_data via_686a_data __devinitdata = { + 0x51, + 0x50, + 0x85, + 0x02, + 0xE2, + 0xF0, + 0xE6 +}; +static struct parport_pc_via_data via_8231_data __devinitdata = { + 0x45, + 0x44, + 0x50, + 0x04, + 0xF2, + 0xFA, + 0xF6 +}; + +static int __devinit sio_via_probe (struct pci_dev *pdev, int autoirq, + int autodma, struct parport_pc_via_data *via) { - u8 tmp; + u8 tmp, tmp2, siofunc, ppcontrol; int dma, irq; - unsigned port1, port2, have_eppecp; + 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; + ppcontrol = 0; + 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, set 0x85_1 - */ - pci_read_config_byte (pdev, 0x85, &tmp); - tmp |= (1 << 1); - pci_write_config_byte (pdev, 0x85, tmp); - - /* - * Super I/O configuration, index port == 3f0h, data port == 3f1h + * 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); + } - /* 0xE2_1-0: Parallel Port Mode / Enable */ - outb (0xE2, 0x3F0); - tmp = inb (0x3F1); + /* Parallel Port I/O Base Address, bits 9-2 */ + outb(via->viacfg_parport_base, VIA_CONFIG_INDEX); + port1 = inb(VIA_CONFIG_DATA) << 2; - if ((tmp & 0x03) == 0x03) { - printk (KERN_INFO "parport_pc: Via 686A parallel port disabled in BIOS\n"); + printk (KERN_DEBUG "parport_pc: Current parallel port base: 0x%X\n",port1); + if ((port1 == 0x3BC) && have_epp) + { + outb(via->viacfg_parport_base, VIA_CONFIG_INDEX); + outb((0x378 >> 2), VIA_CONFIG_DATA); + printk(KERN_DEBUG "parport_pc: Parallel port base changed to 0x378\n"); + port1 = 0x378; + } + + /* + * lock super i/o configuration + */ + pci_read_config_byte(pdev, via->via_pci_superio_config_reg, &tmp); + tmp &= ~via->via_pci_superio_config_data; + pci_write_config_byte(pdev, via->via_pci_superio_config_reg, tmp); + + if (siofunc == VIA_FUNCTION_PARPORT_DISABLE) { + printk(KERN_INFO "parport_pc: VIA parallel port disabled in BIOS\n"); return 0; } - /* 0xE6: Parallel Port I/O Base Address, bits 9-2 */ - outb (0xE6, 0x3F0); - port1 = inb (0x3F1) << 2; - + /* Bits 7-4: PnP Routing for Parallel Port IRQ */ + pci_read_config_byte(pdev, via->via_pci_parport_irq_reg, &tmp); + irq = ((tmp & VIA_IRQCONTROL_PARALLEL) >> 4); + + if (siofunc == VIA_FUNCTION_PARPORT_ECP) + { + /* Bits 3-2: PnP Routing for Parallel Port DMA */ + pci_read_config_byte(pdev, via->via_pci_parport_dma_reg, &tmp); + dma = ((tmp & VIA_DMACONTROL_PARALLEL) >> 2); + } + else + /* if ECP not enabled, DMA is not enabled, assumed bogus 'dma' value */ + dma = PARPORT_DMA_NONE; + + /* Let the user (or defaults) steer us away from interrupts and DMA */ + if (autoirq == PARPORT_IRQ_NONE) { + irq = PARPORT_IRQ_NONE; + dma = PARPORT_DMA_NONE; + } + if (autodma == PARPORT_DMA_NONE) + dma = PARPORT_DMA_NONE; + switch (port1) { case 0x3bc: port2 = 0x7bc; break; case 0x378: port2 = 0x778; break; case 0x278: port2 = 0x678; break; default: - printk (KERN_INFO "parport_pc: Weird Via 686A parport base 0x%X, ignoring\n", + printk(KERN_INFO "parport_pc: Weird VIA parport base 0x%X, ignoring\n", port1); return 0; } - /* 0xF0_5: EPP+ECP enable */ - outb (0xF0, 0x3F0); - have_eppecp = (inb (0x3F1) & (1 << 5)); - - /* - * lock super i/o configuration, clear 0x85_1 - */ - pci_read_config_byte (pdev, 0x85, &tmp); - tmp &= ~(1 << 1); - pci_write_config_byte (pdev, 0x85, tmp); - - /* - * Get DMA and IRQ from PCI->ISA bridge PCI config registers - */ - - /* 0x50_3-2: PnP Routing for Parallel Port DRQ */ - pci_read_config_byte (pdev, 0x50, &tmp); - dma = ((tmp >> 2) & 0x03); - - /* 0x51_7-4: PnP Routing for Parallel Port IRQ */ - pci_read_config_byte (pdev, 0x51, &tmp); - irq = ((tmp >> 4) & 0x0F); - /* filter bogus IRQs */ switch (irq) { case 0: @@ -2559,22 +2650,10 @@ static int __devinit sio_via_686a_probe break; } - /* if ECP not enabled, DMA is not enabled, assumed bogus 'dma' value */ - if (!have_eppecp) - dma = PARPORT_DMA_NONE; - - /* Let the user (or defaults) steer us away from interrupts and DMA */ - if (autoirq != PARPORT_IRQ_AUTO) { - irq = PARPORT_IRQ_NONE; - dma = PARPORT_DMA_NONE; - } - if (autodma != PARPORT_DMA_AUTO) - dma = PARPORT_DMA_NONE; - /* finally, do the probe with values obtained */ if (parport_pc_probe_port (port1, port2, irq, dma, NULL)) { printk (KERN_INFO - "parport_pc: Via 686A parallel port: io=0x%X", port1); + "parport_pc: VIA parallel port: io=0x%X", port1); if (irq != PARPORT_IRQ_NONE) printk (", irq=%d", irq); if (dma != PARPORT_DMA_NONE) @@ -2583,7 +2662,7 @@ static int __devinit sio_via_686a_probe return 1; } - printk (KERN_WARNING "parport_pc: Strange, can't probe Via 686A parallel port: io=0x%X, irq=%d, dma=%d\n", + printk(KERN_WARNING "parport_pc: Strange, can't probe VIA parallel port: io=0x%X, irq=%d, dma=%d\n", port1, irq, dma); return 0; } @@ -2591,19 +2670,21 @@ static int __devinit sio_via_686a_probe 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); + int (*probe) (struct pci_dev *pdev, int autoirq, int autodma, struct parport_pc_via_data *via); + struct parport_pc_via_data *via; } parport_pc_superio_info[] __devinitdata = { - { sio_via_686a_probe, }, - { sio_ite_8872_probe, }, + { 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, @@ -2737,6 +2818,7 @@ static struct parport_pc_pci { static 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 }, @@ -2886,7 +2968,7 @@ static int __init parport_pc_init_superi continue; if (parport_pc_superio_info[id->driver_data].probe - (pdev, autoirq, autodma)) { + (pdev, autoirq, autodma,parport_pc_superio_info[id->driver_data].via)) { ret++; } } @@ -3072,9 +3154,27 @@ static int __init parport_parse_dma(cons PARPORT_DMA_NONE, PARPORT_DMA_NOFIFO); } +static int __init parport_init_mode_setup(const 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; +} + #ifdef MODULE static const char *irq[PARPORT_PC_MAX_PORTS]; static const char *dma[PARPORT_PC_MAX_PORTS]; +static const char *init_mode; MODULE_PARM_DESC(io, "Base I/O address (SPP regs)"); MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i"); @@ -3089,12 +3189,17 @@ MODULE_PARM(dma, "1-" __MODULE_STRING(PA MODULE_PARM_DESC(verbose_probing, "Log chit-chat during initialisation"); MODULE_PARM(verbose_probing, "i"); #endif +MODULE_PARM_DESC(init_mode, "Initialise mode for VIA VT8231 port (spp, ps2, epp, ecp or ecpepp)"); +MODULE_PARM(init_mode, "s"); static int __init parse_parport_params(void) { unsigned int i; int val; + if (init_mode) + parport_init_mode_setup(init_mode); + for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++) { if (parport_parse_irq(irq[i], &val)) return 1; @@ -3202,6 +3307,15 @@ static int __init parse_parport_params(v } __setup ("parport=", parport_setup); + +/* + * Acceptable parameters: + * + * parport_init_mode=[spp|ps2|epp|ecp|ecpepp] + */ + +__setup("parport_init_mode=",parport_init_mode_setup); + #endif /* "Parser" ends here */ diff -puN include/linux/parport_pc.h~via8231-support-for-parallel-port-driver include/linux/parport_pc.h --- 25/include/linux/parport_pc.h~via8231-support-for-parallel-port-driver Thu Oct 28 15:22:49 2004 +++ 25-akpm/include/linux/parport_pc.h Thu Oct 28 15:22:49 2004 @@ -43,6 +43,23 @@ struct parport_pc_private { struct parport *port; }; +struct parport_pc_via_data +{ + /* ISA PnP IRQ routing register 1 */ + u8 via_pci_parport_irq_reg; + /* ISA PnP DMA request routing register */ + u8 via_pci_parport_dma_reg; + /* Register and value to enable SuperIO configuration access */ + u8 via_pci_superio_config_reg; + u8 via_pci_superio_config_data; + /* SuperIO function register number */ + u8 viacfg_function; + /* parallel port control register number */ + u8 viacfg_parport_control; + /* Parallel port base address register */ + u8 viacfg_parport_base; +}; + static __inline__ void parport_pc_write_data(struct parport *p, unsigned char d) { #ifdef DEBUG_PARPORT diff -puN /dev/null include/linux/via.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/include/linux/via.h Thu Oct 28 15:22:49 2004 @@ -0,0 +1,22 @@ +/* Miscellaneous definitions for VIA chipsets + Currently used only by drivers/parport/parport_pc.c */ + +/* Values for SuperIO function select configuration register */ +#define VIA_FUNCTION_PARPORT_SPP 0x00 +#define VIA_FUNCTION_PARPORT_ECP 0x01 +#define VIA_FUNCTION_PARPORT_EPP 0x02 +#define VIA_FUNCTION_PARPORT_DISABLE 0x03 +#define VIA_FUNCTION_PROBE 0xFF /* Special magic value to be used in code, not to be written into chip */ + +/* Bits for parallel port mode configuration register */ +#define VIA_PARPORT_ECPEPP 0X20 +#define VIA_PARPORT_BIDIR 0x80 + +/* VIA configuration registers */ +#define VIA_CONFIG_INDEX 0x3F0 +#define VIA_CONFIG_DATA 0x3F1 + +/* Mask for parallel port IRQ bits (in ISA PnP IRQ routing register 1) */ +#define VIA_IRQCONTROL_PARALLEL 0xF0 +/* Mask for parallel port DMA bits (in ISA PnP DMA routing register) */ +#define VIA_DMACONTROL_PARALLEL 0x0C _