From: Alan Cox Date: Fri, 23 Nov 2007 20:11:50 +0000 (-0500) Subject: Import 2.0.35pre6 X-Git-Tag: 2.0.35pre6 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=c7bdbe4dfb3766be2556a182e3021d7af0b219a3;p=history.git Import 2.0.35pre6 --- diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 12ae5a5b38f0..6e21f9706961 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -374,6 +374,7 @@ ret_from_sys_call: jne signal_return 2: RESTORE_ALL ALIGN + .globl signal_return signal_return: movl %esp,%ecx pushl %ecx diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index d22d4df26cf9..084cd21a1f46 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -50,7 +50,7 @@ char x86_vendor_id[13] = "unknown"; unsigned char Cx86_step = 0; static const char *Cx86_type[] = { - "unknown", "1.3", "1.4", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2" + "unknown", "1.3", "1.4", "1.5", "1.6", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2" }; char ignore_irq13 = 0; /* set if exception 16 works */ diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 7c580ba6d73d..19cbf8a1c373 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -192,13 +192,36 @@ DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) DO_ERROR(18, SIGSEGV, "reserved", reserved, current) +/* signal_return is directly after ret_from_sys_call in entry.S */ +asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); +asmlinkage void signal_return(void) __asm__("signal_return"); + asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { if (regs->eflags & VM_MASK) { handle_vm86_fault((struct vm86_regs *) regs, error_code); return; } - die_if_kernel("general protection",regs,error_code); + + /* + * HACK HACK HACK :) Fixing the segment invalid on syscall return + * barfage for 2.0 has been put into the too-hard basket but having + * a user producing endless GPFs is unacceptable as well. - Paul G. + */ + if ((regs->cs & 3) != 3) { + if (regs->eip >= (unsigned long)ret_from_sys_call && + regs->eip < (unsigned long)signal_return) { + static int moancount = 0; + if (moancount < 5) { + printk(KERN_INFO "Ignoring GPF attempt from program \"%s\" (pid %d).\n", + current->comm, current->pid); + moancount++; + } + do_exit(SIGSEGV); + } + else + die_if_kernel("general protection",regs,error_code); + } current->tss.error_code = error_code; current->tss.trap_no = 13; force_sig(SIGSEGV, current); diff --git a/drivers/block/triton.c b/drivers/block/triton.c index 60002fc342db..739b1b948be2 100644 --- a/drivers/block/triton.c +++ b/drivers/block/triton.c @@ -523,5 +523,5 @@ void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, init_triton_dma(hwif1, bmiba + 0x08); return; abort: - printk("ide: Promise/33 not configured correctly (BIOS)\n"); + printk(KERN_WARNING "ide: Promise/33 not configured correctly (BIOS)\n"); } diff --git a/drivers/char/README.stallion b/drivers/char/README.stallion index 361e3a95dbe4..18b39e005977 100644 --- a/drivers/char/README.stallion +++ b/drivers/char/README.stallion @@ -1,52 +1,183 @@ -Stallion Multiport Serial Drivers ---------------------------------- +Stallion Multiport Serial Driver Readme +--------------------------------------- -Version: 1.1.3 -Date: 23APR96 -Author: Greg Ungerer (gerg@stallion.oz.au) +Copyright (C) 1994-1998, Stallion Technologies (support@stallion.com). +Version: 5.4.4 +Date: 20MAR98 -1. INTRODUCTION - -There are two drivers that work with the different families of Stallion -multiport serial boards. One is for the Stallion smart boards - that is -EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent -multiport boards - EasyConnection 8/64, ONboard, Brumby and Stallion. - -If you are using any of the Stallion intelligent multiport boards (Brumby, -ONboard, Stallion, EasyConnection 8/64) with Linux you will need to get the -driver utility package. This package is available at most of the Linux -archive sites (and on CD's that contain these archives). The file will be -called stallion-X.X.X.tar.gz where X.X.X will be the version number. In -particular this package contains the board embedded executable images that -are required for these boards. It also contains the downloader program. -These boards cannot be used without this. - -The following ftp sites (and their mirrors) definitely have the stallion -driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu. -ftp.stallion.com:/drivers/ata5/Linux/stallion-1.1.2.tar.gz -tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-1.1.2.tar.gz -sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-1.1.2.tar.gz -If you are using the EasyIO or EasyConnection 8/32 boards then you don't -need this package. Although it does have a handy script to create the -/dev device nodes for these boards, and a serial stats display program. +1. INTRODUCTION -If you require DIP switch settings, EISA/MCA configuration files, or any -other information related to Stallion boards then have a look at Stallion's -web pages at http://www.stallion.com. +This is a Linux driver for some of the Stallion Technologies range of +multiport serial boards. There are really two drivers in this package. +One is for the Stallion smart boards, the other for the true Stallion +intelligent multiport boards. + +The drivers included in this package are intended as a replacement for +those shipped with Linux kernel versions in the 2.0.X series. For later +versions of the kernel (2.1.0 and above) use the driver source supplied +with the kernel. The drivers in this package specifically add support +for the most recent releases of Stallion hardware - which are not supported +in the Stallion drivers supplied in the 2.0.X kernels. The drivers in this +package do not support kernel versions earlier than 2.0.0. + +The other utilities supplied in this package can be used with Stallion +drivers on any version of the kernel. + +If you have any trouble getting Stallion boards to work in Linux systems, +please contact Stallion Technologies support department via email or phone. +Contact information for Stallion Technologies offices is included in the +file "Offices" contained in this distribution. + +Please note the disclaimers set out in the GNU general public license +included with this driver package. + +All host driver source is included in this package, and is copyrighted under +the GNU GPL. The board "firmware" code in this package is copyright Stallion +Technologies (the files cdk.sys and 2681.sys). + + +1.1 SMART MULTIPORT BOARD DRIVER + +This driver supports the EasyIO and EasyConnection 8/32 range of boards. +These boards are not classic intelligent multiport boards, but are host +based multiport boards that use Cirrus Logic CL-CD1400 UART's, or on +newer versions of the hardware use the Signetics 26C198 UART. Both of +these are high performance UART's with built in FIFO's, automatic flow +control and a host of other features. + +The EasyIO range of cards comes in 4 forms, the EasyIO-4, EasyIO-8, +EasyIO-8M and EasyIO-8-PCI. The first three are ISA based boards while +the last is a PCI bus board. All of these are non-expandable, low cost, +multiport boards with 4 or 8 RS-232C ports. Each ISA EasyIO board requires 8 +bytes of I/O address space and 1 interrupt. The PCI EasyIO board uses 64 +bytes of I/O address space and 1 interrupt. On EISA and PCI systems it is +possible to share 1 interrupt between multiple boards. The EasyIO-4 has 10 +pin RJ connectors, and the EasyIO-8 comes with a dongle cable with either 10 +pin RJ connectors or DB-25 connectors. The EasyIO-8M has 6 pin RJ connectors. + +The EasyConnection 8/32 family of boards is a relatively low cost modular +range of multiport serial boards. The EasyConnection 8/32 boards can be +configured to have from 8 to 32 serial ports by plugging in external serial +port modules that contain either 8 or 16 ports each. There is a wide range +of external modules available that offer: DB-25 connectors, RJ-45 connectors +(both with RS-232 D and E compatible drivers), and also RS-422 and RS-485 +ports. The EasyConnection 8/32 boards come in ISA, PCI and MCA bus versions. +The board takes the form of a host adapter card, with an external connector +cable that plugs into the external modules. The external modules just clip +together to add ports (BTW, they are NOT hot pluggable). Each ISA +EasyConnection 8/32 board requires two separate I/O address ranges, one two +bytes in size and a secondary region of 32 bytes. Each PCI EasyConnection +8/32 requires two regions of I/O address space, normally these will be +automatically allocated by the system BIOS at power on time. Each MCA +EasyConnection board requires one I/O address region 64 bytes in size. All +board types also require one interrupt. On EISA systems multiple boards can +share one interrupt. The secondary I/O range of the ISA board (the 32 byte +range) can be shared between multiple boards on any bus type. + +The EasyConnection 8/64-PCI family is similar to the EasyConnection 8/32-PCI +board, and uses the same external modules. It is supported by the smart +board driver - not the intelligent board driver. It uses 2 regions of I/O +address space, both 64 bytes in size, and 1 interrupt. + + + +1.2 INTELLIGENT MULTIPORT BOARD DRIVER + +This driver is for Stallion's range of true intelligent multiport boards. +It supports the EasyConnection 8/64, ONboard and Brumby families of multiport +boards. The EasyConnection 8/64 and ONboard boards come in ISA, EISA and +Microchannel bus versions. The Brumby boards are only available in ISA +versions. This driver can also work with the original Stallion board, but +these are no longer supported by Stallion Technologies. + +The EasyConnection 8/64 family of boards is a medium cost, high performance, +modular range of intelligent multiport serial boards. The EasyConnection 8/64 +boards can be configured to have from 8 to 64 serial ports by plugging in +external serial port modules that contain either 8 or 16 ports each (these +modules are the same used by the EasyConnection 8/32 board). There is a wide +range of external modules available that offer: DB-25 connectors, RJ-45 +connectors (both with RS-232 D and E compatible drivers), and also RS-422 and +RS-485 ports. The board takes the form of a host adapter card, with an external +connector cable that plugs into the external modules. The external modules +just clip together to add ports (BTW, they are NOT hot pluggable). Each +EasyConnection 8/64 board requires 4 bytes of I/O address space and a region +of memory space. The size of the memory region required depends on the exact +board type. The EISA version requires 64 Kbytes of address space (that can +reside anywhere in the 4 Gigabyte physical address space). The ISA and MCA +boards require 4 Kbytes of address space (which must reside in the lower +1 Mbyte of physical address space - typically in the c8000 to e0000 range). +No interrupts are required. The physical memory region of multiple +EasyConnection 8/64 boards can be shared, but each board must have a separate +I/O address. + +The ONboard family of boards are traditional intelligent multiport serial +boards. They are Stallion's older range of boards with a limited expansion +capability. They come in 4, 8, 12, 16 and 32 port versions. The board uses +the same base card (which has 4 ports on it) and is expanded to more ports via +a mezzanine board that attaches directly onto the base card. External panels +plug into the ONboard providing RS-232C ports with DB-25 plugs. An RS-422 +DB-25 dual interface panel is also available. The ISA and microchannel +ONboards require 16 bytes of I/O address space and 64K bytes of memory +space. The memory space can be anywhere in the 16 Mbyte ISA bus address +range. No interrupt is required. The EISA ONboard requires 64 Kbytes of +memory space that can be anywhere in the 4 Gigabyte physical address space. +All ONboard boards can share their memory region with other ONboards (or +EasyConnection 8/64 boards). + +The Brumby family of boards are traditional, low cost intelligent multiport +serial boards. They are non-expandable and come in 4, 8 and 16 port versions. +They are only available for the ISA bus. The serial ports are all on DB-25 +"dongle" cables that attach to the rear of the board. Each Brumby board +requires 16 bytes of I/O address space and 16 Kbytes of memory space. No +interrupts are required. + +The original Stallion boards are old. They went out of production some years +back and are no longer supported. They offer limited expandability and are +available in 8 or 16 port configurations. An external panel houses 16 RS-232C +ports with DB-9 connectors. They require 16 bytes of I/O address space, and +either 64K or 128K of memory space. No interrupt is required. + +That's the boards supported by the second driver. The ONboard, Brumby and +Stallion boards are Stallion's older range of intelligent multiports - so +there are lots of them around. They only support a maximum baud rate of +38400. The EasyConnection 8/64 is a true high performance intelligent +multiport board, having much greater throughput than any of Stallion's +older boards. It also supports speeds up to 460800 baud. + + +1.3 HOW TO GET BOARDS + +Stallion Technologies has offices all over the world, as well as many more +distributors and resellers. To find out about local availability please +contact the nearest Stallion office and they can give you all the information +you need. Look in the "Offices" file in the driver package for a current list +of Stallion Technologies offices. + +Another good source of information about the Stallion range of boards and +local availability is on the Stallion Web page. Check it out at +http://www.stallion.com. 2. INSTALLATION +This version of the driver is intended for kernel versions 2.0.0 and later. +It will not work on earlier kernel versions, due to kernel interface changes. +(Note that older versions of these drivers do work on older kernels.) +If you are using a more recent development kernel (versions 2.1.X and +greater) you should use the Stallion drivers supplied with that kernel, +they are more up to date. + The drivers can be used as loadable modules or compiled into the kernel. -You can choose which when doing a "make config" on the kernel. +Depending on which form of driver loading you decide to use, the installation +procedure will be a little different. All ISA, EISA and MCA boards that you want to use need to be entered into -the driver(s) configuration structures. All PCI boards will be automatically +the driver(s) configuration structures. PCI boards will be automatically detected when you load the driver - so they do not need to be entered into the driver(s) configuration structure. (Note that kernel PCI BIOS32 support is required to use PCI boards.) @@ -54,45 +185,135 @@ is required to use PCI boards.) Entering ISA, EISA and MCA boards into the driver(s) configuration structure involves editing the driver(s) source file. It's pretty easy if you follow the instructions below. Both drivers can support up to 4 boards. The smart -card driver (the stallion.c driver) supports any combination of EasyIO and -EasyConnection 8/32 boards (up to a total of 4). The intelligent driver -supports any combination of ONboards, Brumbys, Stallions and EasyConnection -8/64 boards (up to a total of 4). +card driver supports any combination of EasyIO, EasyConnection 8/32 and +EasyConnection 8/64-PCI boards (up to a total of 4). The intelligent driver +supports any combination of ONboards, Brumbys, Stallions and +EasyConnection 8/64 boards (up to a total of 4). + -To set up the driver(s) for the boards that you want to use you need to -edit the appropriate driver file and add configuration entries. +2.1 LOADABLE MODULE DRIVERS -If using EasyIO or EasyConnection 8/32 ISA or MCA boards, do: +You will need the gcc compiler and make installed on your system to make the +driver modules. You will also need to have the kernel source on the system, +and have at least done a "make config" and "make dep" on it. (If you haven't +done this before then you may want to read the kernel source README file, +usually found in /usr/src/linux.) + +To build the driver modules: +1. Setup the driver configuration for the boards. If using EasyIO or + EasyConnection 8/32 ISA or MCA boards, do: vi stallion.c - find the definition of the stl_brdconf array (of structures) near the top of the file - modify this to match the boards you are going to install (the comments before this structure should help) - save and exit - -If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do: + If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do: vi istallion.c - find the definition of the stli_brdconf array (of structures) near the top of the file - modify this to match the boards you are going to install (the comments before this structure should help) - save and exit +2. cp stallion.h cd1400.h sc26198.h /usr/include/linux/include/linux + cp istallion.h cdk.h comstats.h /usr/include/linux/include/linux +3. make modules + This will compile the driver modules, as stallion and istallion. + +The stallion module is the EasyIO, EasyConnection 8/32 and +EasyConnection 8/64-PCI driver, the istallion module is the ONboard, +Brumby, Stallion and EasyConnection 8/64 driver. + +To load up the smart board driver use: + insmod ./stallion +This will load the EasyIO and EasyConnection 8/32 driver. It will output a +message to say that it loaded and print the driver version number. It +will also print out whether it found the configured boards or not. (These +messages may appear in your /var/adm/messages file depending on how the +klogd and syslogd daemons are setup on your system). + +To load the intelligent board driver use: + insmod ./istallion +It will output similar messages to the smart board driver. + + +2.2 STATIC DRIVERS (KERNEL LINKED) + +You will need to build a new kernel to link in the Stallion drivers. The first +thing you need is to have the full kernel source. Most people will have this. +The following assumes that the kernel source is in /usr/src/linux. + +To install the drivers: +1. cp stallion.c istallion.c /usr/src/linux/drivers/char + cp stallion.h cd1400.h sc26198.h /usr/include/linux/include/linux + cp istallion.h cdk.h comstats.h /usr/include/linux/include/linux +2. cd /usr/src/linux/drivers/char +3. Setup the driver configuration for the boards. If using EasyIO, + EasyConnection 8/32 or EasyConnection 8/64-PCI boards, do: + vi stallion.c + - find the definition of the stl_brdconf array (of structures) + near the top of the file + - modify this to match the boards you are going to install + (the comments before this structure should help) + - save and exit + If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do: + vi istallion.c + - find the definition of the stli_brdconf array (of structures) + near the top of the file + - modify this to match the boards you are going to install + (the comments before this structure should help) + - save and exit +4. cd /usr/src/linux +5. build a new kernel - if you haven't done this before you may want to + read the README file in /usr/src/linux. + +Once you have a new kernel built, reboot to start it up. On startup the +driver will output a message to say it is operational (with the driver +version number). It will also print out if it could find the boards listed +in its configuration structure or not. -Once you have set up the board configurations then you are ready to build -the kernel or modules. -When the new kernel is booted, or the loadable module loaded then the -driver will emit some kernel trace messages about whether the configured -boards where detected or not. Depending on how your system logger is set -up these may come out on the console, or just be logged to -/var/adm/messages. You should check the messages to confirm that all is well. +2.3 INTELLIGENT DRIVER OPERATION + +The intelligent boards also need to have their "firmware" code downloaded +to them. This is done via a user level application supplied in the driver +package called "stlload". Compile this program where ever you dropped the +package files, by typing "make". In its simplest form you can then type + ./stlload -i cdk.sys +in this directory and that will download board 0 (assuming board 0 is an +EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do: + ./stlload -i 2681.sys +Normally you would want all boards to be downloaded as part of the standard +system startup. To achieve this, add one of the lines above into the +/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add +the "-b " option to the line. You will need to download code for +every board. You should probably move the stlload program into a system +directory, such as /usr/sbin. Also, the default location of the cdk.sys image +file in the stlload down-loader is /usr/lib/stallion. Create that directory +and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put +them anyway). As an example your /etc/rc.d/rc.S file might have the +following lines added to it (if you had 3 boards): + /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys + /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys + /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys -2.1 SHARING INTERRUPTS +The image files cdk.sys and 2681.sys are specific to the board types. The +cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly +the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards. +If you load the wrong image file into a board it will fail to start up, and +of course the ports will not be operational! -It is possible to share interrupts between multiple EasyIO and -EasyConnection 8/32 boards in an EISA system. To do this you will need to -do a couple of things: +If you are using the module version of the driver you might want to put the +insmod calls in the startup script as well (before the download lines +obviously). + + +2.4 SHARING INTERRUPTS + +As mentioned in the introduction, it is possible to share interrupts between +multiple EasyIO and EasyConnection 8/32 boards in an EISA system. To do this +you will need to do a couple of things: 1. When entering the board resources into the stallion.c file you need to mark the boards as using level triggered interrupts. Do this by replacing @@ -109,17 +330,17 @@ do a couple of things: that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32 EISA configuration files required are supplied by Stallion Technologies on the DOS Utilities floppy (usually supplied in the box with the board - when purchased. If not, you can pick it up from Stallion's FTP site, - ftp.stallion.com). You will need to edit the board resources to choose - level triggered interrupts, and make sure to set each board's interrupt - to the same IRQ number. + when purchased. If not, you can pick it up from Stallion's FTP site + ftp.stallion.com or web site http://www.stallion.com). You will need to + edit the board resources to choose level triggered interrupts, and make + sure to set each board's interrupt to the same IRQ number. You must complete both the above steps for this to work. When you reboot or load the driver your EasyIO and EasyConnection 8/32 boards will be sharing interrupts. -2.2 USING HIGH SHARED MEMORY +2.5 USING HIGH SHARED MEMORY The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of using shared memory addresses above the usual 640K - 1Mb range. The ONboard @@ -128,75 +349,52 @@ ISA and the Stallion boards can be programmed to use memory addresses up to ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus addressing limit). -The higher than 1Mb memory addresses are fully supported by this driver. -Just enter the address as you normally would for a lower than 1Mb address -(in the drivers board configuration structure). +The istallion driver offers direct support for these higher memory regions. +To use them just enter the high memory address as if it were a low memory +address (in the driver board configuration structure). + +2.6 LINUX KERNEL VERSIONS 2.1.X +There may be some minor differences between the driver source code in this +package and that in the Linux kernel source. This will be due to changes +needed in the drivers so that they work correctly on newer kernels. The +driver source included in this package is intended for use with 2.0.X +series kernels. If you have a kernel version 2.1.0 or later then use the +source provided with the kernel - it will be more up to date. Stallion +Technologies regularly submits the latest driver source to be included in +the new kernel source releases. -2.3 TROUBLE SHOOTING + +2.7 TROUBLE SHOOTING If a board is not found by the driver but is actually in the system then the most likely problem is that the I/O address is wrong. Change it in the driver -stallion.c or istallion.c configuration structure and rebuild the kernel or -modules, or change it on the board. On EasyIO and EasyConnection 8/32 boards -the IRQ is software programmable, so if there is a conflict you may need to -change the IRQ used for a board in the stallion.c configuration structure. -There are no interrupts to worry about for ONboard, Brumby, Stallion or -EasyConnection 8/64 boards. The memory region on EasyConnection 8/64 and -ONboard boards is software programmable, but not on the Brumbys or Stallions. +stallion.c or istallion.c configuration structure and rebuild the kernel +or modules, or change it on the board. On EasyIO and EasyConnection 8/32 +boards the IRQ is software programmable, so if there is a conflict you may +need to change the IRQ used for a board in the stallion.c configuration +structure. There are no interrupts to worry about for ONboard, Brumby, +Stallion or EasyConnection 8/64 boards. The memory region on EasyConnection +8/64 and ONboard boards is software programmable, but not on the Brumbys or +Stallions. 3. USING THE DRIVERS -3.1 INTELLIGENT DRIVER OPERATION - -The intelligent boards also need to have their "firmware" code downloaded -to them. This is done via a user level application supplied in the driver -package called "stlload". Compile this program where ever you dropped the -package files, by typing "make". In its simplest form you can then type - ./stlload -i cdk.sys -in this directory and that will download board 0 (assuming board 0 is an -EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do: - ./stlload -i 2681.sys - -Normally you would want all boards to be downloaded as part of the standard -system startup. To achieve this, add one of the lines above into the -/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add -the "-b " option to the line. You will need to download code for -every board. You should probably move the stlload program into a system -directory, such as /usr/sbin. Also, the default location of the cdk.sys image -file in the stlload down-loader is /usr/lib/stallion. Create that directory -and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put -them anyway). As an example your /etc/rc.d/rc.S file might have the -following lines added to it (if you had 3 boards): - /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys - /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys - /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys - -The image files cdk.sys and 2681.sys are specific to the board types. The -cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly -the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards. -If you load the wrong image file into a board it will fail to start up, and -of course the ports will not be operational! - -If you are using the modularized version of the driver you might want to put -the insmod calls in the startup script as well (before the download lines -obviously). - - -3.2 USING THE SERIAL PORTS - Once the driver is installed you will need to setup some device nodes to -access the serial ports. The simplest method is to use the stallion utility -"mkdevnods" script. It will automatically create all possible device entries -required for all 4 boards. This will create the normal serial port devices as -/dev/ttyE# where # is the port number starting from 0. A bank of 64 minor -device numbers is allocated to each board, so the first port on the second -board is port 64, etc. A set of callout type devices is also created. They -are created as the devices /dev/cue# where # is the same as for the ttyE -devices. +access the serial ports. Use the supplied "mkdevnods" script to automatically +create all required device entries for one board. This will create the normal +serial port devices as /dev/ttyE# where # is the port number starting from 0. +A set of callout type devices is also created. They are created as the devices +/dev/cue# where # is the same as for the ttyE devices. + +A bank of 64 minor device numbers is allocated to each board. To create +device nodes for ports on multiple boards supply a number of boards argument +to the "mkdevnods" script. For example to create nodes for four boards use +"mkdevnods 4". This means that the first port on the second board is port 64, +the first port on the third board is 128, etc. For the most part the Stallion driver tries to emulate the standard PC system COM ports and the standard Linux serial driver. The idea is that you should @@ -214,9 +412,18 @@ COM ports. Most importantly "stty" works as expected and "setserial" can be also be used (excepting the ability to auto-configure the I/O and IRQ addresses of boards). Higher baud rates are supported in the usual fashion through setserial or using the CBAUDEX extensions. Note that the EasyIO and -EasyConnection (all types) support 57600 and 115200 baud. The older boards -including ONboard, Brumby and the original Stallion support a maximum baud -rate of 38400. +EasyConnection (all types) support 57600 and 115200 baud, and the newer XP +versions also support 230400 and 460800 baud. The older boards including +ONboard, Brumby and the original Stallion support a maximum baud rate of +38400. + +This driver should work with anything that works on standard Linux serial +ports. Having said that, it has been used on at least the following types of +"things" under Linux: + a) standard dumb terminals (using agetty, getty) + b) serial mice (under X) + c) modems (using cu, uucp, minicom, seyon, uugetty) + d) slip and ppp connections If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO by Greg Hankins. It will explain everything you need to know! @@ -225,6 +432,11 @@ by Greg Hankins. It will explain everything you need to know! 4. NOTES +The major device numbers used by this driver are conformant with the Linux +Device Registry, so they shouldn't clash with any other devices. Also the +device naming scheme is the "standard" used by most Linux serial port +devices. + You can use both drivers at once if you have a mix of board types installed in a system. However to do this you will need to change the major numbers used by one of the drivers. Currently both drivers use major numbers 24, 25 @@ -234,20 +446,17 @@ major numbers. For example, you could change the istallion.c driver to use major numbers 60, 61 and 62. You will also need to create device nodes with different names for the ports, for example ttyF# and cuf#. -The original Stallion board is no longer supported by Stallion Technologies. -Although it is known to work with the istallion driver. - Finding a free physical memory address range can be a problem. The older boards like the Stallion and ONboard need large areas (64K or even 128K), so they can be very difficult to get into a system. If you have 16 Mb of RAM then you have no choice but to put them somewhere in the 640K -> 1Mb range. ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some -systems. If you have an original Stallion board, "V4.0" or Rev.O, then you -need a 64K memory address space, so again 0xd0000 and 0xe0000 are good. -Older Stallion boards are a much bigger problem. They need 128K of address -space and must be on a 128K boundary. If you don't have a VGA card then -0xc0000 might be usable - there is really no other place you can put them -below 1Mb. +systems. If you have an original Stallion board, "V4.0" or Rev.O, +then you need a 64K memory address space, so again 0xd0000 and 0xe0000 are +good. Older Stallion boards are a much bigger problem. They need 128K of +address space and must be on a 128K boundary. If you don't have a VGA card +then 0xc0000 might be usable - there is really no other place you can put +them below 1Mb. Both the ONboard and old Stallion boards can use higher memory addresses as well, but you must have less than 16Mb of RAM to be able to use them. Usual @@ -265,19 +474,48 @@ them can be used then the high memory support to use the really high address ranges is the best option. Typically the 2Gb range is convenient for them, and gets them well out of the way. +There is a new utility program included called "stlstty". Most people +will not need to use this. If you have an ONboard/16 which has partial +signals on the upper 12 ports then this program can be used to set the +upper ports to have modem control instead of hardware flow control. Use +the "mapcts maprts" flag options to this utility on the port(s) that you +wish to do this mapping on, eg + ./stlstty maprts mapcts < /dev/cue0 +This enables RTS to act like DTR and CTS to act like DCD on the specified +port. + The ports of the EasyIO-8M board do not have DCD or DTR signals. So these -ports cannot be used as real modem devices. Generally, when using these +ports cannot be used as real modem devices. Generally when using these ports you should only use the cueX devices. -The driver utility package contains a couple of very useful programs. One -is a serial port statistics collection and display program - very handy -for solving serial port problems. The other is an extended option setting -program that works with the intelligent boards. +There is another new utility in this package that reports statistics on +the serial ports. You will need to have the curses libray installed on +your system to build it. + +To build the statistics display program type: + make stlstats +Once compiled simply run it (you will need to be root) and it will display +a port sumary for the first board and panel installed. Use the digits to +select different board numbers, or 'n' to cycle through the panels on a +board. To look at detailed port information then hit 'p', that will display +detailed port 0 information. Use the digits and letters 'a' through 'f' to +select the different ports (on this board and panel). + + + +5. ACKNOWLEDGEMENTS + +This driver is loosely based on code written by Theodore T'so, Linus +Torvalds, and others, so a big thanks to them all. -5. DISCLAIMER +6. DISCLAIMER -I do not speak for Stallion Technologies in any capacity, officially or -unofficially. +The information contained in this document is believed to be accurate and +reliable. However, no responsibility is assumed by Stallion Technologies +Pty. Ltd. for its use, nor any infringements of patents or other rights +of third parties resulting from its use. Stallion Technologies reserves +the right to modify the design of its products and will endeavour to change +the information in manuals and accompanying documentation accordingly. diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 2cc26a90f576..5773ba4a8757 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -3,6 +3,7 @@ /* * istallion.c -- stallion intelligent multiport serial driver. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This code is loosely based on the Linux serial driver, written by @@ -73,6 +74,8 @@ #define BRD_ECPE 24 #define BRD_ECPMC 25 #define BRD_ECHPCI 26 +#define BRD_ECH64PCI 27 +#define BRD_EASYIOPCI 28 #define BRD_BRUMBY BRD_BRUMBY4 @@ -90,6 +93,12 @@ * boards can share the same shared memory address space. No interrupt * is required for this board type. * Another example: + * { BRD_ECPE, 0x5000, 0, 0x80000000, 0, 0 }, + * This line will configure an EasyConnection 8/64 EISA in slot 5 and + * shared memory address of 0x80000000 (2 GByte). Multiple + * EasyConnection 8/64 EISA boards can share the same shared memory + * address space. No interrupt is required for this board type. + * Another example: * { BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 }, * This line will configure an ONboard (ISA type) at io address 240, * and shared memory address of d0000. Multiple ONboards can share @@ -156,8 +165,8 @@ static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t); * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ -static char *stli_drvname = "Stallion Intelligent Multiport Serial Driver"; -static char *stli_drvversion = "1.1.3"; +static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; +static char *stli_drvversion = "5.4.4"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -281,6 +290,8 @@ static char *stli_brdnames[] = { "EC8/64-EI", "EC8/64-MC", "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /* @@ -436,6 +447,7 @@ int stli_eisaprobe = STLI_EISAPROBE; #define ECH_PNLSTATUS 2 #define ECH_PNL16PORT 0x20 #define ECH_PNLIDMASK 0x07 +#define ECH_PNLXPID 0x40 #define ECH_PNLINTRPEND 0x80 /* @@ -473,9 +485,9 @@ int stli_eisaprobe = STLI_EISAPROBE; /* * Define the maximal baud rate, and the default baud base for ports. */ -#define STL_MAXBAUD 230400 +#define STL_MAXBAUD 460800 #define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +#define STL_CLOSEDELAY (5 * HZ / 10) /*****************************************************************************/ @@ -492,7 +504,7 @@ int stli_eisaprobe = STLI_EISAPROBE; */ static unsigned int stli_baudrates[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400 + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 }; /*****************************************************************************/ @@ -530,19 +542,16 @@ static void stli_start(struct tty_struct *tty); static void stli_flushbuffer(struct tty_struct *tty); static void stli_hangup(struct tty_struct *tty); -static int stli_initbrds(void); static int stli_brdinit(stlibrd_t *brdp); -static int stli_initecp(stlibrd_t *brdp); -static int stli_initonb(stlibrd_t *brdp); -static int stli_eisamemprobe(stlibrd_t *brdp); -static int stli_findeisabrds(void); -static int stli_initports(stlibrd_t *brdp); static int stli_startbrd(stlibrd_t *brdp); +static int stli_memopen(struct inode *ip, struct file *fp); +static void stli_memclose(struct inode *ip, struct file *fp); static int stli_memread(struct inode *ip, struct file *fp, char *buf, int count); static int stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int count); static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); +static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp); static void stli_poll(unsigned long arg); -static int stli_hostcmd(stlibrd_t *brdp, int channr); +static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp); static int stli_initopen(stlibrd_t *brdp, stliport_t *portp); static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); @@ -561,6 +570,7 @@ static void stli_getserial(stliport_t *portp, struct serial_struct *sp); static int stli_setserial(stliport_t *portp, struct serial_struct *sp); static int stli_getbrdstats(combrd_t *bp); static int stli_getportstats(stliport_t *portp, comstats_t *cp); +static int stli_portcmdstats(stliport_t *portp); static int stli_clrportstats(stliport_t *portp, comstats_t *cp); static int stli_getportstruct(unsigned long arg); static int stli_getbrdstruct(unsigned long arg); @@ -601,6 +611,13 @@ static void stli_stalreset(stlibrd_t *brdp); static stliport_t *stli_getport(int brdnr, int panelnr, int portnr); +static inline int stli_initbrds(void); +static inline int stli_initecp(stlibrd_t *brdp); +static inline int stli_initonb(stlibrd_t *brdp); +static inline int stli_findeisabrds(void); +static inline int stli_eisamemprobe(stlibrd_t *brdp); +static inline int stli_initports(stlibrd_t *brdp); + /*****************************************************************************/ /* @@ -617,8 +634,8 @@ static struct file_operations stli_fsiomem = { NULL, stli_memioctl, NULL, - NULL, - NULL, + stli_memopen, + stli_memclose, NULL }; @@ -678,7 +695,8 @@ void cleanup_module() printk("cleanup_module()\n"); #endif - printk(KERN_INFO "Unloading %s: version %s\n", stli_drvname, stli_drvversion); + printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle, + stli_drvversion); save_flags(flags); cli(); @@ -695,12 +713,14 @@ void cleanup_module() i = tty_unregister_driver(&stli_serial); j = tty_unregister_driver(&stli_callout); if (i || j) { - printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j); + printk("STALLION: failed to un-register tty driver, " + "errno=%d,%d\n", -i, -j); restore_flags(flags); return; } if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) - printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i); + printk("STALLION: failed to un-register serial memory device, " + "errno=%d\n", -i); if (stli_tmpwritebuf != (char *) NULL) kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE); @@ -722,10 +742,8 @@ void cleanup_module() if (brdp->memaddr >= 0x100000) vfree(brdp->membase); - if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC)) - release_region(brdp->iobase, ECP_IOSIZE); - else - release_region(brdp->iobase, ONB_IOSIZE); + if (brdp->iosize > 0) + release_region(brdp->iobase, brdp->iosize); kfree_s(brdp, sizeof(stlibrd_t)); stli_brds[i] = (stlibrd_t *) NULL; } @@ -756,7 +774,8 @@ static int stli_open(struct tty_struct *tty, struct file *filp) int brdnr, portnr, rc; #if DEBUG - printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device); + printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, + (int) filp, tty->device); #endif minordev = MINOR(tty->device); @@ -778,6 +797,8 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if (portp->devnr < 1) return(-ENODEV); + MOD_INC_USE_COUNT; + /* * Check if this port is in the middle of closing. If so then wait * until it is closed then return error status based on flag settings. @@ -842,10 +863,10 @@ static int stli_open(struct tty_struct *tty, struct file *filp) return(-EBUSY); if (portp->flags & ASYNC_CALLOUT_ACTIVE) { if ((portp->flags & ASYNC_SESSION_LOCKOUT) && - (portp->session != current->session)) + (portp->session != current->session)) return(-EBUSY); if ((portp->flags & ASYNC_PGRP_LOCKOUT) && - (portp->pgrp != current->pgrp)) + (portp->pgrp != current->pgrp)) return(-EBUSY); } portp->flags |= ASYNC_CALLOUT_ACTIVE; @@ -892,10 +913,14 @@ static void stli_close(struct tty_struct *tty, struct file *filp) save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } + if ((tty->count == 1) && (portp->refcount != 1)) + portp->refcount = 1; if (portp->refcount-- > 1) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -916,10 +941,8 @@ static void stli_close(struct tty_struct *tty, struct file *filp) if (tty == stli_txcooktty) stli_flushchars(tty); tty->closing = 1; - if (test_bit(ST_TXBUSY, &portp->state)) { - if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, portp->closing_wait); - } + if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, portp->closing_wait); portp->flags &= ~ASYNC_INITIALIZED; brdp = stli_brds[portp->brdnr]; @@ -929,7 +952,8 @@ static void stli_close(struct tty_struct *tty, struct file *filp) if (test_bit(ST_CMDING, &portp->state)) set_bit(ST_DOSIGS, &portp->state); else - stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0); } clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); @@ -940,7 +964,6 @@ static void stli_close(struct tty_struct *tty, struct file *filp) stli_flushbuffer(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -949,8 +972,10 @@ static void stli_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&portp->open_wait); } - portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | + ASYNC_CLOSING); wake_up_interruptible(&portp->close_wait); + MOD_DEC_USE_COUNT; restore_flags(flags); } @@ -981,23 +1006,27 @@ static int stli_initopen(stlibrd_t *brdp, stliport_t *portp) memset(&nt, 0, sizeof(asynotify_t)); nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK); nt.signal = SG_DCD; - if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, + sizeof(asynotify_t), 0)) < 0) return(rc); tty = portp->tty; if (tty == (struct tty_struct *) NULL) return(-ENODEV); stli_mkasyport(portp, &aport, tty->termios); - if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, + sizeof(asyport_t), 0)) < 0) return(rc); set_bit(ST_GETSIGS, &portp->state); - if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, + sizeof(asysigs_t), 1)) < 0) return(rc); if (clear_bit(ST_GETSIGS, &portp->state)) portp->sigs = stli_mktiocm(portp->asig.sigvalue); stli_mkasysigs(&portp->asig, 1, 1); - if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0)) < 0) return(rc); return(0); @@ -1021,7 +1050,8 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i int rc; #if DEBUG - printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); + printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", + (int) brdp, (int) portp, (int) arg, wait); #endif /* @@ -1054,8 +1084,8 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i cp->openarg = arg; cp->open = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1101,7 +1131,8 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int rc; #if DEBUG - printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); + printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", + (int) brdp, (int) portp, (int) arg, wait); #endif save_flags(flags); @@ -1129,8 +1160,8 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, cp->closearg = arg; cp->close = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1173,7 +1204,9 @@ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, v unsigned long flags; #if DEBUG - printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); + printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," + "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, + (int) arg, size, copyback); #endif save_flags(flags); @@ -1243,7 +1276,7 @@ static int stli_setport(stliport_t *portp) static void stli_delay(int len) { #if DEBUG - printk("stl_delay(len=%d)\n", len); + printk("stli_delay(len=%d)\n", len); #endif if (len > 0) { current->state = TASK_INTERRUPTIBLE; @@ -1265,7 +1298,8 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil int rc, doclocal; #if DEBUG - printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", (int) brdp, (int) portp, (int) filp); + printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", + (int) brdp, (int) portp, (int) filp); #endif rc = 0; @@ -1282,16 +1316,18 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil save_flags(flags); cli(); portp->openwaitcnt++; - if (portp->refcount > 0) + if (! tty_hung_up_p(filp)) portp->refcount--; for (;;) { if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) { stli_mkasysigs(&portp->asig, 1, 1); - if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0)) < 0) break; } - if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) { + if (tty_hung_up_p(filp) || + ((portp->flags & ASYNC_INITIALIZED) == 0)) { if (portp->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else @@ -1299,8 +1335,8 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil break; } if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) && - ((portp->flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) { + ((portp->flags & ASYNC_CLOSING) == 0) && + (doclocal || (portp->sigs & TIOCM_CD))) { break; } if (current->signal & ~current->blocked) { @@ -1338,10 +1374,12 @@ static int stli_write(struct tty_struct *tty, int from_user, const unsigned char unsigned long flags; #if DEBUG - printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count); + printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", + (int) tty, from_user, (int) buf, count); #endif - if ((tty == (struct tty_struct *) NULL) || (stli_tmpwritebuf == (char *) NULL)) + if ((tty == (struct tty_struct *) NULL) || + (stli_tmpwritebuf == (char *) NULL)) return(0); if (tty == stli_txcooktty) stli_flushchars(tty); @@ -1375,7 +1413,8 @@ static int stli_write(struct tty_struct *tty, int from_user, const unsigned char tail = (unsigned int) ap->txq.tail; if (tail != ((unsigned int) ap->txq.tail)) tail = (unsigned int) ap->txq.tail; - len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : (tail - head - 1); + len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : + (tail - head - 1); count = MIN(len, count); EBRDDISABLE(brdp); @@ -1430,8 +1469,8 @@ static int stli_write(struct tty_struct *tty, int from_user, const unsigned char ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_TXBUSY, &portp->state); @@ -1562,8 +1601,8 @@ static void stli_flushchars(struct tty_struct *tty) ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_TXBUSY, &portp->state); @@ -1729,12 +1768,14 @@ static int stli_setserial(stliport_t *portp, struct serial_struct *sp) memcpy_fromfs(&sio, sp, sizeof(struct serial_struct)); if (!suser()) { if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK))) + (sio.close_delay != portp->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->flags & ~ASYNC_USR_MASK))) return(-EPERM); } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); + portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; @@ -1755,7 +1796,8 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm int rc; #if DEBUG - printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg); + printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", + (int) tty, (int) file, cmd, (int) arg); #endif if (tty == (struct tty_struct *) NULL) @@ -1769,6 +1811,12 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm if (brdp == (stlibrd_t *) NULL) return(0); + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return(-EIO); + } + rc = 0; switch (cmd) { @@ -1777,7 +1825,8 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm tty_wait_until_sent(tty, 0); if (! arg) { val = 250; - rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0); + rc = stli_cmdwait(brdp, portp, A_BREAK, &val, + sizeof(unsigned long), 0); } } break; @@ -1785,72 +1834,98 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm if ((rc = tty_check_change(tty)) == 0) { tty_wait_until_sent(tty, 0); val = (arg ? (arg * 100) : 250); - rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0); + rc = stli_cmdwait(brdp, portp, A_BREAK, &val, + sizeof(unsigned long), 0); } break; case TIOCGSOFTCAR: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0) - put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(long))) == 0) + put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), + (unsigned long *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0); } break; case TIOCMGET: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) { - if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { + if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, + &portp->asig, sizeof(asysigs_t), 1)) < 0) return(rc); val = stli_mktiocm(portp->asig.sigvalue); put_fs_long(val, (unsigned long *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((arg & TIOCM_DTR) ? 1 : -1), + ((arg & TIOCM_RTS) ? 1 : -1)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCMBIC: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((arg & TIOCM_DTR) ? 0 : -1), + ((arg & TIOCM_RTS) ? 0 : -1)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCMSET: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((arg & TIOCM_DTR) ? 1 : 0), + ((arg & TIOCM_RTS) ? 1 : 0)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) stli_getserial(portp, (struct serial_struct *) arg); break; case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) - rc = stli_setserial(portp, (struct serial_struct *) arg); + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) + rc = stli_setserial(portp, (struct serial_struct *)arg); break; case STL_GETPFLAG: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned long))) == 0) put_fs_long(portp->pflag, (unsigned long *) arg); break; case STL_SETPFLAG: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned long))) == 0) { portp->pflag = get_fs_long((unsigned long *) arg); stli_setport(portp); } break; case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stli_getportstats(portp, (comstats_t *) arg); break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stli_clrportstats(portp, (comstats_t *) arg); break; case TIOCSERCONFIG: @@ -1898,13 +1973,15 @@ static void stli_settermios(struct tty_struct *tty, struct termios *old) return; tiosp = tty->termios; - if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag)) + if ((tiosp->c_cflag == old->c_cflag) && + (tiosp->c_iflag == old->c_iflag)) return; stli_mkasyport(portp, &aport, tiosp); stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0); stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1); - stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0); if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) tty->hw_stopped = 0; if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) @@ -1996,7 +2073,7 @@ static void stli_stop(struct tty_struct *tty) memset(&actrl, 0, sizeof(asyctrl_t)); actrl.txctrl = CT_STOPFLOW; #if 0 - stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t)); + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); #endif } @@ -2030,7 +2107,7 @@ static void stli_start(struct tty_struct *tty) memset(&actrl, 0, sizeof(asyctrl_t)); actrl.txctrl = CT_STARTFLOW; #if 0 - stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t)); + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); #endif } @@ -2104,7 +2181,8 @@ static void stli_hangup(struct tty_struct *tty) set_bit(ST_DOFLUSHTX, &portp->state); set_bit(ST_DOFLUSHRX, &portp->state); } else { - stli_sendcmd(brdp, portp, A_SETSIGNALSF, &portp->asig, sizeof(asysigs_t), 0); + stli_sendcmd(brdp, portp, A_SETSIGNALSF, + &portp->asig, sizeof(asysigs_t), 0); } } restore_flags(flags); @@ -2112,7 +2190,6 @@ static void stli_hangup(struct tty_struct *tty) clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); portp->refcount = 0; @@ -2164,12 +2241,14 @@ static void stli_flushbuffer(struct tty_struct *tty) ftype |= FLUSHRX; clear_bit(ST_DOFLUSHRX, &portp->state); } - stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0); + stli_sendcmd(brdp, portp, A_FLUSH, &ftype, + sizeof(unsigned long), 0); } restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); } @@ -2193,7 +2272,9 @@ static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, unsigned long flags; #if DEBUG - printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); + printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," + "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, + (int) arg, size, copyback); #endif save_flags(flags); @@ -2217,8 +2298,8 @@ static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, cp->status = 0; cp->cmd = cmd; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_CMDING, &portp->state); EBRDDISABLE(brdp); @@ -2307,7 +2388,8 @@ static inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp) int cmd; if (test_bit(ST_DOSIGS, &portp->state)) { - if (test_bit(ST_DOFLUSHTX, &portp->state) && test_bit(ST_DOFLUSHRX, &portp->state)) + if (test_bit(ST_DOFLUSHTX, &portp->state) && + test_bit(ST_DOFLUSHRX, &portp->state)) cmd = A_SETSIGNALSF; else if (test_bit(ST_DOFLUSHTX, &portp->state)) cmd = A_SETSIGNALSFTX; @@ -2318,11 +2400,13 @@ static inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp) clear_bit(ST_DOFLUSHTX, &portp->state); clear_bit(ST_DOFLUSHRX, &portp->state); clear_bit(ST_DOSIGS, &portp->state); - memcpy((void *) &(cp->args[0]), (void *) &portp->asig, sizeof(asysigs_t)); + memcpy((void *) &(cp->args[0]), (void *) &portp->asig, + sizeof(asysigs_t)); cp->status = 0; cp->cmd = cmd; set_bit(ST_CMDING, &portp->state); - } else if (test_bit(ST_DOFLUSHTX, &portp->state) || test_bit(ST_DOFLUSHRX, &portp->state)) { + } else if (test_bit(ST_DOFLUSHTX, &portp->state) || + test_bit(ST_DOFLUSHRX, &portp->state)) { cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0); cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0); clear_bit(ST_DOFLUSHTX, &portp->state); @@ -2342,15 +2426,17 @@ static inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp) * enabled and interrupts off when called. Notice that by servicing the * read data last we don't need to change the shared memory pointer * during processing (which is a slow IO operation). + * Return value indicates if this port is still awaiting actions from + * the slave (like open, command, or even TX data being sent). If 0 + * then port is still busy, otherwise no longer busy. */ -static inline int stli_hostcmd(stlibrd_t *brdp, int channr) +static inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp) { volatile cdkasy_t *ap; volatile cdkctrl_t *cp; struct tty_struct *tty; asynotify_t nt; - stliport_t *portp; unsigned long oldsigs; int rc, donerx; @@ -2358,7 +2444,6 @@ static inline int stli_hostcmd(stlibrd_t *brdp, int channr) printk("stli_hostcmd(brdp=%x,channr=%d)\n", (int) brdp, channr); #endif - portp = brdp->ports[(channr - 1)]; ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); cp = &ap->ctrl; @@ -2402,7 +2487,8 @@ static inline int stli_hostcmd(stlibrd_t *brdp, int channr) if (rc > 0) rc--; if (portp->argp != (void *) NULL) { - memcpy(portp->argp, (void *) &(cp->args[0]), portp->argsize); + memcpy(portp->argp, (void *) &(cp->args[0]), + portp->argsize); portp->argp = (void *) NULL; } cp->status = 0; @@ -2429,12 +2515,14 @@ static inline int stli_hostcmd(stlibrd_t *brdp, int channr) oldsigs = portp->sigs; portp->sigs = stli_mktiocm(nt.sigvalue); clear_bit(ST_GETSIGS, &portp->state); - if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) + if ((portp->sigs & TIOCM_CD) && + ((oldsigs & TIOCM_CD) == 0)) wake_up_interruptible(&portp->open_wait); - if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { + if ((oldsigs & TIOCM_CD) && + ((portp->sigs & TIOCM_CD) == 0)) { if (portp->flags & ASYNC_CHECK_CD) { if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) && - (portp->flags & ASYNC_CALLOUT_NOHUP))) { + (portp->flags & ASYNC_CALLOUT_NOHUP))) { if (tty != (struct tty_struct *) NULL) queue_task_irq_off(&portp->tqhangup, &tq_scheduler); } @@ -2446,7 +2534,8 @@ static inline int stli_hostcmd(stlibrd_t *brdp, int channr) clear_bit(ST_TXBUSY, &portp->state); if (nt.data & (DT_TXEMPTY | DT_TXLOW)) { if (tty != (struct tty_struct *) NULL) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) { (tty->ldisc.write_wakeup)(tty); EBRDENABLE(brdp); } @@ -2460,12 +2549,10 @@ static inline int stli_hostcmd(stlibrd_t *brdp, int channr) tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; *tty->flip.char_buf_ptr++ = 0; -#ifndef MODULE if (portp->flags & ASYNC_SAK) { do_SAK(tty); EBRDENABLE(brdp); } -#endif tty_schedule_flip(tty); } } @@ -2490,7 +2577,74 @@ static inline int stli_hostcmd(stlibrd_t *brdp, int channr) stli_read(brdp, portp); } - return(0); + return((test_bit(ST_OPENING, &portp->state) || + test_bit(ST_CLOSING, &portp->state) || + test_bit(ST_CMDING, &portp->state) || + test_bit(ST_TXBUSY, &portp->state) || + test_bit(ST_RXING, &portp->state)) ? 0 : 1); +} + +/*****************************************************************************/ + +/* + * Service all ports on a particular board. Assumes that the boards + * shared memory is enabled, and that the page pointer is pointed + * at the cdk header structure. + */ + +static inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp) +{ + stliport_t *portp; + unsigned char hostbits[(STL_MAXCHANS / 8) + 1]; + unsigned char slavebits[(STL_MAXCHANS / 8) + 1]; + unsigned char *slavep; + int bitpos, bitat, bitsize; + int channr, nrdevs, slavebitchange; + + bitsize = brdp->bitsize; + nrdevs = brdp->nrdevs; + +/* + * Check if slave wants any service. Basically we try to do as + * little work as possible here. There are 2 levels of service + * bits. So if there is nothing to do we bail early. We check + * 8 service bits at a time in the inner loop, so we can bypass + * the lot if none of them want service. + */ + memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset), + bitsize); + + memset(&slavebits[0], 0, bitsize); + slavebitchange = 0; + + for (bitpos = 0; (bitpos < bitsize); bitpos++) { + if (hostbits[bitpos] == 0) + continue; + channr = bitpos * 8; + for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) { + if (hostbits[bitpos] & bitat) { + portp = brdp->ports[(channr - 1)]; + if (stli_hostcmd(brdp, portp)) { + slavebitchange++; + slavebits[bitpos] |= bitat; + } + } + } + } + +/* + * If any of the ports are no longer busy then update them in the + * slave request bits. We need to do this after, since a host port + * service may initiate more slave requests. + */ + if (slavebitchange) { + hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + slavep = ((unsigned char *) hdrp) + brdp->slaveoffset; + for (bitpos = 0; (bitpos < bitsize); bitpos++) { + if (slavebits[bitpos]) + slavep[bitpos] &= ~slavebits[bitpos]; + } + } } /*****************************************************************************/ @@ -2507,12 +2661,8 @@ static inline int stli_hostcmd(stlibrd_t *brdp, int channr) static void stli_poll(unsigned long arg) { volatile cdkhdr_t *hdrp; - unsigned char bits[(STL_MAXCHANS / 8) + 1]; - unsigned char hostreq, slavereq; - stliport_t *portp; stlibrd_t *brdp; - int bitpos, bitat, bitsize; - int brdnr, channr, nrdevs; + int brdnr; stli_timerlist.expires = STLI_TIMEOUT; add_timer(&stli_timerlist); @@ -2529,67 +2679,8 @@ static void stli_poll(unsigned long arg) EBRDENABLE(brdp); hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hostreq = hdrp->hostreq; - slavereq = hdrp->slavereq; - bitsize = brdp->bitsize; - nrdevs = brdp->nrdevs; - -/* - * Check if slave wants any service. Basically we try to do as - * little work as possible here. There are 2 levels of service - * bits. So if there is nothing to do we bail early. We check - * 8 service bits at a time in the inner loop, so we can bypass - * the lot if none of them want service. - */ - if (hostreq) { - memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize); - - for (bitpos = 0; (bitpos < bitsize); bitpos++) { - if (bits[bitpos] == 0) - continue; - channr = bitpos * 8; - for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) { - if (bits[bitpos] & bitat) { - stli_hostcmd(brdp, channr); - } - } - } - } - -/* - * Check if any of the out-standing host commands have completed. - * It is a bit unfortunate that we need to check stuff that we - * initiated! This ain't pretty, but it needs to be fast. - */ - if (slavereq) { - slavereq = 0; - hostreq = 0; - memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->slaveoffset), bitsize); - - for (bitpos = 0; (bitpos < bitsize); bitpos++) { - if (bits[bitpos] == 0) - continue; - channr = bitpos * 8; - for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) { - if (bits[bitpos] & bitat) { - portp = brdp->ports[(channr - 1)]; - if (test_bit(ST_OPENING, &portp->state) || - test_bit(ST_CLOSING, &portp->state) || - test_bit(ST_CMDING, &portp->state) || - test_bit(ST_TXBUSY, &portp->state)) { - slavereq |= portp->reqbit; - } else { - bits[bitpos] &= ~bitat; - hostreq++; - } - } - } - } - hdrp->slavereq = slavereq; - if (hostreq) - memcpy((((unsigned char *) hdrp) + brdp->slaveoffset), &bits[0], bitsize); - } - + if (hdrp->hostreq) + stli_brdpoll(brdp, hdrp); EBRDDISABLE(brdp); } } @@ -2604,7 +2695,8 @@ static void stli_poll(unsigned long arg) static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp) { #if DEBUG - printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp); + printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", + (int) portp, (int) pp, (int) tiosp); #endif memset(pp, 0, sizeof(asyport_t)); @@ -2615,7 +2707,7 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tio pp->baudout = tiosp->c_cflag & CBAUD; if (pp->baudout & CBAUDEX) { pp->baudout &= ~CBAUDEX; - if ((pp->baudout < 1) || (pp->baudout > 2)) + if ((pp->baudout < 1) || (pp->baudout > 5)) tiosp->c_cflag &= ~CBAUDEX; else pp->baudout += 15; @@ -2706,7 +2798,10 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tio /* * Transfer any persistent flags into the asyport structure. */ - pp->pflag = portp->pflag; + pp->pflag = (portp->pflag & 0xffff); + pp->vmin = (portp->pflag & P_RXIMIN) ? 1 : 0; + pp->vtime = (portp->pflag & P_RXITIME) ? 1 : 0; + pp->cc[1] = (portp->pflag & P_RXTHOLD) ? 1 : 0; } /*****************************************************************************/ @@ -2765,7 +2860,7 @@ static long stli_mktiocm(unsigned long sigvalue) * we need to do here is set up the appropriate per port data structures. */ -static int stli_initports(stlibrd_t *brdp) +static inline int stli_initports(stlibrd_t *brdp) { stliport_t *portp; int i, panelnr, panelport; @@ -2855,11 +2950,14 @@ static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) unsigned char val; #if DEBUG - printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -2942,11 +3040,14 @@ static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line unsigned char val; #if DEBUG - printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); + printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", + (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -2996,7 +3097,9 @@ static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line unsigned char val; if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3071,11 +3174,14 @@ static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) void *ptr; #if DEBUG - printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; } else { ptr = brdp->membase + (offset % ONB_ATPAGESIZE); @@ -3158,11 +3264,14 @@ static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line) unsigned char val; #if DEBUG - printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); + printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", + (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3224,11 +3333,14 @@ static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line) unsigned char val; #if DEBUG - printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3282,11 +3394,14 @@ static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) void *ptr; #if DEBUG - printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; } else { ptr = brdp->membase + (offset % STAL_PAGESIZE); @@ -3319,12 +3434,13 @@ static void stli_stalreset(stlibrd_t *brdp) * board types. */ -static int stli_initecp(stlibrd_t *brdp) +static inline int stli_initecp(stlibrd_t *brdp) { cdkecpsig_t sig; cdkecpsig_t *sigsp; unsigned int status, nxtid; - int panelnr; + char *name; + int panelnr, nrports; #if DEBUG printk("stli_initecp(brdp=%x)\n", (int) brdp); @@ -3336,6 +3452,11 @@ static int stli_initecp(stlibrd_t *brdp) if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); + brdp->iosize = ECP_IOSIZE; + if (check_region(brdp->iobase, brdp->iosize)) + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->iobase); + /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now @@ -3353,6 +3474,7 @@ static int stli_initecp(stlibrd_t *brdp) brdp->getmemptr = stli_ecpgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpreset; + name = "serial(EC8/64)"; break; case BRD_ECPE: @@ -3366,6 +3488,7 @@ static int stli_initecp(stlibrd_t *brdp) brdp->getmemptr = stli_ecpeigetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpeireset; + name = "serial(EC8/64-EI)"; break; case BRD_ECPMC: @@ -3379,6 +3502,7 @@ static int stli_initecp(stlibrd_t *brdp) brdp->getmemptr = stli_ecpmcgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpmcreset; + name = "serial(EC8/64-MCA)"; break; default: @@ -3410,10 +3534,11 @@ static int stli_initecp(stlibrd_t *brdp) EBRDDISABLE(brdp); #if 0 - printk("%s(%d): sig-> magic=%x romver=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", + printk("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", __FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0], - (int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3], - (int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6], + (int) sig.panelid[1], (int) sig.panelid[2], + (int) sig.panelid[3], (int) sig.panelid[4], + (int) sig.panelid[5], (int) sig.panelid[6], (int) sig.panelid[7]); #endif @@ -3428,20 +3553,18 @@ static int stli_initecp(stlibrd_t *brdp) status = sig.panelid[nxtid]; if ((status & ECH_PNLIDMASK) != nxtid) break; - if (status & ECH_PNL16PORT) { - brdp->panels[panelnr] = 16; - brdp->nrports += 16; - nxtid += 2; - } else { - brdp->panels[panelnr] = 8; - brdp->nrports += 8; - nxtid++; - } + brdp->panelids[panelnr] = status; + nrports = (status & ECH_PNL16PORT) ? 16 : 8; + if ((nrports == 16) && ((status & ECH_PNLXPID) == 0)) + nxtid++; + brdp->panels[panelnr] = nrports; + brdp->nrports += nrports; + nxtid++; brdp->nrpanels++; } - request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)"); + request_region(brdp->iobase, brdp->iosize, name); brdp->state |= BST_FOUND; return(0); } @@ -3453,10 +3576,11 @@ static int stli_initecp(stlibrd_t *brdp) * This handles only these board types. */ -static int stli_initonb(stlibrd_t *brdp) +static inline int stli_initonb(stlibrd_t *brdp) { cdkonbsig_t sig; cdkonbsig_t *sigsp; + char *name; int i; #if DEBUG @@ -3469,6 +3593,11 @@ static int stli_initonb(stlibrd_t *brdp) if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); + brdp->iosize = ONB_IOSIZE; + if (check_region(brdp->iobase, brdp->iosize)) + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->iobase); + /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now @@ -3494,6 +3623,7 @@ static int stli_initonb(stlibrd_t *brdp) brdp->enabval = ONB_MEMENABHI; else brdp->enabval = ONB_MEMENABLO; + name = "serial(ONBoard)"; break; case BRD_ONBOARDE: @@ -3507,6 +3637,7 @@ static int stli_initonb(stlibrd_t *brdp) brdp->getmemptr = stli_onbegetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbereset; + name = "serial(ONBoard/E)"; break; case BRD_BRUMBY4: @@ -3522,6 +3653,7 @@ static int stli_initonb(stlibrd_t *brdp) brdp->getmemptr = stli_bbygetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_bbyreset; + name = "serial(Brumby)"; break; case BRD_STALLION: @@ -3535,6 +3667,7 @@ static int stli_initonb(stlibrd_t *brdp) brdp->getmemptr = stli_stalgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_stalreset; + name = "serial(Stallion)"; break; default: @@ -3572,7 +3705,7 @@ static int stli_initonb(stlibrd_t *brdp) #endif if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) || - (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) + (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) return(-ENODEV); /* @@ -3591,7 +3724,7 @@ static int stli_initonb(stlibrd_t *brdp) } brdp->panels[0] = brdp->nrports; - request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)"); + request_region(brdp->iobase, brdp->iosize, name); brdp->state |= BST_FOUND; return(0); } @@ -3626,14 +3759,16 @@ static int stli_startbrd(stlibrd_t *brdp) nrdevs = hdrp->nrdevs; #if 0 - printk("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x slavep=%x\n", + printk("%s(%d): CDK version %d.%d.%d --> " + "nrdevs=%d memp=%x hostp=%x slavep=%x\n", __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification, hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp, (int) hdrp->slavep); #endif if (nrdevs < (brdp->nrports + 1)) { - printk("STALLION: slave failed to allocate memory for all devices, devices=%d\n", nrdevs); + printk("STALLION: slave failed to allocate memory for all " + "devices, devices=%d\n", nrdevs); brdp->nrports = nrdevs - 1; } brdp->nrdevs = nrdevs; @@ -3671,6 +3806,8 @@ static int stli_startbrd(stlibrd_t *brdp) portp->portbit = (unsigned char) (0x1 << (i % 8)); } + hdrp->slavereq = 0xff; + /* * For each port setup a local copy of the RX and TX buffer offsets * and sizes. We do this separate from the above, because we need to @@ -3743,20 +3880,27 @@ static int stli_brdinit(stlibrd_t *brdp) case BRD_ECH: case BRD_ECHMC: case BRD_ECHPCI: - printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]); + printk("STALLION: %s board type not supported in this driver\n", + stli_brdnames[brdp->brdtype]); return(ENODEV); default: - printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); + printk("STALLION: unit=%d is unknown board type=%d\n", + brdp->brdnr, brdp->brdtype); return(ENODEV); } if ((brdp->state & BST_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr); + printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", + stli_brdnames[brdp->brdtype], brdp->brdnr, + brdp->iobase, (int) brdp->memaddr); return(ENODEV); } stli_initports(brdp); - printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports); + printk("STALLION: %s found, unit=%d io=%x mem=%x " + "nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], + brdp->brdnr, brdp->iobase, (int) brdp->memaddr, + brdp->nrpanels, brdp->nrports); return(0); } @@ -3767,7 +3911,7 @@ static int stli_brdinit(stlibrd_t *brdp) * might be. This is a bit if hack, but it is the best we can do. */ -static int stli_eisamemprobe(stlibrd_t *brdp) +static inline int stli_eisamemprobe(stlibrd_t *brdp) { cdkecpsig_t ecpsig, *ecpsigp; cdkonbsig_t onbsig, *onbsigp; @@ -3820,15 +3964,19 @@ static int stli_eisamemprobe(stlibrd_t *brdp) continue; } if (brdp->brdtype == BRD_ECPE) { - ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, CDK_SIGADDR, __LINE__); + ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, + CDK_SIGADDR, __LINE__); memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t)); if (ecpsig.magic == ECP_MAGIC) foundit = 1; } else { - onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, CDK_SIGADDR, __LINE__); + onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, + CDK_SIGADDR, __LINE__); memcpy(&onbsig, onbsigp, sizeof(cdkonbsig_t)); - if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) && - (onbsig.magic2 == ONB_MAGIC2) && (onbsig.magic3 == ONB_MAGIC3)) + if ((onbsig.magic0 == ONB_MAGIC0) && + (onbsig.magic1 == ONB_MAGIC1) && + (onbsig.magic2 == ONB_MAGIC2) && + (onbsig.magic3 == ONB_MAGIC3)) foundit = 1; } if (brdp->memaddr >= 0x100000) @@ -3849,7 +3997,9 @@ static int stli_eisamemprobe(stlibrd_t *brdp) if (! foundit) { brdp->memaddr = 0; brdp->membase = 0; - printk("STALLION: failed to probe shared memory region for %s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], (brdp->iobase >> 12)); + printk("STALLION: failed to probe shared memory region for " + "%s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], + (brdp->iobase >> 12)); return(-ENODEV); } return(0); @@ -3867,7 +4017,7 @@ static int stli_eisamemprobe(stlibrd_t *brdp) * do is go probing around in the usual places hoping we can find it. */ -static int stli_findeisabrds() +static inline int stli_findeisabrds() { stlibrd_t *brdp; unsigned int iobase, eid; @@ -3915,7 +4065,8 @@ static int stli_findeisabrds() * info table. */ if (stli_nrbrds >= STL_MAXBRDS) { - printk("STALLION: no room for more probed boards, maximum supported %d\n", STL_MAXBRDS); + printk("STALLION: no room for more probed boards, " + "maximum supported %d\n", STL_MAXBRDS); break; } @@ -3925,7 +4076,8 @@ static int stli_findeisabrds() */ brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlibrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlibrd_t)); @@ -3956,7 +4108,7 @@ static int stli_findeisabrds() * can find. */ -static int stli_initbrds() +static inline int stli_initbrds() { stlibrd_t *brdp, *nxtbrdp; stlconf_t *confp; @@ -3967,7 +4119,8 @@ static int stli_initbrds() #endif if (stli_nrbrds > STL_MAXBRDS) { - printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS); + printk("STALLION: too many boards in configuration table, " + "truncating to %d\n", STL_MAXBRDS); stli_nrbrds = STL_MAXBRDS; } @@ -3979,7 +4132,8 @@ static int stli_initbrds() confp = &stli_brdconf[i]; brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlibrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlibrd_t)); @@ -4013,7 +4167,9 @@ static int stli_initbrds() nxtbrdp = stli_brds[j]; if (nxtbrdp == (stlibrd_t *) NULL) continue; - if ((brdp->membase >= nxtbrdp->membase) && (brdp->membase <= (nxtbrdp->membase + nxtbrdp->memsize - 1))) { + if ((brdp->membase >= nxtbrdp->membase) && + (brdp->membase <= (nxtbrdp->membase + + nxtbrdp->memsize - 1))) { stli_shared++; break; } @@ -4053,7 +4209,8 @@ static int stli_memread(struct inode *ip, struct file *fp, char *buf, int count) int brdnr, size, n; #if DEBUG - printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip, + (int) fp, (int) buf, count); #endif brdnr = MINOR(ip->i_rdev); @@ -4103,7 +4260,8 @@ static int stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int int brdnr, size, n; #if DEBUG - printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip, + (int) fp, (int) buf, count); #endif brdnr = MINOR(ip->i_rdev); @@ -4205,31 +4363,28 @@ static stliport_t *stli_getport(int brdnr, int panelnr, int portnr) * what port to get stats for (used through board control device). */ -static int stli_getportstats(stliport_t *portp, comstats_t *cp) +static int stli_portcmdstats(stliport_t *portp) { unsigned long flags; stlibrd_t *brdp; int rc; - if (portp == (stliport_t *) NULL) { - memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t)); - portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); - if (portp == (stliport_t *) NULL) - return(-ENODEV); - } + memset(&stli_comstats, 0, sizeof(comstats_t)); + if (portp == (stliport_t *) NULL) + return(-ENODEV); brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); if (brdp->state & BST_STARTED) { - if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats, sizeof(asystats_t), 1)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, + &stli_cdkstats, sizeof(asystats_t), 1)) < 0) return(rc); } else { memset(&stli_cdkstats, 0, sizeof(asystats_t)); } - memset(&stli_comstats, 0, sizeof(comstats_t)); stli_comstats.brd = portp->brdnr; stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; @@ -4272,6 +4427,37 @@ static int stli_getportstats(stliport_t *portp, comstats_t *cp) stli_comstats.hwid = stli_cdkstats.hwid; stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals); + return(0); +} + +/*****************************************************************************/ + +/* + * Return the port stats structure to user app. A NULL port struct + * pointer passed in means that we need to find out from the app + * what port to get stats for (used through board control device). + */ + +static int stli_getportstats(stliport_t *portp, comstats_t *cp) +{ + stlibrd_t *brdp; + int rc; + + if (portp == (stliport_t *) NULL) { + memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t)); + portp = stli_getport(stli_comstats.brd, stli_comstats.panel, + stli_comstats.port); + if (portp == (stliport_t *) NULL) + return(-ENODEV); + } + + brdp = stli_brds[portp->brdnr]; + if (brdp == (stlibrd_t *) NULL) + return(-ENODEV); + + if ((rc = stli_portcmdstats(portp)) < 0) + return(rc); + memcpy_tofs(cp, &stli_comstats, sizeof(comstats_t)); return(0); } @@ -4289,7 +4475,8 @@ static int stli_clrportstats(stliport_t *portp, comstats_t *cp) if (portp == (stliport_t *) NULL) { memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t)); - portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); + portp = stli_getport(stli_comstats.brd, stli_comstats.panel, + stli_comstats.port); if (portp == (stliport_t *) NULL) return(-ENODEV); } @@ -4353,6 +4540,26 @@ static int stli_getbrdstruct(unsigned long arg) /*****************************************************************************/ +/* + * Memory device open code. Need to keep track of opens and close + * for module handling. + */ + +static int stli_memopen(struct inode *ip, struct file *fp) +{ + MOD_INC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +static void stli_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; +} + +/*****************************************************************************/ + /* * The "staliomem" device is also required to do some special operations on * the board. We need to be able to send an interrupt to the board, @@ -4365,7 +4572,8 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un int brdnr, rc, done; #if DEBUG - printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg); + printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, + (int) fp, cmd, (int) arg); #endif /* @@ -4376,27 +4584,34 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un switch (cmd) { case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stli_getportstats((stliport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stli_getportstats((stliport_t *) NULL, + (comstats_t *) arg); done++; break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stli_clrportstats((stliport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stli_clrportstats((stliport_t *) NULL, + (comstats_t *) arg); done++; break; case COM_GETBRDSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(combrd_t))) == 0) rc = stli_getbrdstats((combrd_t *) arg); done++; break; case COM_READPORT: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stliport_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stliport_t))) == 0) rc = stli_getportstruct(arg); done++; break; case COM_READBOARD: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlibrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlibrd_t))) == 0) rc = stli_getbrdstruct(arg); done++; break; @@ -4450,7 +4665,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un int stli_init() { - printk(KERN_INFO "%s: version %s\n", stli_drvname, stli_drvversion); + printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); stli_initbrds(); @@ -4459,10 +4674,12 @@ int stli_init() */ stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE); if (stli_tmpwritebuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STLI_TXBUFSIZE); stli_txcookbuf = (char *) stli_memalloc(STLI_TXBUFSIZE); if (stli_txcookbuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STLI_TXBUFSIZE); /* * Set up a character driver for the shared memory region. We need this diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index c58a1f205724..9c1a932f2bc0 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -3,6 +3,7 @@ /* * stallion.c -- stallion multiport serial driver. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This code is loosely based on the Linux serial driver, written by @@ -37,12 +38,13 @@ #include #include #include +#include #include #include #include #include #include -#include /* for CONFIG_PCI */ +#include #include #include #include @@ -55,16 +57,16 @@ /*****************************************************************************/ /* - * Define different board types. At the moment I have only declared - * those boards that this driver supports. But I will use the standard - * "assigned" board numbers. In the future this driver will support - * some of the other Stallion boards. Currently supported boards are - * abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32. + * Define different board types. Use the standard Stallion "assigned" + * board numbers. Boards supported in this driver are abbreviated as + * EIO = EasyIO and ECH = EasyConnection 8/32. */ #define BRD_EASYIO 20 #define BRD_ECH 21 #define BRD_ECHMC 22 #define BRD_ECHPCI 26 +#define BRD_ECH64PCI 27 +#define BRD_EASYIOPCI 28 /* * Define a configuration structure to hold the board configuration. @@ -126,12 +128,11 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); #define STL_DRVTYPCALLOUT 2 /* - * I haven't really decided (or measured) what TX buffer size gives - * a good balance between performance and memory usage. These seem - * to work pretty well... + * Set the TX buffer size. Bigger is better, but we don't want + * to chew too much memory with buffers! */ -#define STL_TXBUFLOW 256 -#define STL_TXBUFSIZE 2048 +#define STL_TXBUFLOW 512 +#define STL_TXBUFSIZE 4096 /*****************************************************************************/ @@ -139,8 +140,8 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ -static char *stl_drvname = "Stallion Multiport Serial Driver"; -static char *stl_drvversion = "1.1.3"; +static char *stl_drvtitle = "Stallion Multiport Serial Driver"; +static char *stl_drvversion = "5.4.4"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -184,6 +185,11 @@ static combrd_t stl_brdstats; static stlbrd_t stl_dummybrd; static stlport_t stl_dummyport; +/* + * Define global place to put buffer overflow characters. + */ +static char stl_unwanted[SC26198_RXFIFOSIZE]; + /* * Keep track of what interrupts we have requested for us. * We don't need to request an interrupt twice if it is being @@ -198,7 +204,7 @@ static stlbrd_t *stl_brds[STL_MAXBRDS]; /* * Per board state flags. Used with the state field of the board struct. - * Not really much here yet! + * Not really much here! */ #define BRD_FOUND 0x1 @@ -210,6 +216,7 @@ static stlbrd_t *stl_brds[STL_MAXBRDS]; #define ASYI_TXBUSY 1 #define ASYI_TXLOW 2 #define ASYI_DCDCHANGE 3 +#define ASYI_TXFLOWED 4 /* * Define an array of board names as printable strings. Handy for @@ -243,23 +250,33 @@ static char *stl_brdnames[] = { (char *) NULL, (char *) NULL, "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /*****************************************************************************/ /* * Hardware ID bits for the EasyIO and ECH boards. These defines apply - * to the directly accessible io ports of these boards (not the cd1400 - * uarts - they are in cd1400.h). + * to the directly accessible io ports of these boards (not the uarts - + * they are in cd1400.h and sc26198.h). */ #define EIO_8PORTRS 0x04 #define EIO_4PORTRS 0x05 #define EIO_8PORTDI 0x00 #define EIO_8PORTM 0x06 +#define EIO_MK3 0x03 #define EIO_IDBITMASK 0x07 + +#define EIO_BRDMASK 0xf0 +#define ID_BRD4 0x10 +#define ID_BRD8 0x20 +#define ID_BRD16 0x30 + #define EIO_INTRPEND 0x08 #define EIO_INTEDGE 0x00 #define EIO_INTLEVEL 0x08 +#define EIO_0WS 0x10 #define ECH_ID 0xa0 #define ECH_IDBITMASK 0xe0 @@ -278,24 +295,10 @@ static char *stl_brdnames[] = { #define ECH_PNLSTATUS 2 #define ECH_PNL16PORT 0x20 #define ECH_PNLIDMASK 0x07 +#define ECH_PNLXPID 0x40 #define ECH_PNLINTRPEND 0x80 -#define ECH_ADDR2MASK 0x1e0 - -#define EIO_CLK 25000000 -#define EIO_CLK8M 20000000 -#define ECH_CLK EIO_CLK - -/* - * Define the offsets within the register bank for all io registers. - * These io address offsets are common to both the EIO and ECH. - */ -#define EREG_ADDR 0 -#define EREG_DATA 4 -#define EREG_RXACK 5 -#define EREG_TXACK 6 -#define EREG_MDACK 7 -#define EREG_BANKSIZE 8 +#define ECH_ADDR2MASK 0x1e0 /* * Define the vector mapping bits for the programmable interrupt board @@ -326,18 +329,51 @@ static unsigned char stl_vecmap[] = { outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \ stl_brds[(brdnr)]->ioctrl); +#define STL_CD1400MAXBAUD 230400 +#define STL_SC26198MAXBAUD 460800 + +#define STL_BAUDBASE 115200 +#define STL_CLOSEDELAY (5 * HZ / 10) + +/*****************************************************************************/ + +#ifdef CONFIG_PCI + /* - * Define the cd1400 baud rate clocks. These are used when calculating - * what clock and divisor to use for the required baud rate. Also - * define the maximum baud rate allowed, and the default base baud. + * Define the Stallion PCI vendor and device IDs. */ -static int stl_cd1400clkdivs[] = { - CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 +#ifndef PCI_VENDOR_ID_STALLION +#define PCI_VENDOR_ID_STALLION 0x124d +#endif +#ifndef PCI_DEVICE_ID_ECHPCI832 +#define PCI_DEVICE_ID_ECHPCI832 0x0000 +#endif +#ifndef PCI_DEVICE_ID_ECHPCI864 +#define PCI_DEVICE_ID_ECHPCI864 0x0002 +#endif +#ifndef PCI_DEVICE_ID_EIOPCI +#define PCI_DEVICE_ID_EIOPCI 0x0003 +#endif + +/* + * Define structure to hold all Stallion PCI boards. + */ +typedef struct stlpcibrd { + unsigned short vendid; + unsigned short devid; + int brdtype; +} stlpcibrd_t; + +static stlpcibrd_t stl_pcibrds[] = { + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864, BRD_ECH64PCI }, + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI, BRD_EASYIOPCI }, + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, BRD_ECHPCI }, + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, BRD_ECHPCI }, }; -#define STL_MAXBAUD 230400 -#define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +static int stl_nrpcibrds = sizeof(stl_pcibrds) / sizeof(stlpcibrd_t); + +#endif /*****************************************************************************/ @@ -349,16 +385,14 @@ static int stl_cd1400clkdivs[] = { /* * Define a baud rate table that converts termios baud rate selector - * into the actual baud rate value. All baud rate calculates are based - * on the actual baud rate required. + * into the actual baud rate value. All baud rate calculations are + * based on the actual baud rate required. */ static unsigned int stl_baudrates[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400 + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 }; -/*****************************************************************************/ - /* * Define some handy local macros... */ @@ -392,15 +426,15 @@ static void stl_unthrottle(struct tty_struct *tty); static void stl_stop(struct tty_struct *tty); static void stl_start(struct tty_struct *tty); static void stl_flushbuffer(struct tty_struct *tty); +static void stl_waituntilsent(struct tty_struct *tty, int timeout); static void stl_hangup(struct tty_struct *tty); +static int stl_memopen(struct inode *ip, struct file *fp); +static void stl_memclose(struct inode *ip, struct file *fp); static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); -static int stl_initbrds(void); static int stl_brdinit(stlbrd_t *brdp); -static int stl_initeio(stlbrd_t *brdp); -static int stl_initech(stlbrd_t *brdp); static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp); -static int stl_mapirq(int irq); +static int stl_mapirq(int irq, char *name); static void stl_getserial(stlport_t *portp, struct serial_struct *sp); static int stl_setserial(stlport_t *portp, struct serial_struct *sp); static int stl_getbrdstats(combrd_t *bp); @@ -408,28 +442,212 @@ static int stl_getportstats(stlport_t *portp, comstats_t *cp); static int stl_clrportstats(stlport_t *portp, comstats_t *cp); static int stl_getportstruct(unsigned long arg); static int stl_getbrdstruct(unsigned long arg); -static void stl_setreg(stlport_t *portp, int regnr, int value); -static int stl_getreg(stlport_t *portp, int regnr); -static int stl_updatereg(stlport_t *portp, int regnr, int value); -static void stl_setport(stlport_t *portp, struct termios *tiosp); -static int stl_getsignals(stlport_t *portp); -static void stl_setsignals(stlport_t *portp, int dtr, int rts); -static void stl_ccrwait(stlport_t *portp); -static void stl_enablerxtx(stlport_t *portp, int rx, int tx); -static void stl_startrxtx(stlport_t *portp, int rx, int tx); -static void stl_disableintrs(stlport_t *portp); -static void stl_sendbreak(stlport_t *portp, long len); static int stl_waitcarrier(stlport_t *portp, struct file *filp); static void stl_delay(int len); static void stl_intr(int irq, void *dev_id, struct pt_regs *regs); +static void stl_eiointr(stlbrd_t *brdp); +static void stl_echatintr(stlbrd_t *brdp); +static void stl_echmcaintr(stlbrd_t *brdp); +static void stl_echpciintr(stlbrd_t *brdp); +static void stl_echpci64intr(stlbrd_t *brdp); static void stl_offintr(void *private); static void *stl_memalloc(int len); static stlport_t *stl_getport(int brdnr, int panelnr, int portnr); +static inline int stl_initbrds(void); +static inline int stl_initeio(stlbrd_t *brdp); +static inline int stl_initech(stlbrd_t *brdp); + #ifdef CONFIG_PCI -static int stl_findpcibrds(void); +static inline int stl_findpcibrds(void); +static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr); #endif +/* + * CD1400 uart specific handling functions. + */ +static void stl_cd1400setreg(stlport_t *portp, int regnr, int value); +static int stl_cd1400getreg(stlport_t *portp, int regnr); +static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value); +static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp); +static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp); +static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp); +static int stl_cd1400getsignals(stlport_t *portp); +static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts); +static void stl_cd1400ccrwait(stlport_t *portp); +static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx); +static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx); +static void stl_cd1400disableintrs(stlport_t *portp); +static void stl_cd1400sendbreak(stlport_t *portp, long len); +static void stl_cd1400flowctrl(stlport_t *portp, int state); +static void stl_cd1400sendflow(stlport_t *portp, int state); +static void stl_cd1400flush(stlport_t *portp); +static int stl_cd1400datastate(stlport_t *portp); +static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase); +static void stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase); +static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr); +static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr); +static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr); + +/* + * SC26198 uart specific handling functions. + */ +static void stl_sc26198setreg(stlport_t *portp, int regnr, int value); +static int stl_sc26198getreg(stlport_t *portp, int regnr); +static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value); +static int stl_sc26198getglobreg(stlport_t *portp, int regnr); +static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp); +static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp); +static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp); +static int stl_sc26198getsignals(stlport_t *portp); +static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts); +static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx); +static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx); +static void stl_sc26198disableintrs(stlport_t *portp); +static void stl_sc26198sendbreak(stlport_t *portp, long len); +static void stl_sc26198flowctrl(stlport_t *portp, int state); +static void stl_sc26198sendflow(stlport_t *portp, int state); +static void stl_sc26198flush(stlport_t *portp); +static int stl_sc26198datastate(stlport_t *portp); +static void stl_sc26198wait(stlport_t *portp); +static void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty); +static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase); +static void stl_sc26198txisr(stlport_t *port); +static void stl_sc26198rxisr(stlport_t *port, unsigned int iack); +static void stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch); +static void stl_sc26198rxbadchars(stlport_t *portp); +static void stl_sc26198otherisr(stlport_t *port, unsigned int iack); + +/*****************************************************************************/ + +/* + * Generic UART support structure. + */ +typedef struct uart { + int (*panelinit)(stlbrd_t *brdp, stlpanel_t *panelp); + void (*portinit)(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp); + void (*setport)(stlport_t *portp, struct termios *tiosp); + int (*getsignals)(stlport_t *portp); + void (*setsignals)(stlport_t *portp, int dtr, int rts); + void (*enablerxtx)(stlport_t *portp, int rx, int tx); + void (*startrxtx)(stlport_t *portp, int rx, int tx); + void (*disableintrs)(stlport_t *portp); + void (*sendbreak)(stlport_t *portp, long len); + void (*flowctrl)(stlport_t *portp, int state); + void (*sendflow)(stlport_t *portp, int state); + void (*flush)(stlport_t *portp); + int (*datastate)(stlport_t *portp); + void (*intr)(stlpanel_t *panelp, unsigned int iobase); +} uart_t; + +/* + * Define some macros to make calling these functions nice and clean. + */ +#define stl_panelinit (* ((uart_t *) panelp->uartp)->panelinit) +#define stl_portinit (* ((uart_t *) portp->uartp)->portinit) +#define stl_setport (* ((uart_t *) portp->uartp)->setport) +#define stl_getsignals (* ((uart_t *) portp->uartp)->getsignals) +#define stl_setsignals (* ((uart_t *) portp->uartp)->setsignals) +#define stl_enablerxtx (* ((uart_t *) portp->uartp)->enablerxtx) +#define stl_startrxtx (* ((uart_t *) portp->uartp)->startrxtx) +#define stl_disableintrs (* ((uart_t *) portp->uartp)->disableintrs) +#define stl_sendbreak (* ((uart_t *) portp->uartp)->sendbreak) +#define stl_flowctrl (* ((uart_t *) portp->uartp)->flowctrl) +#define stl_sendflow (* ((uart_t *) portp->uartp)->sendflow) +#define stl_flush (* ((uart_t *) portp->uartp)->flush) +#define stl_datastate (* ((uart_t *) portp->uartp)->datastate) + +/*****************************************************************************/ + +/* + * CD1400 UART specific data initialization. + */ +static uart_t stl_cd1400uart = { + stl_cd1400panelinit, + stl_cd1400portinit, + stl_cd1400setport, + stl_cd1400getsignals, + stl_cd1400setsignals, + stl_cd1400enablerxtx, + stl_cd1400startrxtx, + stl_cd1400disableintrs, + stl_cd1400sendbreak, + stl_cd1400flowctrl, + stl_cd1400sendflow, + stl_cd1400flush, + stl_cd1400datastate, + stl_cd1400eiointr +}; + +/* + * Define the offsets within the register bank of a cd1400 based panel. + * These io address offsets are common to the EasyIO board as well. + */ +#define EREG_ADDR 0 +#define EREG_DATA 4 +#define EREG_RXACK 5 +#define EREG_TXACK 6 +#define EREG_MDACK 7 + +#define EREG_BANKSIZE 8 + +#define CD1400_CLK 25000000 +#define CD1400_CLK8M 20000000 + +/* + * Define the cd1400 baud rate clocks. These are used when calculating + * what clock and divisor to use for the required baud rate. Also + * define the maximum baud rate allowed, and the default base baud. + */ +static int stl_cd1400clkdivs[] = { + CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 +}; + +/*****************************************************************************/ + +/* + * SC26198 UART specific data initization. + */ +static uart_t stl_sc26198uart = { + stl_sc26198panelinit, + stl_sc26198portinit, + stl_sc26198setport, + stl_sc26198getsignals, + stl_sc26198setsignals, + stl_sc26198enablerxtx, + stl_sc26198startrxtx, + stl_sc26198disableintrs, + stl_sc26198sendbreak, + stl_sc26198flowctrl, + stl_sc26198sendflow, + stl_sc26198flush, + stl_sc26198datastate, + stl_sc26198intr +}; + +/* + * Define the offsets within the register bank of a sc26198 based panel. + */ +#define XP_DATA 0 +#define XP_ADDR 1 +#define XP_MODID 2 +#define XP_STATUS 2 +#define XP_IACK 3 + +#define XP_BANKSIZE 4 + +/* + * Define the sc26198 baud rate table. Offsets within the table + * represent the actual baud rate selector of sc26198 registers. + */ +static unsigned int sc26198_baudtable[] = { + 50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600, + 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, + 230400, 460800, 921600 +}; + +#define SC26198_NRBAUDS (sizeof(sc26198_baudtable) / sizeof(unsigned int)) + /*****************************************************************************/ /* @@ -444,8 +662,8 @@ static struct file_operations stl_fsiomem = { NULL, stl_memioctl, NULL, - NULL, - NULL, + stl_memopen, + stl_memclose, NULL }; @@ -487,7 +705,8 @@ void cleanup_module() printk("cleanup_module()\n"); #endif - printk(KERN_INFO "Unloading %s: version %s\n", stl_drvname, stl_drvversion); + printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle, + stl_drvversion); save_flags(flags); cli(); @@ -501,12 +720,14 @@ void cleanup_module() i = tty_unregister_driver(&stl_serial); j = tty_unregister_driver(&stl_callout); if (i || j) { - printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j); + printk("STALLION: failed to un-register tty driver, " + "errno=%d,%d\n", -i, -j); restore_flags(flags); return; } if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) - printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i); + printk("STALLION: failed to un-register serial memory device, " + "errno=%d\n", -i); if (stl_tmpwritebuf != (char *) NULL) kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE); @@ -515,33 +736,24 @@ void cleanup_module() brdp = stl_brds[i]; for (j = 0; (j < STL_MAXPANELS); j++) { panelp = brdp->panels[j]; - if (panelp != (stlpanel_t *) NULL) { - for (k = 0; (k < STL_PORTSPERPANEL); k++) { - portp = panelp->ports[k]; - if (portp != (stlport_t *) NULL) { - if (portp->tty != (struct tty_struct *) NULL) - stl_hangup(portp->tty); - if (portp->tx.buf != (char *) NULL) - kfree_s(portp->tx.buf, STL_TXBUFSIZE); - kfree_s(portp, sizeof(stlport_t)); - } - } - kfree_s(panelp, sizeof(stlpanel_t)); + if (panelp == (stlpanel_t *) NULL) + continue; + for (k = 0; (k < STL_PORTSPERPANEL); k++) { + portp = panelp->ports[k]; + if (portp == (stlport_t *) NULL) + continue; + if (portp->tty != (struct tty_struct *) NULL) + stl_hangup(portp->tty); + if (portp->tx.buf != (char *) NULL) + kfree_s(portp->tx.buf, STL_TXBUFSIZE); + kfree_s(portp, sizeof(stlport_t)); } - + kfree_s(panelp, sizeof(stlpanel_t)); } - if (brdp->brdtype == BRD_ECH) { - release_region(brdp->ioaddr1, 2); - release_region(brdp->ioaddr2, 32); - } else if (brdp->brdtype == BRD_ECHPCI) { - release_region(brdp->ioaddr1, 4); - release_region(brdp->ioaddr2, 8); - } else if (brdp->brdtype == BRD_ECHMC) { - release_region(brdp->ioaddr1, 64); - } else if (brdp->brdtype == BRD_EASYIO) { - release_region(brdp->ioaddr1, 8); - } + release_region(brdp->ioaddr1, brdp->iosize1); + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); kfree_s(brdp, sizeof(stlbrd_t)); stl_brds[i] = (stlbrd_t *) NULL; @@ -576,7 +788,8 @@ static int stl_open(struct tty_struct *tty, struct file *filp) int brdnr, panelnr, portnr, rc; #if DEBUG - printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device); + printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, + (int) filp, tty->device); #endif minordev = MINOR(tty->device); @@ -603,6 +816,8 @@ static int stl_open(struct tty_struct *tty, struct file *filp) if (portp == (stlport_t *) NULL) return(-ENODEV); + MOD_INC_USE_COUNT; + /* * On the first open of the device setup the port hardware, and * initialize the per port data structure. @@ -651,10 +866,10 @@ static int stl_open(struct tty_struct *tty, struct file *filp) return(-EBUSY); if (portp->flags & ASYNC_CALLOUT_ACTIVE) { if ((portp->flags & ASYNC_SESSION_LOCKOUT) && - (portp->session != current->session)) + (portp->session != current->session)) return(-EBUSY); if ((portp->flags & ASYNC_PGRP_LOCKOUT) && - (portp->pgrp != current->pgrp)) + (portp->pgrp != current->pgrp)) return(-EBUSY); } portp->flags |= ASYNC_CALLOUT_ACTIVE; @@ -712,13 +927,14 @@ static int stl_waitcarrier(stlport_t *portp, struct file *filp) save_flags(flags); cli(); portp->openwaitcnt++; - if (portp->refcount > 0) + if (! tty_hung_up_p(filp)) portp->refcount--; for (;;) { if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) stl_setsignals(portp, 1, 1); - if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) { + if (tty_hung_up_p(filp) || + ((portp->flags & ASYNC_INITIALIZED) == 0)) { if (portp->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else @@ -726,8 +942,8 @@ static int stl_waitcarrier(stlport_t *portp, struct file *filp) break; } if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) && - ((portp->flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) { + ((portp->flags & ASYNC_CLOSING) == 0) && + (doclocal || (portp->sigs & TIOCM_CD))) { break; } if (current->signal & ~current->blocked) { @@ -763,10 +979,14 @@ static void stl_close(struct tty_struct *tty, struct file *filp) save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } + if ((tty->count == 1) && (portp->refcount != 1)) + portp->refcount = 1; if (portp->refcount-- > 1) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -781,14 +1001,14 @@ static void stl_close(struct tty_struct *tty, struct file *filp) /* * May want to wait for any data to drain before closing. The BUSY - * flag keeps track of whether we are still sending or not - it allows - * for the FIFO in the cd1400. + * flag keeps track of whether we are still sending or not - it is + * very accurate for the cd1400, not quite so for the sc26198. + * (The sc26198 has no "end-of-data" interrupt only empty FIFO) */ tty->closing = 1; - if (test_bit(ASYI_TXBUSY, &portp->istate)) { - if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, portp->closing_wait); - } + if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, portp->closing_wait); + stl_waituntilsent(tty, (HZ / 2)); portp->flags &= ~ASYNC_INITIALIZED; stl_disableintrs(portp); @@ -808,7 +1028,6 @@ static void stl_close(struct tty_struct *tty, struct file *filp) (tty->ldisc.flush_buffer)(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -817,8 +1036,10 @@ static void stl_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&portp->open_wait); } - portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | + ASYNC_CLOSING); wake_up_interruptible(&portp->close_wait); + MOD_DEC_USE_COUNT; restore_flags(flags); } @@ -858,10 +1079,12 @@ static int stl_write(struct tty_struct *tty, int from_user, const unsigned char char *head, *tail; #if DEBUG - printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count); + printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", + (int) tty, from_user, (int) buf, count); #endif - if ((tty == (struct tty_struct *) NULL) || (stl_tmpwritebuf == (char *) NULL)) + if ((tty == (struct tty_struct *) NULL) || + (stl_tmpwritebuf == (char *) NULL)) return(0); portp = tty->driver_data; if (portp == (stlport_t *) NULL) @@ -983,7 +1206,8 @@ static void stl_flushchars(struct tty_struct *tty) return; #if 0 - if (tty->stopped || tty->hw_stopped || (portp->tx.head == portp->tx.tail)) + if (tty->stopped || tty->hw_stopped || + (portp->tx.head == portp->tx.tail)) return; #endif stl_startrxtx(portp, -1, 1); @@ -1066,7 +1290,6 @@ static void stl_getserial(stlport_t *portp, struct serial_struct *sp) #endif memset(&sio, 0, sizeof(struct serial_struct)); - sio.type = PORT_CIRRUS; sio.line = portp->portnr; sio.port = portp->ioaddr; sio.flags = portp->flags; @@ -1074,8 +1297,14 @@ static void stl_getserial(stlport_t *portp, struct serial_struct *sp) sio.close_delay = portp->close_delay; sio.closing_wait = portp->closing_wait; sio.custom_divisor = portp->custom_divisor; - sio.xmit_fifo_size = CD1400_TXFIFOSIZE; sio.hub6 = 0; + if (portp->uartp == &stl_cd1400uart) { + sio.type = PORT_CIRRUS; + sio.xmit_fifo_size = CD1400_TXFIFOSIZE; + } else { + sio.type = PORT_UNKNOWN; + sio.xmit_fifo_size = SC26198_TXFIFOSIZE; + } brdp = stl_brds[portp->brdnr]; if (brdp != (stlbrd_t *) NULL) @@ -1103,12 +1332,14 @@ static int stl_setserial(stlport_t *portp, struct serial_struct *sp) memcpy_fromfs(&sio, sp, sizeof(struct serial_struct)); if (!suser()) { if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK))) + (sio.close_delay != portp->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->flags & ~ASYNC_USR_MASK))) return(-EPERM); } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); + portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; @@ -1126,7 +1357,8 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd int rc; #if DEBUG - printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg); + printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", + (int) tty, (int) file, cmd, (int) arg); #endif if (tty == (struct tty_struct *) NULL) @@ -1135,6 +1367,12 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd if (portp == (stlport_t *) NULL) return(-ENODEV); + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return(-EIO); + } + rc = 0; switch (cmd) { @@ -1152,53 +1390,69 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd } break; case TIOCGSOFTCAR: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0) - put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(long))) == 0) + put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), + (unsigned long *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0); } break; case TIOCMGET: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { val = (unsigned long) stl_getsignals(portp); put_fs_long(val, (unsigned long *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1)); + stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1), + ((arg & TIOCM_RTS) ? 1 : -1)); } break; case TIOCMBIC: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1)); + stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1), + ((arg & TIOCM_RTS) ? 0 : -1)); } break; case TIOCMSET: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0)); + stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0), + ((arg & TIOCM_RTS) ? 1 : 0)); } break; case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) stl_getserial(portp, (struct serial_struct *) arg); break; case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) rc = stl_setserial(portp, (struct serial_struct *) arg); break; case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stl_getportstats(portp, (comstats_t *) arg); break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stl_clrportstats(portp, (comstats_t *) arg); break; case TIOCSERCONFIG: @@ -1234,11 +1488,13 @@ static void stl_settermios(struct tty_struct *tty, struct termios *old) return; tiosp = tty->termios; - if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag)) + if ((tiosp->c_cflag == old->c_cflag) && + (tiosp->c_iflag == old->c_iflag)) return; stl_setport(portp, tiosp); - stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), -1); + stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), + -1); if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) { tty->hw_stopped = 0; stl_start(tty); @@ -1257,7 +1513,6 @@ static void stl_settermios(struct tty_struct *tty, struct termios *old) static void stl_throttle(struct tty_struct *tty) { stlport_t *portp; - unsigned long flags; #if DEBUG printk("stl_throttle(tty=%x)\n", (int) tty); @@ -1268,24 +1523,7 @@ static void stl_throttle(struct tty_struct *tty) portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (tty->termios->c_iflag & IXOFF) { - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_SENDSCHR2); - portp->stats.rxxoff++; - stl_ccrwait(portp); - } - if (tty->termios->c_cflag & CRTSCTS) { - stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0)); - stl_setreg(portp, MSVR2, 0); - portp->stats.rxrtsoff++; - } - BRDDISABLE(portp->brdnr); - restore_flags(flags); + stl_flowctrl(portp, 0); } /*****************************************************************************/ @@ -1297,7 +1535,6 @@ static void stl_throttle(struct tty_struct *tty) static void stl_unthrottle(struct tty_struct *tty) { stlport_t *portp; - unsigned long flags; #if DEBUG printk("stl_unthrottle(tty=%x)\n", (int) tty); @@ -1308,30 +1545,7 @@ static void stl_unthrottle(struct tty_struct *tty) portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (tty->termios->c_iflag & IXOFF) { - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_SENDSCHR1); - portp->stats.rxxon++; - stl_ccrwait(portp); - } -/* - * Question: should we return RTS to what it was before? It may have - * been set by an ioctl... Suppose not, since if you have hardware - * flow control set then it is pretty silly to go and set the RTS line - * by hand. - */ - if (tty->termios->c_cflag & CRTSCTS) { - stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD)); - stl_setreg(portp, MSVR2, MSVR2_RTS); - portp->stats.rxrtson++; - } - BRDDISABLE(portp->brdnr); - restore_flags(flags); + stl_flowctrl(portp, 1); } /*****************************************************************************/ @@ -1354,7 +1568,6 @@ static void stl_stop(struct tty_struct *tty) portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - stl_startrxtx(portp, -1, 0); } @@ -1377,7 +1590,6 @@ static void stl_start(struct tty_struct *tty) portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - stl_startrxtx(portp, -1, 1); } @@ -1417,7 +1629,6 @@ static void stl_hangup(struct tty_struct *tty) portp->tx.head = (char *) NULL; portp->tx.tail = (char *) NULL; } - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); portp->refcount = 0; @@ -1429,7 +1640,6 @@ static void stl_hangup(struct tty_struct *tty) static void stl_flushbuffer(struct tty_struct *tty) { stlport_t *portp; - unsigned long flags; #if DEBUG printk("stl_flushbuffer(tty=%x)\n", (int) tty); @@ -1441,439 +1651,183 @@ static void stl_flushbuffer(struct tty_struct *tty) if (portp == (stlport_t *) NULL) return; - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_TXFLUSHFIFO); - stl_ccrwait(portp); - portp->tx.tail = portp->tx.head; - BRDDISABLE(portp->brdnr); - restore_flags(flags); - + stl_flush(portp); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); } /*****************************************************************************/ -/* - * These functions get/set/update the registers of the cd1400 UARTs. - * Access to the cd1400 registers is via an address/data io port pair. - * (Maybe should make this inline...) - */ - -static int stl_getreg(stlport_t *portp, int regnr) +static void stl_waituntilsent(struct tty_struct *tty, int timeout) { - outb((regnr + portp->uartaddr), portp->ioaddr); - return(inb(portp->ioaddr + EREG_DATA)); -} + stlport_t *portp; + unsigned long tend; -static void stl_setreg(stlport_t *portp, int regnr, int value) -{ - outb((regnr + portp->uartaddr), portp->ioaddr); - outb(value, portp->ioaddr + EREG_DATA); -} +#if DEBUG + printk("stl_waituntilsent(tty=%x,timeout=%d)\n", (int) tty, timeout); +#endif -static int stl_updatereg(stlport_t *portp, int regnr, int value) -{ - outb((regnr + portp->uartaddr), portp->ioaddr); - if (inb(portp->ioaddr + EREG_DATA) != value) { - outb(value, portp->ioaddr + EREG_DATA); - return(1); + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stlport_t *) NULL) + return; + + if (timeout == 0) + timeout = HZ; + tend = jiffies + timeout; + + while (stl_datastate(portp)) { + if (current->signal & ~current->blocked) + break; + stl_delay(2); + if (jiffies >= tend) + break; } - return(0); } /*****************************************************************************/ /* - * Transmit interrupt handler. This has gotta be fast! Handling TX - * chars is pretty simple, stuff as many as possible from the TX buffer - * into the cd1400 FIFO. Must also handle TX breaks here, since they - * are embedded as commands in the data stream. Oh no, had to use a goto! - * This could be optimized more, will do when I get time... - * In practice it is possible that interrupts are enabled but that the - * port has been hung up. Need to handle not having any TX buffer here, - * this is done by using the side effect that head and tail will also - * be NULL if the buffer has been freed. + * All board interrupts are vectored through here first. This code then + * calls off to the approrpriate board interrupt handlers. */ -static inline void stl_txisr(stlpanel_t *panelp, int ioaddr) +static void stl_intr(int irq, void *dev_id, struct pt_regs *regs) { - stlport_t *portp; - int len, stlen; - char *head, *tail; - unsigned char ioack, srer; + stlbrd_t *brdp; + int i; #if DEBUG - printk("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); + printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs); #endif - ioack = inb(ioaddr + EREG_TXACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { - printk("STALLION: bad TX interrupt ack value=%x\n", ioack); - return; + for (i = 0; (i < stl_nrbrds); i++) { + if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) + continue; + if (brdp->state == 0) + continue; + (* brdp->isr)(brdp); } - portp = panelp->ports[(ioack >> 3)]; +} + +/*****************************************************************************/ /* - * Unfortunately we need to handle breaks in the data stream, since - * this is the only way to generate them on the cd1400. Do it now if - * a break is to be sent. + * Interrupt service routine for EasyIO board types. */ - if (portp->brklen != 0) { - if (portp->brklen > 0) { - outb((TDR + portp->uartaddr), ioaddr); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_DELAY, (ioaddr + EREG_DATA)); - outb(portp->brklen, (ioaddr + EREG_DATA)); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); - portp->brklen = -1; - goto stl_txalldone; - } else { - outb((COR2 + portp->uartaddr), ioaddr); - outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA)); - portp->brklen = 0; - } - } - - head = portp->tx.head; - tail = portp->tx.tail; - len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { - set_bit(ASYI_TXLOW, &portp->istate); - queue_task_irq_off(&portp->tqueue, &tq_scheduler); - } - if (len == 0) { - outb((SRER + portp->uartaddr), ioaddr); - srer = inb(ioaddr + EREG_DATA); - if (srer & SRER_TXDATA) { - srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; - } else { - srer &= ~(SRER_TXDATA | SRER_TXEMPTY); - clear_bit(ASYI_TXBUSY, &portp->istate); - } - outb(srer, (ioaddr + EREG_DATA)); - } else { - len = MIN(len, CD1400_TXFIFOSIZE); - portp->stats.txtotal += len; - stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); - outb((TDR + portp->uartaddr), ioaddr); - outsb((ioaddr + EREG_DATA), tail, stlen); - len -= stlen; - tail += stlen; - if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) - tail = portp->tx.buf; - if (len > 0) { - outsb((ioaddr + EREG_DATA), tail, len); - tail += len; - } - portp->tx.tail = tail; - } +static void stl_eiointr(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int iobase; -stl_txalldone: - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); + panelp = brdp->panels[0]; + iobase = panelp->iobase; + while (inb(brdp->iostatus) & EIO_INTRPEND) + (* panelp->isr)(panelp, iobase); } /*****************************************************************************/ /* - * Receive character interrupt handler. Determine if we have good chars - * or bad chars and then process appropriately. Good chars are easy - * just shove the lot into the RX buffer and set all status byte to 0. - * If a bad RX char then process as required. This routine needs to be - * fast! In practice it is possible that we get an interrupt on a port - * that is closed. This can happen on hangups - since they completely - * shutdown a port not in user context. Need to handle this case. + * Interrupt service routine for ECH-AT board types. */ -static inline void stl_rxisr(stlpanel_t *panelp, int ioaddr) +static void stl_echatintr(stlbrd_t *brdp) { - stlport_t *portp; - struct tty_struct *tty; - unsigned int ioack, len, buflen; - unsigned char status; - char ch; - static char unwanted[CD1400_RXFIFOSIZE]; - -#if DEBUG - printk("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); -#endif + stlpanel_t *panelp; + unsigned int ioaddr; + int bnknr; - ioack = inb(ioaddr + EREG_RXACK); - if ((ioack & panelp->ackmask) != 0) { - printk("STALLION: bad RX interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - tty = portp->tty; + outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { - outb((RDCR + portp->uartaddr), ioaddr); - len = inb(ioaddr + EREG_DATA); - if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) || - ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { - outb((RDSR + portp->uartaddr), ioaddr); - insb((ioaddr + EREG_DATA), &unwanted[0], len); - portp->stats.rxlost += len; - portp->stats.rxtotal += len; - } else { - len = MIN(len, buflen); - if (len > 0) { - outb((RDSR + portp->uartaddr), ioaddr); - insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len); - memset(tty->flip.flag_buf_ptr, 0, len); - tty->flip.flag_buf_ptr += len; - tty->flip.char_buf_ptr += len; - tty->flip.count += len; - tty_schedule_flip(tty); - portp->stats.rxtotal += len; - } - } - } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { - outb((RDSR + portp->uartaddr), ioaddr); - status = inb(ioaddr + EREG_DATA); - ch = inb(ioaddr + EREG_DATA); - if (status & ST_PARITY) - portp->stats.rxparity++; - if (status & ST_FRAMING) - portp->stats.rxframing++; - if (status & ST_OVERRUN) - portp->stats.rxoverrun++; - if (status & ST_BREAK) - portp->stats.rxbreaks++; - if (status & ST_SCHARMASK) { - if ((status & ST_SCHARMASK) == ST_SCHAR1) - portp->stats.txxon++; - if ((status & ST_SCHARMASK) == ST_SCHAR2) - portp->stats.txxoff++; - goto stl_rxalldone; - } - if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) { - if (portp->rxmarkmsk & status) { - if (status & ST_BREAK) { - status = TTY_BREAK; -#ifndef MODULE - if (portp->flags & ASYNC_SAK) { - do_SAK(tty); - BRDENABLE(portp->brdnr, portp->pagenr); - } -#endif - } else if (status & ST_PARITY) { - status = TTY_PARITY; - } else if (status & ST_FRAMING) { - status = TTY_FRAME; - } else if(status & ST_OVERRUN) { - status = TTY_OVERRUN; - } else { - status = 0; - } - } else { - status = 0; - } - if (tty->flip.char_buf_ptr != (char *) NULL) { - if (tty->flip.count < TTY_FLIPBUF_SIZE) { - *tty->flip.flag_buf_ptr++ = status; - *tty->flip.char_buf_ptr++ = ch; - tty->flip.count++; - } - tty_schedule_flip(tty); + while (inb(brdp->iostatus) & ECH_INTRPEND) { + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); } } - } else { - printk("STALLION: bad RX interrupt ack value=%x\n", ioack); - return; } -stl_rxalldone: - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); + outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); } /*****************************************************************************/ /* - * Modem interrupt handler. The is called when the modem signal line - * (DCD) has changed state. Leave most of the work to the off-level - * processing routine. + * Interrupt service routine for ECH-MCA board types. */ -static inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr) +static void stl_echmcaintr(stlbrd_t *brdp) { - stlport_t *portp; - unsigned int ioack; - unsigned char misr; - -#if DEBUG - printk("stl_mdmisr(panelp=%x)\n", (int) panelp); -#endif - - ioack = inb(ioaddr + EREG_MDACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { - printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - - outb((MISR + portp->uartaddr), ioaddr); - misr = inb(ioaddr + EREG_DATA); - if (misr & MISR_DCD) { - set_bit(ASYI_DCDCHANGE, &portp->istate); - queue_task_irq_off(&portp->tqueue, &tq_scheduler); - portp->stats.modem++; + stlpanel_t *panelp; + unsigned int ioaddr; + int bnknr; + + while (inb(brdp->iostatus) & ECH_INTRPEND) { + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } } - - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); } /*****************************************************************************/ /* - * Interrupt handler for EIO and ECH boards. This code ain't all that - * pretty, but the idea is to make it as fast as possible. This code is - * well suited to be assemblerized :-) We don't use the general purpose - * register access functions here, for speed we will go strait to the - * io region. + * Interrupt service routine for ECH-PCI board types. */ -static void stl_intr(int irq, void *dev_id, struct pt_regs *regs) +static void stl_echpciintr(stlbrd_t *brdp) { - stlbrd_t *brdp; stlpanel_t *panelp; - unsigned char svrtype; - int i, panelnr, iobase; - -#if DEBUG - printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs); -#endif - - panelp = (stlpanel_t *) NULL; - for (i = 0; (i < stl_nrbrds); ) { - if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) { - i++; - continue; - } - if (brdp->state == 0) { - i++; - continue; - } -/* - * The following section of code handles the subtle differences - * between board types. It is sort of similar, but different - * enough to handle each separately. - */ - if (brdp->brdtype == BRD_EASYIO) { - if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) { - i++; - continue; - } - panelp = brdp->panels[0]; - iobase = panelp->iobase; - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - if (brdp->nrports > 4) { - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } - } else if (brdp->brdtype == BRD_ECH) { - if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { - i++; - continue; - } - outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - iobase = panelp->iobase; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - iobase += 0x8; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; - } - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } else if (brdp->brdtype == BRD_ECHPCI) { - iobase = brdp->ioaddr2; - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - outb(panelp->pagenr, brdp->ioctrl); - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - outb((panelp->pagenr + 1), brdp->ioctrl); - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; - } - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } else if (brdp->brdtype == BRD_ECHMC) { - if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { - i++; - continue; - } - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - iobase = panelp->iobase; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - iobase += 0x8; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; + unsigned int ioaddr; + int bnknr, recheck; + + while (1) { + recheck = 0; + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl); + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + recheck++; } - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } else { - printk("STALLION: unknown board type=%x\n", brdp->brdtype); - i++; - continue; } + if (! recheck) + break; + } +} + +/*****************************************************************************/ /* - * We have determined what type of service is required for a - * port. From here on in the service of a port is the same no - * matter what the board type... + * Interrupt service routine for ECH-8/64-PCI board types. */ - if (svrtype & SVRR_RX) - stl_rxisr(panelp, iobase); - if (svrtype & SVRR_TX) - stl_txisr(panelp, iobase); - if (svrtype & SVRR_MDM) - stl_mdmisr(panelp, iobase); - if (brdp->brdtype == BRD_ECH) - outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); +static void stl_echpci64intr(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int ioaddr; + int bnknr; + + while (inb(brdp->ioctrl) & 0x1) { + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } } } @@ -1890,6 +1844,7 @@ static void stl_offintr(void *private) unsigned int oldsigs; portp = private; + #if DEBUG printk("stl_offintr(portp=%x)\n", (int) portp); #endif @@ -1901,7 +1856,8 @@ static void stl_offintr(void *private) return; if (test_bit(ASYI_TXLOW, &portp->istate)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); } @@ -1914,7 +1870,7 @@ static void stl_offintr(void *private) if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { if (portp->flags & ASYNC_CHECK_CD) { if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) && - (portp->flags & ASYNC_CALLOUT_NOHUP))) { + (portp->flags & ASYNC_CALLOUT_NOHUP))) { tty_hangup(tty); } } @@ -1925,75 +1881,1218 @@ static void stl_offintr(void *private) /*****************************************************************************/ /* - * Wait for the command register to be ready. We will poll this, - * since it won't usually take too long to be ready. + * Map in interrupt vector to this driver. Check that we don't + * already have this vector mapped, we might be sharing this + * interrupt across multiple boards. */ -static void stl_ccrwait(stlport_t *portp) +static int stl_mapirq(int irq, char *name) { - int i; + int rc, i; - for (i = 0; (i < CCR_MAXWAIT); i++) { - if (stl_getreg(portp, CCR) == 0) { - return; +#if DEBUG + printk("stl_mapirq(irq=%d,name=%s)\n", irq, name); +#endif + + rc = 0; + for (i = 0; (i < stl_numintrs); i++) { + if (stl_gotintrs[i] == irq) + break; + } + if (i >= stl_numintrs) { + if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) { + printk("STALLION: failed to register interrupt " + "routine for %s irq=%d\n", name, irq); + rc = -ENODEV; + } else { + stl_gotintrs[stl_numintrs++] = irq; } } - - printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr); + return(rc); } /*****************************************************************************/ /* - * Set up the cd1400 registers for a port based on the termios port - * settings. + * Initialize all the ports on a panel. */ -static void stl_setport(stlport_t *portp, struct termios *tiosp) +static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) { - stlbrd_t *brdp; - unsigned long flags; - unsigned int clkdiv, baudrate; - unsigned char cor1, cor2, cor3; - unsigned char cor4, cor5, ccr; - unsigned char srer, sreron, sreroff; - unsigned char mcor1, mcor2, rtpr; - unsigned char clk, div; + stlport_t *portp; + int chipmask, i; - cor1 = 0; - cor2 = 0; - cor3 = 0; - cor4 = 0; - cor5 = 0; - ccr = 0; - rtpr = 0; - clk = 0; - div = 0; - mcor1 = 0; - mcor2 = 0; - sreron = 0; - sreroff = 0; +#if DEBUG + printk("stl_initports(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp); +#endif - brdp = stl_brds[portp->brdnr]; - if (brdp == (stlbrd_t *) NULL) - return; + chipmask = stl_panelinit(brdp, panelp); /* - * Set up the RX char ignore mask with those RX error types we - * can ignore. We can get the cd1400 to help us out a little here, - * it will ignore parity errors and breaks for us. + * All UART's are initialized (if found!). Now go through and setup + * each ports data structures. */ - portp->rxignoremsk = 0; - if (tiosp->c_iflag & IGNPAR) { - portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); - cor1 |= COR1_PARIGNORE; - } - if (tiosp->c_iflag & IGNBRK) { - portp->rxignoremsk |= ST_BREAK; - cor4 |= COR4_IGNBRK; + for (i = 0; (i < panelp->nrports); i++) { + portp = (stlport_t *) stl_memalloc(sizeof(stlport_t)); + if (portp == (stlport_t *) NULL) { + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlport_t)); + break; + } + memset(portp, 0, sizeof(stlport_t)); + + portp->magic = STL_PORTMAGIC; + portp->portnr = i; + portp->brdnr = panelp->brdnr; + portp->panelnr = panelp->panelnr; + portp->uartp = panelp->uartp; + portp->clk = brdp->clk; + portp->baud_base = STL_BAUDBASE; + portp->close_delay = STL_CLOSEDELAY; + portp->closing_wait = 30 * HZ; + portp->normaltermios = stl_deftermios; + portp->callouttermios = stl_deftermios; + portp->tqueue.routine = stl_offintr; + portp->tqueue.data = portp; + portp->stats.brd = portp->brdnr; + portp->stats.panel = portp->panelnr; + portp->stats.port = portp->portnr; + panelp->ports[i] = portp; + stl_portinit(brdp, panelp, portp); } - portp->rxmarkmsk = ST_OVERRUN; + return(0); +} + +/*****************************************************************************/ + +/* + * Try to find and initialize an EasyIO board. + */ + +static inline int stl_initeio(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int status; + char *name; + int rc; + +#if DEBUG + printk("stl_initeio(brdp=%x)\n", (int) brdp); +#endif + + brdp->ioctrl = brdp->ioaddr1 + 1; + brdp->iostatus = brdp->ioaddr1 + 2; + + status = inb(brdp->iostatus); + if ((status & EIO_IDBITMASK) == EIO_MK3) + brdp->ioctrl++; + +/* + * Handle board specific stuff now. The real difference is PCI + * or not PCI. + */ + if (brdp->brdtype == BRD_EASYIOPCI) { + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EIO-PCI)"; + outb(0x41, (brdp->ioaddr2 + 0x4c)); + } else { + brdp->iosize1 = 8; + name = "serial(EIO)"; + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + return(-EINVAL); + } + outb((stl_vecmap[brdp->irq] | EIO_0WS | + ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), + brdp->ioctrl); + } + + if (check_region(brdp->ioaddr1, brdp->iosize1)) { + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->ioaddr1); + } + if (brdp->iosize2 > 0) { + if (check_region(brdp->ioaddr2, brdp->iosize2)) { + printk("STALLION: Warning, unit %d I/O address %x " + "conflicts with another device\n", + brdp->brdnr, brdp->ioaddr2); + } + } + +/* + * Everything looks OK, so lets go ahead and probe for the hardware. + */ + brdp->clk = CD1400_CLK; + brdp->isr = stl_eiointr; + + switch (status & EIO_IDBITMASK) { + case EIO_8PORTM: + brdp->clk = CD1400_CLK8M; + /* fall thru */ + case EIO_8PORTRS: + case EIO_8PORTDI: + brdp->nrports = 8; + break; + case EIO_4PORTRS: + brdp->nrports = 4; + break; + case EIO_MK3: + switch (status & EIO_BRDMASK) { + case ID_BRD4: + brdp->nrports = 4; + break; + case ID_BRD8: + brdp->nrports = 8; + break; + case ID_BRD16: + brdp->nrports = 16; + break; + default: + return(-ENODEV); + } + break; + default: + return(-ENODEV); + } + +/* + * We have verfied that the board is actually present, so now we + * can complete the setup. + */ + request_region(brdp->ioaddr1, brdp->iosize1, name); + if (brdp->iosize2 > 0) + request_region(brdp->ioaddr2, brdp->iosize2, name); + + panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); + if (panelp == (stlpanel_t *) NULL) { + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlpanel_t)); + return(-ENOMEM); + } + memset(panelp, 0, sizeof(stlpanel_t)); + + panelp->magic = STL_PANELMAGIC; + panelp->brdnr = brdp->brdnr; + panelp->panelnr = 0; + panelp->nrports = brdp->nrports; + panelp->iobase = brdp->ioaddr1; + panelp->hwid = status; + if ((status & EIO_IDBITMASK) == EIO_MK3) { + panelp->uartp = (void *) &stl_sc26198uart; + panelp->isr = stl_sc26198intr; + } else { + panelp->uartp = (void *) &stl_cd1400uart; + panelp->isr = stl_cd1400eiointr; + } + + brdp->panels[0] = panelp; + brdp->nrpanels = 1; + brdp->state |= BRD_FOUND; + brdp->hwid = status; + rc = stl_mapirq(brdp->irq, name); + return(rc); +} + +/*****************************************************************************/ + +/* + * Try to find an ECH board and initialize it. This code is capable of + * dealing with all types of ECH board. + */ + +static inline int stl_initech(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int status, nxtid, ioaddr, conflict; + int panelnr, banknr, i; + char *name; + +#if DEBUG + printk("stl_initech(brdp=%x)\n", (int) brdp); +#endif + + status = 0; + conflict = 0; + +/* + * Set up the initial board register contents for boards. This varies a + * bit between the different board types. So we need to handle each + * separately. Also do a check that the supplied IRQ is good. + */ + switch (brdp->brdtype) { + + case BRD_ECH: + brdp->isr = stl_echatintr; + brdp->ioctrl = brdp->ioaddr1 + 1; + brdp->iostatus = brdp->ioaddr1 + 1; + status = inb(brdp->iostatus); + if ((status & ECH_IDBITMASK) != ECH_ID) + return(-ENODEV); + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + return(-EINVAL); + } + status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); + status |= (stl_vecmap[brdp->irq] << 1); + outb((status | ECH_BRDRESET), brdp->ioaddr1); + brdp->ioctrlval = ECH_INTENABLE | + ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); + for (i = 0; (i < 10); i++) + outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); + brdp->iosize1 = 2; + brdp->iosize2 = 32; + name = "serial(EC8/32)"; + outb(status, brdp->ioaddr1); + break; + + case BRD_ECHMC: + brdp->isr = stl_echmcaintr; + brdp->ioctrl = brdp->ioaddr1 + 0x20; + brdp->iostatus = brdp->ioctrl; + status = inb(brdp->iostatus); + if ((status & ECH_IDBITMASK) != ECH_ID) + return(-ENODEV); + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + return(-EINVAL); + } + outb(ECHMC_BRDRESET, brdp->ioctrl); + outb(ECHMC_INTENABLE, brdp->ioctrl); + brdp->iosize1 = 64; + name = "serial(EC8/32-MC)"; + break; + + case BRD_ECHPCI: + brdp->isr = stl_echpciintr; + brdp->ioctrl = brdp->ioaddr1 + 2; + brdp->iosize1 = 4; + brdp->iosize2 = 8; + name = "serial(EC8/32-PCI)"; + break; + + case BRD_ECH64PCI: + brdp->isr = stl_echpci64intr; + brdp->ioctrl = brdp->ioaddr2 + 0x40; + outb(0x43, (brdp->ioaddr1 + 0x4c)); + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EC8/64-PCI)"; + break; + + default: + printk("STALLION: unknown board type=%d\n", brdp->brdtype); + return(-EINVAL); + break; + } + +/* + * Check boards for possible IO address conflicts. We won't actually + * do anything about it here, just issue a warning... + */ + conflict = check_region(brdp->ioaddr1, brdp->iosize1) ? + brdp->ioaddr1 : 0; + if ((conflict == 0) && (brdp->iosize2 > 0)) + conflict = check_region(brdp->ioaddr2, brdp->iosize2) ? + brdp->ioaddr2 : 0; + if (conflict) { + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, conflict); + } + + request_region(brdp->ioaddr1, brdp->iosize1, name); + if (brdp->iosize2 > 0) + request_region(brdp->ioaddr2, brdp->iosize2, name); + +/* + * Scan through the secondary io address space looking for panels. + * As we find'em allocate and initialize panel structures for each. + */ + brdp->clk = CD1400_CLK; + brdp->hwid = status; + + ioaddr = brdp->ioaddr2; + banknr = 0; + panelnr = 0; + nxtid = 0; + + for (i = 0; (i < STL_MAXPANELS); i++) { + if (brdp->brdtype == BRD_ECHPCI) { + outb(nxtid, brdp->ioctrl); + ioaddr = brdp->ioaddr2; + } + status = inb(ioaddr + ECH_PNLSTATUS); + if ((status & ECH_PNLIDMASK) != nxtid) + break; + panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); + if (panelp == (stlpanel_t *) NULL) { + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlpanel_t)); + break; + } + memset(panelp, 0, sizeof(stlpanel_t)); + panelp->magic = STL_PANELMAGIC; + panelp->brdnr = brdp->brdnr; + panelp->panelnr = panelnr; + panelp->iobase = ioaddr; + panelp->pagenr = nxtid; + panelp->hwid = status; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS; + + if (status & ECH_PNLXPID) { + panelp->uartp = (void *) &stl_sc26198uart; + panelp->isr = stl_sc26198intr; + if (status & ECH_PNL16PORT) { + panelp->nrports = 16; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + 4 + + ECH_PNLSTATUS; + } else { + panelp->nrports = 8; + } + } else { + panelp->uartp = (void *) &stl_cd1400uart; + panelp->isr = stl_cd1400echintr; + if (status & ECH_PNL16PORT) { + panelp->nrports = 16; + panelp->ackmask = 0x80; + if (brdp->brdtype != BRD_ECHPCI) + ioaddr += EREG_BANKSIZE; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = ++nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + + ECH_PNLSTATUS; + } else { + panelp->nrports = 8; + panelp->ackmask = 0xc0; + } + } + + nxtid++; + ioaddr += EREG_BANKSIZE; + brdp->nrports += panelp->nrports; + brdp->panels[panelnr++] = panelp; + if ((brdp->brdtype != BRD_ECHPCI) && + (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) + break; + } + + brdp->nrpanels = panelnr; + brdp->nrbnks = banknr; + if (brdp->brdtype == BRD_ECH) + outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); + + brdp->state |= BRD_FOUND; + i = stl_mapirq(brdp->irq, name); + return(i); +} + +/*****************************************************************************/ + +/* + * Initialize and configure the specified board. + * Scan through all the boards in the configuration and see what we + * can find. Handle EIO and the ECH boards a little differently here + * since the initial search and setup is very different. + */ + +static int stl_brdinit(stlbrd_t *brdp) +{ + int i; + +#if DEBUG + printk("stl_brdinit(brdp=%x)\n", (int) brdp); +#endif + + switch (brdp->brdtype) { + case BRD_EASYIO: + case BRD_EASYIOPCI: + stl_initeio(brdp); + break; + case BRD_ECH: + case BRD_ECHMC: + case BRD_ECHPCI: + case BRD_ECH64PCI: + stl_initech(brdp); + break; + default: + printk("STALLION: unit=%d is unknown board type=%d\n", + brdp->brdnr, brdp->brdtype); + return(ENODEV); + } + + stl_brds[brdp->brdnr] = brdp; + if ((brdp->state & BRD_FOUND) == 0) { + printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", + stl_brdnames[brdp->brdtype], brdp->brdnr, + brdp->ioaddr1, brdp->irq); + return(ENODEV); + } + + for (i = 0; (i < STL_MAXPANELS); i++) + if (brdp->panels[i] != (stlpanel_t *) NULL) + stl_initports(brdp, brdp->panels[i]); + + printk("STALLION: %s found, unit=%d io=%x irq=%d " + "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], + brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, + brdp->nrports); + return(0); +} + +/*****************************************************************************/ + +#ifdef CONFIG_PCI + +/* + * We have a Stallion board. Allocate a board structure and + * initialize it. Read its IO and IRQ resources from PCI + * configuration space. + */ + +static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr) +{ + unsigned int bar[4]; + stlbrd_t *brdp; + int i, rc; + unsigned char irq; + +#if DEBUG + printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", + brdtype, busnr, devnr); +#endif + + brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); + if (brdp == (stlbrd_t *) NULL) { + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlbrd_t)); + return(-ENOMEM); + } + + memset(brdp, 0, sizeof(stlbrd_t)); + brdp->magic = STL_BOARDMAGIC; + brdp->brdnr = stl_nrbrds++; + brdp->brdtype = brdtype; + +/* + * Read in all the BAR registers from this board. Different Stallion + * boards use these in different ways, so we just read in the whole + * lot and then figure out what is what later. + */ + for (i = 0; (i < 4); i++) { + rc = pcibios_read_config_dword(busnr, devnr, + (PCI_BASE_ADDRESS_0 + (i * 0x4)), &bar[i]); + if (rc) { + printk("STALLION: failed to read BAR register %d " + "from PCI board, errno=%x\n", i, rc); + return(0); + } + } + + rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq); + if (rc) { + printk("STALLION: failed to read INTERRUPT register " + "from PCI board, errno=%x\n", rc); + return(0); + } + +#if DEBUG + printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__, + bar[0], bar[1], bar[2], bar[3], irq); +#endif + +/* + * We have all resources from the board, so lets setup the actual + * board structure now. + */ + switch (brdtype) { + case BRD_ECHPCI: + brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + case BRD_ECH64PCI: + brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + case BRD_EASYIOPCI: + brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + default: + printk("STALLION: unknown PCI board type=%d\n", brdtype); + break; + } + + brdp->irq = irq; + stl_brdinit(brdp); + + return(0); +} + + +/*****************************************************************************/ + +/* + * Find all Stallion PCI boards that might be installed. Initialize each + * one as it is found. + */ + + +static inline int stl_findpcibrds() +{ + unsigned char busnr, devnr; + unsigned short class; + int i, rc, brdtypnr; + +#if DEBUG + printk("stl_findpcibrds()\n"); +#endif + + if (! pcibios_present()) + return(0); + + for (i = 0; (i < stl_nrpcibrds); i++) { + for (brdtypnr = 0; ; brdtypnr++) { + + rc = pcibios_find_device(stl_pcibrds[i].vendid, + stl_pcibrds[i].devid, brdtypnr, &busnr, &devnr); + if (rc) + break; + +/* + * Check that we can handle more boards... + */ + if (stl_nrbrds >= STL_MAXBRDS) { + printk("STALLION: too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + i = stl_nrpcibrds; + break; + } + +/* + * Found a device on the PCI bus that has our vendor and + * device ID. Need to check now that it is really us. + */ + rc = pcibios_read_config_word(busnr, devnr, + PCI_CLASS_DEVICE, &class); + if (rc) { + printk("STALLION: failed to read class type " + "from PCI board, errno=%x\n", rc); + continue; + } + if (class == PCI_CLASS_STORAGE_IDE) + continue; + + rc = stl_initpcibrd(stl_pcibrds[i].brdtype, busnr, + devnr); + if (rc) + return(rc); + } + } + + return(0); +} + +#endif + +/*****************************************************************************/ + +/* + * Scan through all the boards in the configuration and see what we + * can find. Handle EIO and the ECH boards a little differently here + * since the initial search and setup is too different. + */ + +static inline int stl_initbrds() +{ + stlbrd_t *brdp; + stlconf_t *confp; + int i; + +#if DEBUG + printk("stl_initbrds()\n"); +#endif + + if (stl_nrbrds > STL_MAXBRDS) { + printk("STALLION: too many boards in configuration table, " + "truncating to %d\n", STL_MAXBRDS); + stl_nrbrds = STL_MAXBRDS; + } + +/* + * Firstly scan the list of static boards configured. Allocate + * resources and initialize the boards as found. + */ + for (i = 0; (i < stl_nrbrds); i++) { + confp = &stl_brdconf[i]; + brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); + if (brdp == (stlbrd_t *) NULL) { + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlbrd_t)); + return(-ENOMEM); + } + memset(brdp, 0, sizeof(stlbrd_t)); + + brdp->magic = STL_BOARDMAGIC; + brdp->brdnr = i; + brdp->brdtype = confp->brdtype; + brdp->ioaddr1 = confp->ioaddr1; + brdp->ioaddr2 = confp->ioaddr2; + brdp->irq = confp->irq; + brdp->irqtype = confp->irqtype; + stl_brdinit(brdp); + } + +#ifdef CONFIG_PCI +/* + * If the PCI BIOS support is compiled in then let's go looking for + * ECH-PCI boards. + */ + stl_findpcibrds(); +#endif + + return(0); +} + +/*****************************************************************************/ + +/* + * Return the board stats structure to user app. + */ + +static int stl_getbrdstats(combrd_t *bp) +{ + stlbrd_t *brdp; + stlpanel_t *panelp; + int i; + + memcpy_fromfs(&stl_brdstats, bp, sizeof(combrd_t)); + if (stl_brdstats.brd >= STL_MAXBRDS) + return(-ENODEV); + brdp = stl_brds[stl_brdstats.brd]; + if (brdp == (stlbrd_t *) NULL) + return(-ENODEV); + + memset(&stl_brdstats, 0, sizeof(combrd_t)); + stl_brdstats.brd = brdp->brdnr; + stl_brdstats.type = brdp->brdtype; + stl_brdstats.hwid = brdp->hwid; + stl_brdstats.state = brdp->state; + stl_brdstats.ioaddr = brdp->ioaddr1; + stl_brdstats.ioaddr2 = brdp->ioaddr2; + stl_brdstats.irq = brdp->irq; + stl_brdstats.nrpanels = brdp->nrpanels; + stl_brdstats.nrports = brdp->nrports; + for (i = 0; (i < brdp->nrpanels); i++) { + panelp = brdp->panels[i]; + stl_brdstats.panels[i].panel = i; + stl_brdstats.panels[i].hwid = panelp->hwid; + stl_brdstats.panels[i].nrports = panelp->nrports; + } + + memcpy_tofs(bp, &stl_brdstats, sizeof(combrd_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Resolve the referenced port number into a port struct pointer. + */ + +static stlport_t *stl_getport(int brdnr, int panelnr, int portnr) +{ + stlbrd_t *brdp; + stlpanel_t *panelp; + + if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) + return((stlport_t *) NULL); + brdp = stl_brds[brdnr]; + if (brdp == (stlbrd_t *) NULL) + return((stlport_t *) NULL); + if ((panelnr < 0) || (panelnr >= brdp->nrpanels)) + return((stlport_t *) NULL); + panelp = brdp->panels[panelnr]; + if (panelp == (stlpanel_t *) NULL) + return((stlport_t *) NULL); + if ((portnr < 0) || (portnr >= panelp->nrports)) + return((stlport_t *) NULL); + return(panelp->ports[portnr]); +} + +/*****************************************************************************/ + +/* + * Return the port stats structure to user app. A NULL port struct + * pointer passed in means that we need to find out from the app + * what port to get stats for (used through board control device). + */ + +static int stl_getportstats(stlport_t *portp, comstats_t *cp) +{ + unsigned char *head, *tail; + unsigned long flags; + + if (portp == (stlport_t *) NULL) { + memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); + if (portp == (stlport_t *) NULL) + return(-ENODEV); + } + + portp->stats.state = portp->istate; + portp->stats.flags = portp->flags; + portp->stats.hwid = portp->hwid; + + portp->stats.ttystate = 0; + portp->stats.cflags = 0; + portp->stats.iflags = 0; + portp->stats.oflags = 0; + portp->stats.lflags = 0; + portp->stats.rxbuffered = 0; + + save_flags(flags); + cli(); + if (portp->tty != (struct tty_struct *) NULL) { + if (portp->tty->driver_data == portp) { + portp->stats.ttystate = portp->tty->flags; + portp->stats.rxbuffered = portp->tty->flip.count; + if (portp->tty->termios != (struct termios *) NULL) { + portp->stats.cflags = portp->tty->termios->c_cflag; + portp->stats.iflags = portp->tty->termios->c_iflag; + portp->stats.oflags = portp->tty->termios->c_oflag; + portp->stats.lflags = portp->tty->termios->c_lflag; + } + } + } + restore_flags(flags); + + head = portp->tx.head; + tail = portp->tx.tail; + portp->stats.txbuffered = ((head >= tail) ? (head - tail) : + (STL_TXBUFSIZE - (tail - head))); + + portp->stats.signals = (unsigned long) stl_getsignals(portp); + + memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Clear the port stats structure. We also return it zeroed out... + */ + +static int stl_clrportstats(stlport_t *portp, comstats_t *cp) +{ + if (portp == (stlport_t *) NULL) { + memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); + if (portp == (stlport_t *) NULL) + return(-ENODEV); + } + + memset(&portp->stats, 0, sizeof(comstats_t)); + portp->stats.brd = portp->brdnr; + portp->stats.panel = portp->panelnr; + portp->stats.port = portp->portnr; + memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Return the entire driver ports structure to a user app. + */ + +static int stl_getportstruct(unsigned long arg) +{ + stlport_t *portp; + + memcpy_fromfs(&stl_dummyport, (void *) arg, sizeof(stlport_t)); + portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr, + stl_dummyport.portnr); + if (portp == (stlport_t *) NULL) + return(-ENODEV); + memcpy_tofs((void *) arg, portp, sizeof(stlport_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Return the entire driver board structure to a user app. + */ + +static int stl_getbrdstruct(unsigned long arg) +{ + stlbrd_t *brdp; + + memcpy_fromfs(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t)); + if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS)) + return(-ENODEV); + brdp = stl_brds[stl_dummybrd.brdnr]; + if (brdp == (stlbrd_t *) NULL) + return(-ENODEV); + memcpy_tofs((void *) arg, brdp, sizeof(stlbrd_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Memory device open code. Need to keep track of opens and close + * for module handling. + */ + +static int stl_memopen(struct inode *ip, struct file *fp) +{ + MOD_INC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +static void stl_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; +} + +/*****************************************************************************/ + +/* + * The "staliomem" device is also required to do some special operations + * on the board and/or ports. In this driver it is mostly used for stats + * collection. + */ + +static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) +{ + int brdnr, rc; + +#if DEBUG + printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, + (int) fp, cmd, (int) arg); +#endif + + brdnr = MINOR(ip->i_rdev); + if (brdnr >= STL_MAXBRDS) + return(-ENODEV); + rc = 0; + + switch (cmd) { + case COM_GETPORTSTATS: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stl_getportstats((stlport_t *) NULL, + (comstats_t *) arg); + break; + case COM_CLRPORTSTATS: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stl_clrportstats((stlport_t *) NULL, + (comstats_t *) arg); + break; + case COM_GETBRDSTATS: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(combrd_t))) == 0) + rc = stl_getbrdstats((combrd_t *) arg); + break; + case COM_READPORT: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlport_t))) == 0) + rc = stl_getportstruct(arg); + break; + case COM_READBOARD: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlbrd_t))) == 0) + rc = stl_getbrdstruct(arg); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + + return(rc); +} + +/*****************************************************************************/ + +int stl_init(void) +{ + printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); + + stl_initbrds(); + +/* + * Allocate a temporary write buffer. + */ + stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE); + if (stl_tmpwritebuf == (char *) NULL) + printk("STALLION: failed to allocate memory (size=%d)\n", + STL_TXBUFSIZE); + +/* + * Set up a character driver for per board stuff. This is mainly used + * to do stats ioctls on the ports. + */ + if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) + printk("STALLION: failed to register serial board device\n"); + +/* + * Set up the tty driver structure and register us as a driver. + * Also setup the callout tty device. + */ + memset(&stl_serial, 0, sizeof(struct tty_driver)); + stl_serial.magic = TTY_DRIVER_MAGIC; + stl_serial.name = stl_serialname; + stl_serial.major = STL_SERIALMAJOR; + stl_serial.minor_start = 0; + stl_serial.num = STL_MAXBRDS * STL_MAXPORTS; + stl_serial.type = TTY_DRIVER_TYPE_SERIAL; + stl_serial.subtype = STL_DRVTYPSERIAL; + stl_serial.init_termios = stl_deftermios; + stl_serial.flags = TTY_DRIVER_REAL_RAW; + stl_serial.refcount = &stl_refcount; + stl_serial.table = stl_ttys; + stl_serial.termios = stl_termios; + stl_serial.termios_locked = stl_termioslocked; + + stl_serial.open = stl_open; + stl_serial.close = stl_close; + stl_serial.write = stl_write; + stl_serial.put_char = stl_putchar; + stl_serial.flush_chars = stl_flushchars; + stl_serial.write_room = stl_writeroom; + stl_serial.chars_in_buffer = stl_charsinbuffer; + stl_serial.ioctl = stl_ioctl; + stl_serial.set_termios = stl_settermios; + stl_serial.throttle = stl_throttle; + stl_serial.unthrottle = stl_unthrottle; + stl_serial.stop = stl_stop; + stl_serial.start = stl_start; + stl_serial.hangup = stl_hangup; + stl_serial.flush_buffer = stl_flushbuffer; + + stl_callout = stl_serial; + stl_callout.name = stl_calloutname; + stl_callout.major = STL_CALLOUTMAJOR; + stl_callout.subtype = STL_DRVTYPCALLOUT; + + if (tty_register_driver(&stl_serial)) + printk("STALLION: failed to register serial driver\n"); + if (tty_register_driver(&stl_callout)) + printk("STALLION: failed to register callout driver\n"); + + return(0); +} + +/*****************************************************************************/ +/* CD1400 HARDWARE FUNCTIONS */ +/*****************************************************************************/ + +/* + * These functions get/set/update the registers of the cd1400 UARTs. + * Access to the cd1400 registers is via an address/data io port pair. + * (Maybe should make this inline...) + */ + +static int stl_cd1400getreg(stlport_t *portp, int regnr) +{ + outb((regnr + portp->uartaddr), portp->ioaddr); + return(inb(portp->ioaddr + EREG_DATA)); +} + +static void stl_cd1400setreg(stlport_t *portp, int regnr, int value) +{ + outb((regnr + portp->uartaddr), portp->ioaddr); + outb(value, portp->ioaddr + EREG_DATA); +} + +static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value) +{ + outb((regnr + portp->uartaddr), portp->ioaddr); + if (inb(portp->ioaddr + EREG_DATA) != value) { + outb(value, portp->ioaddr + EREG_DATA); + return(1); + } + return(0); +} + +/*****************************************************************************/ + +/* + * Inbitialize the UARTs in a panel. We don't care what sort of board + * these ports are on - since the port io registers are almost + * identical when dealing with ports. + */ + +static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp) +{ + unsigned int gfrcr; + int chipmask, i, j; + int nrchips, uartaddr, ioaddr; + +#if DEBUG + printk("stl_panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp); +#endif + + BRDENABLE(panelp->brdnr, panelp->pagenr); + +/* + * Check that each chip is present and started up OK. + */ + chipmask = 0; + nrchips = panelp->nrports / CD1400_PORTS; + for (i = 0; (i < nrchips); i++) { + if (brdp->brdtype == BRD_ECHPCI) { + outb((panelp->pagenr + (i >> 1)), brdp->ioctrl); + ioaddr = panelp->iobase; + } else { + ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); + } + uartaddr = (i & 0x01) ? 0x080 : 0; + outb((GFRCR + uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); + outb((CCR + uartaddr), ioaddr); + outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); + outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); + outb((GFRCR + uartaddr), ioaddr); + for (j = 0; (j < CCR_MAXWAIT); j++) { + if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0) + break; + } + if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) { + printk("STALLION: cd1400 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); + continue; + } + chipmask |= (0x1 << i); + outb((PPR + uartaddr), ioaddr); + outb(PPR_SCALAR, (ioaddr + EREG_DATA)); + } + + BRDDISABLE(panelp->brdnr); + return(chipmask); +} + +/*****************************************************************************/ + +/* + * Initialize hardware specific port registers. + */ + +static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp) +{ +#if DEBUG + printk("stl_cd1400portinit(brdp=%x,panelp=%x,portp=%x)\n", + (int) brdp, (int) panelp, (int) portp); +#endif + + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) + return; + + portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) || + (portp->portnr < 8)) ? 0 : EREG_BANKSIZE); + portp->uartaddr = (portp->portnr & 0x04) << 5; + portp->pagenr = panelp->pagenr + (portp->portnr >> 3); + + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, LIVR, (portp->portnr << 3)); + portp->hwid = stl_cd1400getreg(portp, GFRCR); + BRDDISABLE(portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Wait for the command register to be ready. We will poll this, + * since it won't usually take too long to be ready. + */ + +static void stl_cd1400ccrwait(stlport_t *portp) +{ + int i; + + for (i = 0; (i < CCR_MAXWAIT); i++) { + if (stl_cd1400getreg(portp, CCR) == 0) { + return; + } + } + + printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Set up the cd1400 registers for a port based on the termios port + * settings. + */ + +static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp) +{ + stlbrd_t *brdp; + unsigned long flags; + unsigned int clkdiv, baudrate; + unsigned char cor1, cor2, cor3; + unsigned char cor4, cor5, ccr; + unsigned char srer, sreron, sreroff; + unsigned char mcor1, mcor2, rtpr; + unsigned char clk, div; + + cor1 = 0; + cor2 = 0; + cor3 = 0; + cor4 = 0; + cor5 = 0; + ccr = 0; + rtpr = 0; + clk = 0; + div = 0; + mcor1 = 0; + mcor2 = 0; + sreron = 0; + sreroff = 0; + + brdp = stl_brds[portp->brdnr]; + if (brdp == (stlbrd_t *) NULL) + return; + +/* + * Set up the RX char ignore mask with those RX error types we + * can ignore. We can get the cd1400 to help us out a little here, + * it will ignore parity errors and breaks for us. + */ + portp->rxignoremsk = 0; + if (tiosp->c_iflag & IGNPAR) { + portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); + cor1 |= COR1_PARIGNORE; + } + if (tiosp->c_iflag & IGNBRK) { + portp->rxignoremsk |= ST_BREAK; + cor4 |= COR4_IGNBRK; + } + + portp->rxmarkmsk = ST_OVERRUN; if (tiosp->c_iflag & (INPCK | PARMRK)) portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING); if (tiosp->c_iflag & BRKINT) @@ -2064,8 +3163,8 @@ static void stl_setport(stlport_t *portp, struct termios *tiosp) else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } - if (baudrate > STL_MAXBAUD) - baudrate = STL_MAXBAUD; + if (baudrate > STL_CD1400MAXBAUD) + baudrate = STL_CD1400MAXBAUD; if (baudrate > 0) { for (clk = 0; (clk < CD1400_NUMCLKS); clk++) { @@ -2107,159 +3206,390 @@ static void stl_setport(stlport_t *portp, struct termios *tiosp) } /* - * All register cd1400 register values calculated so go through and set - * them all up. + * All cd1400 register values calculated so go through and set + * them all up. + */ + +#if DEBUG + printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); + printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", + cor1, cor2, cor3, cor4, cor5); + printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", + mcor1, mcor2, rtpr, sreron, sreroff); + printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); + printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); +#endif + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3)); + srer = stl_cd1400getreg(portp, SRER); + stl_cd1400setreg(portp, SRER, 0); + if (stl_cd1400updatereg(portp, COR1, cor1)) + ccr = 1; + if (stl_cd1400updatereg(portp, COR2, cor2)) + ccr = 1; + if (stl_cd1400updatereg(portp, COR3, cor3)) + ccr = 1; + if (ccr) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_CORCHANGE); + } + stl_cd1400setreg(portp, COR4, cor4); + stl_cd1400setreg(portp, COR5, cor5); + stl_cd1400setreg(portp, MCOR1, mcor1); + stl_cd1400setreg(portp, MCOR2, mcor2); + if (baudrate > 0) { + stl_cd1400setreg(portp, TCOR, clk); + stl_cd1400setreg(portp, TBPR, div); + stl_cd1400setreg(portp, RCOR, clk); + stl_cd1400setreg(portp, RBPR, div); + } + stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]); + stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); + stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]); + stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); + stl_cd1400setreg(portp, RTPR, rtpr); + mcor1 = stl_cd1400getreg(portp, MSVR1); + if (mcor1 & MSVR1_DCD) + portp->sigs |= TIOCM_CD; + else + portp->sigs &= ~TIOCM_CD; + stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron)); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + +/* + * Set the state of the DTR and RTS signals. + */ + +static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts) +{ + unsigned char msvr1, msvr2; + unsigned long flags; + +#if DEBUG + printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); +#endif + + msvr1 = 0; + msvr2 = 0; + if (dtr > 0) + msvr1 = MSVR1_DTR; + if (rts > 0) + msvr2 = MSVR2_RTS; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + if (rts >= 0) + stl_cd1400setreg(portp, MSVR2, msvr2); + if (dtr >= 0) + stl_cd1400setreg(portp, MSVR1, msvr1); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + +/* + * Return the state of the signals. + */ + +static int stl_cd1400getsignals(stlport_t *portp) +{ + unsigned char msvr1, msvr2; + unsigned long flags; + int sigs; + +#if DEBUG + printk("stl_cd1400getsignals(portp=%x)\n", (int) portp); +#endif + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + msvr1 = stl_cd1400getreg(portp, MSVR1); + msvr2 = stl_cd1400getreg(portp, MSVR2); + BRDDISABLE(portp->brdnr); + restore_flags(flags); + + sigs = 0; + sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; + sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; + sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; + sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; +#if 0 + sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; + sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; +#else + sigs |= TIOCM_DSR; +#endif + return(sigs); +} + +/*****************************************************************************/ + +/* + * Enable/Disable the Transmitter and/or Receiver. + */ + +static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx) +{ + unsigned char ccr; + unsigned long flags; + +#if DEBUG + printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); +#endif + ccr = 0; + + if (tx == 0) + ccr |= CCR_TXDISABLE; + else if (tx > 0) + ccr |= CCR_TXENABLE; + if (rx == 0) + ccr |= CCR_RXDISABLE; + else if (rx > 0) + ccr |= CCR_RXENABLE; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, ccr); + stl_cd1400ccrwait(portp); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + +/* + * Start/stop the Transmitter and/or Receiver. */ +static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx) +{ + unsigned char sreron, sreroff; + unsigned long flags; + #if DEBUG - printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr); - printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5); - printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff); - printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); - printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); + printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif + sreron = 0; + sreroff = 0; + if (tx == 0) + sreroff |= (SRER_TXDATA | SRER_TXEMPTY); + else if (tx == 1) + sreron |= SRER_TXDATA; + else if (tx >= 2) + sreron |= SRER_TXEMPTY; + if (rx == 0) + sreroff |= SRER_RXDATA; + else if (rx > 0) + sreron |= SRER_RXDATA; + save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x3)); - srer = stl_getreg(portp, SRER); - stl_setreg(portp, SRER, 0); - if (stl_updatereg(portp, COR1, cor1)) - ccr = 1; - if (stl_updatereg(portp, COR2, cor2)) - ccr = 1; - if (stl_updatereg(portp, COR3, cor3)) - ccr = 1; - if (ccr) { - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_CORCHANGE); - } - stl_setreg(portp, COR4, cor4); - stl_setreg(portp, COR5, cor5); - stl_setreg(portp, MCOR1, mcor1); - stl_setreg(portp, MCOR2, mcor2); - if (baudrate > 0) { - stl_setreg(portp, TCOR, clk); - stl_setreg(portp, TBPR, div); - stl_setreg(portp, RCOR, clk); - stl_setreg(portp, RBPR, div); - } - stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]); - stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); - stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]); - stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); - stl_setreg(portp, RTPR, rtpr); - mcor1 = stl_getreg(portp, MSVR1); - if (mcor1 & MSVR1_DCD) - portp->sigs |= TIOCM_CD; - else - portp->sigs &= ~TIOCM_CD; - stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron)); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); BRDDISABLE(portp->brdnr); + if (tx > 0) + set_bit(ASYI_TXBUSY, &portp->istate); restore_flags(flags); } /*****************************************************************************/ /* - * Set the state of the DTR and RTS signals. + * Disable all interrupts from this port. */ -static void stl_setsignals(stlport_t *portp, int dtr, int rts) +static void stl_cd1400disableintrs(stlport_t *portp) { - unsigned char msvr1, msvr2; unsigned long flags; #if DEBUG - printk("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts); + printk("stl_cd1400disableintrs(portp=%x)\n", (int) portp); #endif + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, SRER, 0); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} - msvr1 = 0; - msvr2 = 0; - if (dtr > 0) - msvr1 = MSVR1_DTR; - if (rts > 0) - msvr2 = MSVR2_RTS; +/*****************************************************************************/ + +static void stl_cd1400sendbreak(stlport_t *portp, long len) +{ + unsigned long flags; + +#if DEBUG + printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", + (int) portp, (int) len); +#endif save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (rts >= 0) - stl_setreg(portp, MSVR2, msvr2); - if (dtr >= 0) - stl_setreg(portp, MSVR1, msvr1); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, COR2, + (stl_cd1400getreg(portp, COR2) | COR2_ETC)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY)); BRDDISABLE(portp->brdnr); + len = len / 5; + portp->brklen = (len > 255) ? 255 : len; + portp->stats.txbreaks++; restore_flags(flags); } /*****************************************************************************/ /* - * Return the state of the signals. + * Take flow control actions... */ -static int stl_getsignals(stlport_t *portp) +static void stl_cd1400flowctrl(stlport_t *portp, int state) { - unsigned char msvr1, msvr2; - unsigned long flags; - int sigs; + struct tty_struct *tty; + unsigned long flags; #if DEBUG - printk("stl_getsignals(portp=%x)\n", (int) portp); + printk("stl_cd1400flowctrl(portp=%x,state=%x)\n", (int) portp, state); #endif + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; + save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - msvr1 = stl_getreg(portp, MSVR1); - msvr2 = stl_getreg(portp, MSVR2); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + + if (state) { + if (tty->termios->c_iflag & IXOFF) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); + portp->stats.rxxon++; + stl_cd1400ccrwait(portp); + } +/* + * Question: should we return RTS to what it was before? It may + * have been set by an ioctl... Suppose not, since if you have + * hardware flow control set then it is pretty silly to go and + * set the RTS line by hand. + */ + if (tty->termios->c_cflag & CRTSCTS) { + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) | + FIFO_RTSTHRESHOLD)); + stl_cd1400setreg(portp, MSVR2, MSVR2_RTS); + portp->stats.rxrtson++; + } + } else { + if (tty->termios->c_iflag & IXOFF) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); + portp->stats.rxxoff++; + stl_cd1400ccrwait(portp); + } + if (tty->termios->c_cflag & CRTSCTS) { + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) & 0xf0)); + stl_cd1400setreg(portp, MSVR2, 0); + portp->stats.rxrtsoff++; + } + } + BRDDISABLE(portp->brdnr); - sigs = 0; - sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; - sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; - sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; - sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; - sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; - sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; restore_flags(flags); - return(sigs); } /*****************************************************************************/ /* - * Enable/Disable the Transmitter and/or Receiver. + * Send a flow control character... */ -static void stl_enablerxtx(stlport_t *portp, int rx, int tx) +static void stl_cd1400sendflow(stlport_t *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + +#if DEBUG + printk("stl_cd1400sendflow(portp=%x,state=%x)\n", (int) portp, state); +#endif + + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + if (state) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); + portp->stats.rxxon++; + stl_cd1400ccrwait(portp); + } else { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); + portp->stats.rxxoff++; + stl_cd1400ccrwait(portp); + } + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + +static void stl_cd1400flush(stlport_t *portp) { - unsigned char ccr; unsigned long flags; #if DEBUG - printk("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_cd1400flush(portp=%x)\n", (int) portp); #endif - ccr = 0; - if (tx == 0) - ccr |= CCR_TXDISABLE; - else if (tx > 0) - ccr |= CCR_TXENABLE; - if (rx == 0) - ccr |= CCR_RXDISABLE; - else if (rx > 0) - ccr |= CCR_RXENABLE; + if (portp == (stlport_t *) NULL) + return; save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_ccrwait(portp); - stl_setreg(portp, CCR, ccr); - stl_ccrwait(portp); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO); + stl_cd1400ccrwait(portp); + portp->tx.tail = portp->tx.head; BRDDISABLE(portp->brdnr); restore_flags(flags); } @@ -2267,137 +3597,400 @@ static void stl_enablerxtx(stlport_t *portp, int rx, int tx) /*****************************************************************************/ /* - * Start/stop the Transmitter and/or Receiver. + * Return the current state of data flow on this port. This is only + * really interresting when determining if data has fully completed + * transmission or not... This is easy for the cd1400, it accurately + * maintains the busy port flag. + */ + +static int stl_cd1400datastate(stlport_t *portp) +{ +#if DEBUG + printk("stl_cd1400datastate(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return(0); + + return(test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0); +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for cd1400 EasyIO boards. + */ + +static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase) +{ + unsigned char svrtype; + +#if DEBUG + printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n", + (int) panelp, iobase); +#endif + + outb(SVRR, iobase); + svrtype = inb(iobase + EREG_DATA); + if (panelp->nrports > 4) { + outb((SVRR + 0x80), iobase); + svrtype |= inb(iobase + EREG_DATA); + } + + if (svrtype & SVRR_RX) + stl_cd1400rxisr(panelp, iobase); + else if (svrtype & SVRR_TX) + stl_cd1400txisr(panelp, iobase); + else if (svrtype & SVRR_MDM) + stl_cd1400mdmisr(panelp, iobase); +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for cd1400 panels. + */ + +static void stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase) +{ + unsigned char svrtype; + +#if DEBUG + printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp, + iobase); +#endif + + outb(SVRR, iobase); + svrtype = inb(iobase + EREG_DATA); + outb((SVRR + 0x80), iobase); + svrtype |= inb(iobase + EREG_DATA); + if (svrtype & SVRR_RX) + stl_cd1400rxisr(panelp, iobase); + else if (svrtype & SVRR_TX) + stl_cd1400txisr(panelp, iobase); + else if (svrtype & SVRR_MDM) + stl_cd1400mdmisr(panelp, iobase); +} + +/*****************************************************************************/ + +/* + * Transmit interrupt handler. This has gotta be fast! Handling TX + * chars is pretty simple, stuff as many as possible from the TX buffer + * into the cd1400 FIFO. Must also handle TX breaks here, since they + * are embedded as commands in the data stream. Oh no, had to use a goto! + * This could be optimized more, will do when I get time... + * In practice it is possible that interrupts are enabled but that the + * port has been hung up. Need to handle not having any TX buffer here, + * this is done by using the side effect that head and tail will also + * be NULL if the buffer has been freed. + */ + +static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr) +{ + stlport_t *portp; + int len, stlen; + char *head, *tail; + unsigned char ioack, srer; + +#if DEBUG + printk("stl_cd1400txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); +#endif + + ioack = inb(ioaddr + EREG_TXACK); + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { + printk("STALLION: bad TX interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + +/* + * Unfortunately we need to handle breaks in the data stream, since + * this is the only way to generate them on the cd1400. Do it now if + * a break is to be sent. + */ + if (portp->brklen != 0) { + if (portp->brklen > 0) { + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_DELAY, (ioaddr + EREG_DATA)); + outb(portp->brklen, (ioaddr + EREG_DATA)); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); + portp->brklen = -1; + goto stl_txalldone; + } else { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), + (ioaddr + EREG_DATA)); + portp->brklen = 0; + } + } + + head = portp->tx.head; + tail = portp->tx.tail; + len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + set_bit(ASYI_TXLOW, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + } + + if (len == 0) { + outb((SRER + portp->uartaddr), ioaddr); + srer = inb(ioaddr + EREG_DATA); + if (srer & SRER_TXDATA) { + srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; + } else { + srer &= ~(SRER_TXDATA | SRER_TXEMPTY); + clear_bit(ASYI_TXBUSY, &portp->istate); + } + outb(srer, (ioaddr + EREG_DATA)); + } else { + len = MIN(len, CD1400_TXFIFOSIZE); + portp->stats.txtotal += len; + stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); + outb((TDR + portp->uartaddr), ioaddr); + outsb((ioaddr + EREG_DATA), tail, stlen); + len -= stlen; + tail += stlen; + if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) + tail = portp->tx.buf; + if (len > 0) { + outsb((ioaddr + EREG_DATA), tail, len); + tail += len; + } + portp->tx.tail = tail; + } + +stl_txalldone: + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); +} + +/*****************************************************************************/ + +/* + * Receive character interrupt handler. Determine if we have good chars + * or bad chars and then process appropriately. Good chars are easy + * just shove the lot into the RX buffer and set all status byte to 0. + * If a bad RX char then process as required. This routine needs to be + * fast! In practice it is possible that we get an interrupt on a port + * that is closed. This can happen on hangups - since they completely + * shutdown a port not in user context. Need to handle this case. */ -static void stl_startrxtx(stlport_t *portp, int rx, int tx) +static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr) { - unsigned char sreron, sreroff; - unsigned long flags; + stlport_t *portp; + struct tty_struct *tty; + unsigned int ioack, len, buflen; + unsigned char status; + char ch; #if DEBUG - printk("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_cd1400rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); #endif - sreron = 0; - sreroff = 0; - if (tx == 0) - sreroff |= (SRER_TXDATA | SRER_TXEMPTY); - else if (tx == 1) - sreron |= SRER_TXDATA; - else if (tx >= 2) - sreron |= SRER_TXEMPTY; - if (rx == 0) - sreroff |= SRER_RXDATA; - else if (rx > 0) - sreron |= SRER_RXDATA; + ioack = inb(ioaddr + EREG_RXACK); + if ((ioack & panelp->ackmask) != 0) { + printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + tty = portp->tty; - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron)); - BRDDISABLE(portp->brdnr); - if (tx > 0) - set_bit(ASYI_TXBUSY, &portp->istate); - restore_flags(flags); + if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { + outb((RDCR + portp->uartaddr), ioaddr); + len = inb(ioaddr + EREG_DATA); + if ((tty == (struct tty_struct *) NULL) || + (tty->flip.char_buf_ptr == (char *) NULL) || + ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + outb((RDSR + portp->uartaddr), ioaddr); + insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); + portp->stats.rxlost += len; + portp->stats.rxtotal += len; + } else { + len = MIN(len, buflen); + if (len > 0) { + outb((RDSR + portp->uartaddr), ioaddr); + insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len); + memset(tty->flip.flag_buf_ptr, 0, len); + tty->flip.flag_buf_ptr += len; + tty->flip.char_buf_ptr += len; + tty->flip.count += len; + tty_schedule_flip(tty); + portp->stats.rxtotal += len; + } + } + } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { + outb((RDSR + portp->uartaddr), ioaddr); + status = inb(ioaddr + EREG_DATA); + ch = inb(ioaddr + EREG_DATA); + if (status & ST_PARITY) + portp->stats.rxparity++; + if (status & ST_FRAMING) + portp->stats.rxframing++; + if (status & ST_OVERRUN) + portp->stats.rxoverrun++; + if (status & ST_BREAK) + portp->stats.rxbreaks++; + if (status & ST_SCHARMASK) { + if ((status & ST_SCHARMASK) == ST_SCHAR1) + portp->stats.txxon++; + if ((status & ST_SCHARMASK) == ST_SCHAR2) + portp->stats.txxoff++; + goto stl_rxalldone; + } + if ((tty != (struct tty_struct *) NULL) && + ((portp->rxignoremsk & status) == 0)) { + if (portp->rxmarkmsk & status) { + if (status & ST_BREAK) { + status = TTY_BREAK; + if (portp->flags & ASYNC_SAK) { + do_SAK(tty); + BRDENABLE(portp->brdnr, portp->pagenr); + } + } else if (status & ST_PARITY) { + status = TTY_PARITY; + } else if (status & ST_FRAMING) { + status = TTY_FRAME; + } else if(status & ST_OVERRUN) { + status = TTY_OVERRUN; + } else { + status = 0; + } + } else { + status = 0; + } + if (tty->flip.char_buf_ptr != (char *) NULL) { + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.flag_buf_ptr++ = status; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + } + tty_schedule_flip(tty); + } + } + } else { + printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + return; + } + +stl_rxalldone: + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); } /*****************************************************************************/ /* - * Disable all interrupts from this port. + * Modem interrupt handler. The is called when the modem signal line + * (DCD) has changed state. Leave most of the work to the off-level + * processing routine. */ -static void stl_disableintrs(stlport_t *portp) +static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr) { - unsigned long flags; + stlport_t *portp; + unsigned int ioack; + unsigned char misr; #if DEBUG - printk("stl_disableintrs(portp=%x)\n", (int) portp); + printk("stl_cd1400mdmisr(panelp=%x)\n", (int) panelp); #endif - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_setreg(portp, SRER, 0); - BRDDISABLE(portp->brdnr); - restore_flags(flags); + + ioack = inb(ioaddr + EREG_MDACK); + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { + printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + + outb((MISR + portp->uartaddr), ioaddr); + misr = inb(ioaddr + EREG_DATA); + if (misr & MISR_DCD) { + set_bit(ASYI_DCDCHANGE, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + portp->stats.modem++; + } + + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); } /*****************************************************************************/ +/* SC26198 HARDWARE FUNCTIONS */ +/*****************************************************************************/ + +/* + * These functions get/set/update the registers of the sc26198 UARTs. + * Access to the sc26198 registers is via an address/data io port pair. + * (Maybe should make this inline...) + */ -static void stl_sendbreak(stlport_t *portp, long len) +static int stl_sc26198getreg(stlport_t *portp, int regnr) { - unsigned long flags; + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + return(inb(portp->ioaddr + XP_DATA)); +} -#if DEBUG - printk("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); -#endif +static void stl_sc26198setreg(stlport_t *portp, int regnr, int value) +{ + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + outb(value, (portp->ioaddr + XP_DATA)); +} - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC)); - stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY)); - BRDDISABLE(portp->brdnr); - len = len / 5; - portp->brklen = (len > 255) ? 255 : len; - portp->stats.txbreaks++; - restore_flags(flags); +static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value) +{ + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + if (inb(portp->ioaddr + XP_DATA) != value) { + outb(value, (portp->ioaddr + XP_DATA)); + return(1); + } + return(0); } /*****************************************************************************/ /* - * Map in interrupt vector to this driver. Check that we don't - * already have this vector mapped, we might be sharing this - * interrupt across multiple boards. + * Functions to get and set the sc26198 global registers. */ -static int stl_mapirq(int irq) +static int stl_sc26198getglobreg(stlport_t *portp, int regnr) { - int rc, i; - -#if DEBUG - printk("stl_mapirq(irq=%d)\n", irq); -#endif + outb(regnr, (portp->ioaddr + XP_ADDR)); + return(inb(portp->ioaddr + XP_DATA)); +} - rc = 0; - for (i = 0; (i < stl_numintrs); i++) { - if (stl_gotintrs[i] == irq) - break; - } - if (i >= stl_numintrs) { - if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) { - printk("STALLION: failed to register interrupt routine for irq=%d\n", irq); - rc = -ENODEV; - } else { - stl_gotintrs[stl_numintrs++] = irq; - } - } - return(rc); +#if 0 +static void stl_sc26198setglobreg(stlport_t *portp, int regnr, int value) +{ + outb(regnr, (portp->ioaddr + XP_ADDR)); + outb(value, (portp->ioaddr + XP_DATA)); } +#endif /*****************************************************************************/ /* - * Try to find and initialize all the ports on a panel. We don't care - * what sort of board these ports are on - since the port io registers - * are almost identical when dealing with ports. + * Inbitialize the UARTs in a panel. We don't care what sort of board + * these ports are on - since the port io registers are almost + * identical when dealing with ports. */ -static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) +static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp) { - stlport_t *portp; - unsigned int chipmask; - unsigned int gfrcr; - int nrchips, uartaddr, ioaddr; - int i, j; + int chipmask, i; + int nrchips, ioaddr; #if DEBUG - printk("stl_initports(panelp=%x)\n", (int) panelp); + printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", + (int) brdp, (int) panelp); #endif BRDENABLE(panelp->brdnr, panelp->pagenr); @@ -2406,773 +3999,959 @@ static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) * Check that each chip is present and started up OK. */ chipmask = 0; - nrchips = panelp->nrports / CD1400_PORTS; - for (i = 0; (i < nrchips); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb((panelp->pagenr + (i >> 1)), brdp->ioctrl); - ioaddr = panelp->iobase; - } else { - ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); - } - uartaddr = (i & 0x01) ? 0x080 : 0; - outb((GFRCR + uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); - outb((CCR + uartaddr), ioaddr); - outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); - outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); - outb((GFRCR + uartaddr), ioaddr); - for (j = 0; (j < CCR_MAXWAIT); j++) { - if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0) - break; - } - if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) { - printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i); - continue; - } - chipmask |= (0x1 << i); - outb((PPR + uartaddr), ioaddr); - outb(PPR_SCALAR, (ioaddr + EREG_DATA)); - } + nrchips = (panelp->nrports + 4) / SC26198_PORTS; + if (brdp->brdtype == BRD_ECHPCI) + outb(panelp->pagenr, brdp->ioctrl); -/* - * All cd1400's are initialized (if found!). Now go through and setup - * each ports data structures. Also init the LIVR register of cd1400 - * for each port. - */ - ioaddr = panelp->iobase; - for (i = 0; (i < panelp->nrports); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb((panelp->pagenr + (i >> 3)), brdp->ioctrl); - ioaddr = panelp->iobase; - } else { - ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3)); - } - if ((chipmask & (0x1 << (i / 4))) == 0) - continue; - portp = (stlport_t *) stl_memalloc(sizeof(stlport_t)); - if (portp == (stlport_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t)); - break; - } - memset(portp, 0, sizeof(stlport_t)); - portp->magic = STL_PORTMAGIC; - portp->portnr = i; - portp->brdnr = panelp->brdnr; - portp->panelnr = panelp->panelnr; - portp->ioaddr = ioaddr; - portp->uartaddr = (i & 0x04) << 5; - portp->pagenr = panelp->pagenr + (i >> 3); - portp->clk = brdp->clk; - portp->baud_base = STL_BAUDBASE; - portp->close_delay = STL_CLOSEDELAY; - portp->closing_wait = 30 * HZ; - portp->normaltermios = stl_deftermios; - portp->callouttermios = stl_deftermios; - portp->tqueue.routine = stl_offintr; - portp->tqueue.data = portp; - portp->stats.brd = portp->brdnr; - portp->stats.panel = portp->panelnr; - portp->stats.port = portp->portnr; - stl_setreg(portp, CAR, (i & 0x03)); - stl_setreg(portp, LIVR, (i << 3)); - portp->hwid = stl_getreg(portp, GFRCR); - panelp->ports[i] = portp; + for (i = 0; (i < nrchips); i++) { + ioaddr = panelp->iobase + (i * 4); + outb(SCCR, (ioaddr + XP_ADDR)); + outb(CR_RESETALL, (ioaddr + XP_DATA)); + outb(TSTR, (ioaddr + XP_ADDR)); + if (inb(ioaddr + XP_DATA) != 0) { + printk("STALLION: sc26198 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); + continue; + } + chipmask |= (0x1 << i); + outb(GCCR, (ioaddr + XP_ADDR)); + outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA)); + outb(WDTRCR, (ioaddr + XP_ADDR)); + outb(0xff, (ioaddr + XP_DATA)); } BRDDISABLE(panelp->brdnr); - return(0); + return(chipmask); } /*****************************************************************************/ /* - * Try to find and initialize an EasyIO board. + * Initialize hardware specific port registers. */ -static int stl_initeio(stlbrd_t *brdp) +static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp) { - stlpanel_t *panelp; - unsigned int status; - int rc; - #if DEBUG - printk("stl_initeio(brdp=%x)\n", (int) brdp); + printk("stl_sc26198portinit(brdp=%x,panelp=%x,portp=%x)\n", + (int) brdp, (int) panelp, (int) portp); #endif - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 2; - brdp->clk = EIO_CLK; + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) + return; - status = inb(brdp->iostatus); - switch (status & EIO_IDBITMASK) { - case EIO_8PORTM: - brdp->clk = EIO_CLK8M; - /* fall thru */ - case EIO_8PORTRS: - case EIO_8PORTDI: - brdp->nrports = 8; + portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4); + portp->uartaddr = (portp->portnr & 0x07) << 4; + portp->pagenr = panelp->pagenr; + portp->hwid = 0x1; + + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS); + BRDDISABLE(portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Set up the sc26198 registers for a port based on the termios port + * settings. + */ + +static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp) +{ + stlbrd_t *brdp; + unsigned long flags; + unsigned int baudrate; + unsigned char mr0, mr1, mr2, clk; + unsigned char imron, imroff, iopr, ipr; + + mr0 = 0; + mr1 = 0; + mr2 = 0; + clk = 0; + iopr = 0; + imron = 0; + imroff = 0; + + brdp = stl_brds[portp->brdnr]; + if (brdp == (stlbrd_t *) NULL) + return; + +/* + * Set up the RX char ignore mask with those RX error types we + * can ignore. + */ + portp->rxignoremsk = 0; + if (tiosp->c_iflag & IGNPAR) + portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | + SR_RXOVERRUN); + if (tiosp->c_iflag & IGNBRK) + portp->rxignoremsk |= SR_RXBREAK; + + portp->rxmarkmsk = SR_RXOVERRUN; + if (tiosp->c_iflag & (INPCK | PARMRK)) + portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING); + if (tiosp->c_iflag & BRKINT) + portp->rxmarkmsk |= SR_RXBREAK; + +/* + * Go through the char size, parity and stop bits and set all the + * option register appropriately. + */ + switch (tiosp->c_cflag & CSIZE) { + case CS5: + mr1 |= MR1_CS5; break; - case EIO_4PORTRS: - brdp->nrports = 4; + case CS6: + mr1 |= MR1_CS6; + break; + case CS7: + mr1 |= MR1_CS7; break; default: - return(-ENODEV); + mr1 |= MR1_CS8; + break; + } + + if (tiosp->c_cflag & CSTOPB) + mr2 |= MR2_STOP2; + else + mr2 |= MR2_STOP1; + + if (tiosp->c_cflag & PARENB) { + if (tiosp->c_cflag & PARODD) + mr1 |= (MR1_PARENB | MR1_PARODD); + else + mr1 |= (MR1_PARENB | MR1_PAREVEN); + } else { + mr1 |= MR1_PARNONE; } - request_region(brdp->ioaddr1, 8, "serial(EIO)"); + mr1 |= MR1_ERRBLOCK; + +/* + * Set the RX FIFO threshold at 8 chars. This gives a bit of breathing + * space for hardware flow control and the like. This should be set to + * VMIN. + */ + mr2 |= MR2_RXFIFOHALF; /* - * Check that the supplied IRQ is good and then use it to setup the - * programmable interrupt bits on EIO board. Also set the edge/level - * triggered interrupt bit. + * Calculate the baud rate timers. For now we will just assume that + * the input and output baud are the same. The sc26198 has a fixed + * baud rate table, so only discrete baud rates possible. */ - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); - return(-EINVAL); + baudrate = tiosp->c_cflag & CBAUD; + if (baudrate & CBAUDEX) { + baudrate &= ~CBAUDEX; + if ((baudrate < 1) || (baudrate > 5)) + tiosp->c_cflag &= ~CBAUDEX; + else + baudrate += 15; + } + baudrate = stl_baudrates[baudrate]; + if ((tiosp->c_cflag & CBAUD) == B38400) { + if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baudrate = 57600; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baudrate = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + baudrate = (portp->baud_base / portp->custom_divisor); } - outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl); + if (baudrate > STL_SC26198MAXBAUD) + baudrate = STL_SC26198MAXBAUD; - panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); - if (panelp == (stlpanel_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t)); - return(-ENOMEM); + if (baudrate > 0) { + for (clk = 0; (clk < SC26198_NRBAUDS); clk++) { + if (baudrate <= sc26198_baudtable[clk]) + break; + } } - memset(panelp, 0, sizeof(stlpanel_t)); - panelp->magic = STL_PANELMAGIC; - panelp->brdnr = brdp->brdnr; - panelp->panelnr = 0; - panelp->nrports = brdp->nrports; - panelp->iobase = brdp->ioaddr1; - panelp->hwid = status; - brdp->panels[0] = panelp; - brdp->nrpanels = 1; - brdp->state |= BRD_FOUND; - brdp->hwid = status; - rc = stl_mapirq(brdp->irq); - return(rc); +/* + * Check what form of modem signaling is required and set it up. + */ + if (tiosp->c_cflag & CLOCAL) { + portp->flags &= ~ASYNC_CHECK_CD; + } else { + iopr |= IOPR_DCDCOS; + imron |= IR_IOPORT; + portp->flags |= ASYNC_CHECK_CD; + } + +/* + * Setup sc26198 enhanced modes if we can. In particular we want to + * handle as much of the flow control as possible automatically. As + * well as saving a few CPU cycles it will also greatly improve flow + * control reliability. + */ + if (tiosp->c_iflag & IXON) { + mr0 |= MR0_SWFTX | MR0_SWFT; + imron |= IR_XONXOFF; + } else { + imroff |= IR_XONXOFF; + } + if (tiosp->c_iflag & IXOFF) + mr0 |= MR0_SWFRX; + + if (tiosp->c_cflag & CRTSCTS) { + mr2 |= MR2_AUTOCTS; + mr1 |= MR1_AUTORTS; + } + +/* + * All sc26198 register values calculated so go through and set + * them all up. + */ + +#if DEBUG + printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); + printk(" mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk); + printk(" iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff); + printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); +#endif + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IMR, 0); + stl_sc26198updatereg(portp, MR0, mr0); + stl_sc26198updatereg(portp, MR1, mr1); + stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK); + stl_sc26198updatereg(portp, MR2, mr2); + stl_sc26198updatereg(portp, IOPIOR, + ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr)); + + if (baudrate > 0) { + stl_sc26198setreg(portp, TXCSR, clk); + stl_sc26198setreg(portp, RXCSR, clk); + } + + stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]); + stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]); + + ipr = stl_sc26198getreg(portp, IPR); + if (ipr & IPR_DCD) + portp->sigs &= ~TIOCM_CD; + else + portp->sigs |= TIOCM_CD; + + portp->imr = (portp->imr & ~imroff) | imron; + stl_sc26198setreg(portp, IMR, portp->imr); + BRDDISABLE(portp->brdnr); + restore_flags(flags); } /*****************************************************************************/ /* - * Try to find an ECH board and initialize it. This code is capable of - * dealing with all types of ECH board. + * Set the state of the DTR and RTS signals. */ -static int stl_initech(stlbrd_t *brdp) +static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts) { - stlpanel_t *panelp; - unsigned int status, nxtid; - int panelnr, ioaddr, i; + unsigned char iopioron, iopioroff; + unsigned long flags; #if DEBUG - printk("stl_initech(brdp=%x)\n", (int) brdp); + printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); #endif - status = 0; + iopioron = 0; + iopioroff = 0; + if (dtr == 0) + iopioroff |= IPR_DTR; + else if (dtr > 0) + iopioron |= IPR_DTR; + if (rts == 0) + iopioroff |= IPR_RTS; + else if (rts > 0) + iopioron |= IPR_RTS; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IOPIOR, + ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron)); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ /* - * Set up the initial board register contents for boards. This varies a - * bit between the different board types. So we need to handle each - * separately. Also do a check that the supplied IRQ is good. + * Return the state of the signals. */ - if (brdp->brdtype == BRD_ECH) { - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 1; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) - return(-ENODEV); - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); - return(-EINVAL); - } - status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); - status |= (stl_vecmap[brdp->irq] << 1); - outb((status | ECH_BRDRESET), brdp->ioaddr1); - brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); - outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - outb(status, brdp->ioaddr1); +static int stl_sc26198getsignals(stlport_t *portp) +{ + unsigned char ipr; + unsigned long flags; + int sigs; - request_region(brdp->ioaddr1, 2, "serial(EC8/32)"); - request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)"); - } else if (brdp->brdtype == BRD_ECHMC) { - brdp->ioctrl = brdp->ioaddr1 + 0x20; - brdp->iostatus = brdp->ioctrl; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) - return(-ENODEV); +#if DEBUG + printk("stl_sc26198getsignals(portp=%x)\n", (int) portp); +#endif - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); - return(-EINVAL); - } - outb(ECHMC_BRDRESET, brdp->ioctrl); - outb(ECHMC_INTENABLE, brdp->ioctrl); + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + ipr = stl_sc26198getreg(portp, IPR); + BRDDISABLE(portp->brdnr); + restore_flags(flags); - request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)"); - } else if (brdp->brdtype == BRD_ECHPCI) { - brdp->ioctrl = brdp->ioaddr1 + 2; - request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)"); - request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)"); - } + sigs = 0; + sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD; + sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS; + sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR; + sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS; + sigs |= TIOCM_DSR; + return(sigs); +} - brdp->clk = ECH_CLK; - brdp->hwid = status; +/*****************************************************************************/ /* - * Scan through the secondary io address space looking for panels. - * As we find'em allocate and initialize panel structures for each. + * Enable/Disable the Transmitter and/or Receiver. */ - ioaddr = brdp->ioaddr2; - panelnr = 0; - nxtid = 0; - for (i = 0; (i < STL_MAXPANELS); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb(nxtid, brdp->ioctrl); - ioaddr = brdp->ioaddr2; - } - status = inb(ioaddr + ECH_PNLSTATUS); - if ((status & ECH_PNLIDMASK) != nxtid) - break; - panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); - if (panelp == (stlpanel_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t)); - break; - } - memset(panelp, 0, sizeof(stlpanel_t)); - panelp->magic = STL_PANELMAGIC; - panelp->brdnr = brdp->brdnr; - panelp->panelnr = panelnr; - panelp->iobase = ioaddr; - panelp->pagenr = nxtid; - panelp->hwid = status; - if (status & ECH_PNL16PORT) { - if ((brdp->nrports + 16) > 32) - break; - panelp->nrports = 16; - panelp->ackmask = 0x80; - brdp->nrports += 16; - ioaddr += (EREG_BANKSIZE * 2); - nxtid += 2; - } else { - panelp->nrports = 8; - panelp->ackmask = 0xc0; - brdp->nrports += 8; - ioaddr += EREG_BANKSIZE; - nxtid++; - } - brdp->panels[panelnr++] = panelp; - brdp->nrpanels++; - if (ioaddr >= (brdp->ioaddr2 + 0x20)) - break; - } +static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx) +{ + unsigned char ccr; + unsigned long flags; - if (brdp->brdtype == BRD_ECH) - outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); +#if DEBUG + printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); +#endif - brdp->state |= BRD_FOUND; - i = stl_mapirq(brdp->irq); - return(i); + ccr = portp->crenable; + if (tx == 0) + ccr &= ~CR_TXENABLE; + else if (tx > 0) + ccr |= CR_TXENABLE; + if (rx == 0) + ccr &= ~CR_RXENABLE; + else if (rx > 0) + ccr |= CR_RXENABLE; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, ccr); + BRDDISABLE(portp->brdnr); + portp->crenable = ccr; + restore_flags(flags); } /*****************************************************************************/ /* - * Initialize and configure the specified board. - * Scan through all the boards in the configuration and see what we - * can find. Handle EIO and the ECH boards a little differently here - * since the initial search and setup is too different. + * Start/stop the Transmitter and/or Receiver. */ -static int stl_brdinit(stlbrd_t *brdp) +static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx) { - int i; + unsigned char imr; + unsigned long flags; #if DEBUG - printk("stl_brdinit(brdp=%x)\n", (int) brdp); + printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif - switch (brdp->brdtype) { - case BRD_EASYIO: - stl_initeio(brdp); - break; - case BRD_ECH: - case BRD_ECHMC: - case BRD_ECHPCI: - stl_initech(brdp); - break; - default: - printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); - return(ENODEV); - } - - stl_brds[brdp->brdnr] = brdp; - if ((brdp->state & BRD_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq); - return(ENODEV); - } - - for (i = 0; (i < STL_MAXPANELS); i++) - if (brdp->panels[i] != (stlpanel_t *) NULL) - stl_initports(brdp, brdp->panels[i]); + imr = portp->imr; + if (tx == 0) + imr &= ~IR_TXRDY; + else if (tx == 1) + imr |= IR_TXRDY; + if (rx == 0) + imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG); + else if (rx > 0) + imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG; - printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports); - return(0); + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IMR, imr); + BRDDISABLE(portp->brdnr); + portp->imr = imr; + if (tx > 0) + set_bit(ASYI_TXBUSY, &portp->istate); + restore_flags(flags); } /*****************************************************************************/ /* - * Find any ECH-PCI boards that might be installed. Initialize each - * one as it is found. + * Disable all interrupts from this port. */ -#ifdef CONFIG_PCI - -static int stl_findpcibrds() +static void stl_sc26198disableintrs(stlport_t *portp) { - stlbrd_t *brdp; - unsigned char busnr, devnr, irq; - unsigned short class; - unsigned int ioaddr; - int i, rc; + unsigned long flags; #if DEBUG - printk("stl_findpcibrds()\n"); + printk("stl_sc26198disableintrs(portp=%x)\n", (int) portp); #endif - if (pcibios_present()) { - for (i = 0; (i < STL_MAXBRDS); i++) { - if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr)) - break; - -/* - * Found a device on the PCI bus that has our vendor and - * device ID. Need to check now that it is really us. - */ - if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) { - printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc); - continue; - } - if (class == PCI_CLASS_STORAGE_IDE) - continue; - - if (stl_nrbrds >= STL_MAXBRDS) { - printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS); - break; - } + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + portp->imr = 0; + stl_sc26198setreg(portp, IMR, 0); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} -/* - * We have a Stallion board. Allocate a board structure - * and initialize it. Read its IO and IRQ resources - * from conf space. - */ - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); - return(-ENOMEM); - } - memset(brdp, 0, sizeof(stlbrd_t)); - brdp->magic = STL_BOARDMAGIC; - brdp->brdnr = stl_nrbrds++; - brdp->brdtype = BRD_ECHPCI; +/*****************************************************************************/ - if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); - continue; - } - brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK); +static void stl_sc26198sendbreak(stlport_t *portp, long len) +{ + unsigned long flags; - if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); - continue; - } - brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK); #if DEBUG - printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1); + printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, + (int) len); #endif - if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); - continue; - } - brdp->irq = irq; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (len / (1000 / HZ)); - stl_brdinit(brdp); - } - } + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); + BRDDISABLE(portp->brdnr); + portp->stats.txbreaks++; - return(0); -} + schedule(); -#endif + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} /*****************************************************************************/ /* - * Scan through all the boards in the configuration and see what we - * can find. Handle EIO and the ECH boards a little differently here - * since the initial search and setup is too different. + * Take flow control actions... */ -static int stl_initbrds() +static void stl_sc26198flowctrl(stlport_t *portp, int state) { - stlbrd_t *brdp; - stlconf_t *confp; - int i; + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; #if DEBUG - printk("stl_initbrds()\n"); + printk("stl_sc26198flowctrl(portp=%x,state=%x)\n", (int) portp, state); #endif - if (stl_nrbrds > STL_MAXBRDS) { - printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS); - stl_nrbrds = STL_MAXBRDS; - } + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + if (state) { + if (tty->termios->c_iflag & IXOFF) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); + mr0 |= MR0_SWFRX; + portp->stats.rxxon++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } /* - * Firstly scan the list of static boards configured. Allocate - * resources and initialize the boards as found. + * Question: should we return RTS to what it was before? It may + * have been set by an ioctl... Suppose not, since if you have + * hardware flow control set then it is pretty silly to go and + * set the RTS line by hand. */ - for (i = 0; (i < stl_nrbrds); i++) { - confp = &stl_brdconf[i]; - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); - return(-ENOMEM); + if (tty->termios->c_cflag & CRTSCTS) { + stl_sc26198setreg(portp, MR1, + (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS)); + stl_sc26198setreg(portp, IOPIOR, + (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS)); + portp->stats.rxrtson++; + } + } else { + if (tty->termios->c_iflag & IXOFF) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); + mr0 &= ~MR0_SWFRX; + portp->stats.rxxoff++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } + if (tty->termios->c_cflag & CRTSCTS) { + stl_sc26198setreg(portp, MR1, + (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS)); + stl_sc26198setreg(portp, IOPIOR, + (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS)); + portp->stats.rxrtsoff++; } - memset(brdp, 0, sizeof(stlbrd_t)); - - brdp->magic = STL_BOARDMAGIC; - brdp->brdnr = i; - brdp->brdtype = confp->brdtype; - brdp->ioaddr1 = confp->ioaddr1; - brdp->ioaddr2 = confp->ioaddr2; - brdp->irq = confp->irq; - brdp->irqtype = confp->irqtype; - stl_brdinit(brdp); } -#ifdef CONFIG_PCI -/* - * If the PCI BIOS support is compiled in then let's go looking for - * ECH-PCI boards. - */ - stl_findpcibrds(); -#endif - - return(0); + BRDDISABLE(portp->brdnr); + restore_flags(flags); } /*****************************************************************************/ /* - * Return the board stats structure to user app. + * Send a flow control character. */ -static int stl_getbrdstats(combrd_t *bp) +static void stl_sc26198sendflow(stlport_t *portp, int state) { - stlbrd_t *brdp; - stlpanel_t *panelp; - int i; + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; - memcpy_fromfs(&stl_brdstats, bp, sizeof(combrd_t)); - if (stl_brdstats.brd >= STL_MAXBRDS) - return(-ENODEV); - brdp = stl_brds[stl_brdstats.brd]; - if (brdp == (stlbrd_t *) NULL) - return(-ENODEV); +#if DEBUG + printk("stl_sc26198sendflow(portp=%x,state=%x)\n", (int) portp, state); +#endif - memset(&stl_brdstats, 0, sizeof(combrd_t)); - stl_brdstats.brd = brdp->brdnr; - stl_brdstats.type = brdp->brdtype; - stl_brdstats.hwid = brdp->hwid; - stl_brdstats.state = brdp->state; - stl_brdstats.ioaddr = brdp->ioaddr1; - stl_brdstats.ioaddr2 = brdp->ioaddr2; - stl_brdstats.irq = brdp->irq; - stl_brdstats.nrpanels = brdp->nrpanels; - stl_brdstats.nrports = brdp->nrports; - for (i = 0; (i < brdp->nrpanels); i++) { - panelp = brdp->panels[i]; - stl_brdstats.panels[i].panel = i; - stl_brdstats.panels[i].hwid = panelp->hwid; - stl_brdstats.panels[i].nrports = panelp->nrports; - } + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; - memcpy_tofs(bp, &stl_brdstats, sizeof(combrd_t)); - return(0); + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + if (state) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); + mr0 |= MR0_SWFRX; + portp->stats.rxxon++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } else { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); + mr0 &= ~MR0_SWFRX; + portp->stats.rxxoff++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } + BRDDISABLE(portp->brdnr); + restore_flags(flags); } /*****************************************************************************/ -/* - * Resolve the referenced port number into a port struct pointer. - */ - -static stlport_t *stl_getport(int brdnr, int panelnr, int portnr) +static void stl_sc26198flush(stlport_t *portp) { - stlbrd_t *brdp; - stlpanel_t *panelp; + unsigned long flags; - if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) - return((stlport_t *) NULL); - brdp = stl_brds[brdnr]; - if (brdp == (stlbrd_t *) NULL) - return((stlport_t *) NULL); - if ((panelnr < 0) || (panelnr >= brdp->nrpanels)) - return((stlport_t *) NULL); - panelp = brdp->panels[panelnr]; - if (panelp == (stlpanel_t *) NULL) - return((stlport_t *) NULL); - if ((portnr < 0) || (portnr >= panelp->nrports)) - return((stlport_t *) NULL); - return(panelp->ports[portnr]); +#if DEBUG + printk("stl_sc26198flush(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, CR_TXRESET); + stl_sc26198setreg(portp, SCCR, portp->crenable); + BRDDISABLE(portp->brdnr); + portp->tx.tail = portp->tx.head; + restore_flags(flags); } /*****************************************************************************/ /* - * Return the port stats structure to user app. A NULL port struct - * pointer passed in means that we need to find out from the app - * what port to get stats for (used through board control device). + * Return the current state of data flow on this port. This is only + * really interresting when determining if data has fully completed + * transmission or not... The sc26198 interrupt scheme cannot + * determine when all data has actually drained, so we need to + * check the port statusy register to be sure. */ -static int stl_getportstats(stlport_t *portp, comstats_t *cp) +static int stl_sc26198datastate(stlport_t *portp) { - unsigned char *head, *tail; unsigned long flags; + unsigned char sr; - if (portp == (stlport_t *) NULL) { - memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - } - - portp->stats.state = portp->istate; - portp->stats.flags = portp->flags; - portp->stats.hwid = portp->hwid; +#if DEBUG + printk("stl_sc26198datastate(portp=%x)\n", (int) portp); +#endif - portp->stats.ttystate = 0; - portp->stats.cflags = 0; - portp->stats.iflags = 0; - portp->stats.oflags = 0; - portp->stats.lflags = 0; - portp->stats.rxbuffered = 0; + if (portp == (stlport_t *) NULL) + return(0); + if (test_bit(ASYI_TXBUSY, &portp->istate)) + return(1); save_flags(flags); cli(); - if (portp->tty != (struct tty_struct *) NULL) { - if (portp->tty->driver_data == portp) { - portp->stats.ttystate = portp->tty->flags; - portp->stats.rxbuffered = portp->tty->flip.count; - if (portp->tty->termios != (struct termios *) NULL) { - portp->stats.cflags = portp->tty->termios->c_cflag; - portp->stats.iflags = portp->tty->termios->c_iflag; - portp->stats.oflags = portp->tty->termios->c_oflag; - portp->stats.lflags = portp->tty->termios->c_lflag; - } - } - } + BRDENABLE(portp->brdnr, portp->pagenr); + sr = stl_sc26198getreg(portp, SR); + BRDDISABLE(portp->brdnr); restore_flags(flags); - head = portp->tx.head; - tail = portp->tx.tail; - portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head))); + return((sr & SR_TXEMPTY) ? 0 : 1); +} - portp->stats.signals = (unsigned long) stl_getsignals(portp); +/*****************************************************************************/ - memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); - return(0); +/* + * Delay for a small amount of time, to give the sc26198 a chance + * to process a command... + */ + +static void stl_sc26198wait(stlport_t *portp) +{ + int i; + +#if DEBUG + printk("stl_sc26198wait(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return; + + for (i = 0; (i < 20); i++) + stl_sc26198getglobreg(portp, TSTR); } /*****************************************************************************/ /* - * Clear the port stats structure. We also return it zeroed out... + * If we are TX flow controlled and in IXANY mode then we may + * need to unflow control here. We gotta do this because of the + * automatic flow control modes of the sc26198. */ -static int stl_clrportstats(stlport_t *portp, comstats_t *cp) +static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty) { - if (portp == (stlport_t *) NULL) { - memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - } - - memset(&portp->stats, 0, sizeof(comstats_t)); - portp->stats.brd = portp->brdnr; - portp->stats.panel = portp->panelnr; - portp->stats.port = portp->portnr; - memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); - return(0); + unsigned char mr0; + + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_HOSTXON); + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + clear_bit(ASYI_TXFLOWED, &portp->istate); } /*****************************************************************************/ /* - * Return the entire driver ports structure to a user app. + * Interrupt service routine for sc26198 panels. */ -static int stl_getportstruct(unsigned long arg) +static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase) { stlport_t *portp; + unsigned int iack; - memcpy_fromfs(&stl_dummyport, (void *) arg, sizeof(stlport_t)); - portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr, - stl_dummyport.portnr); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - memcpy_tofs((void *) arg, portp, sizeof(stlport_t)); - return(0); +/* + * Work around bug in sc26198 chip... Cannot have A6 address + * line of UART high, else iack will be returned as 0. + */ + outb(0, (iobase + 1)); + + iack = inb(iobase + XP_IACK); + portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)]; + + if (iack & IVR_RXDATA) + stl_sc26198rxisr(portp, iack); + else if (iack & IVR_TXDATA) + stl_sc26198txisr(portp); + else + stl_sc26198otherisr(portp, iack); } /*****************************************************************************/ /* - * Return the entire driver board structure to a user app. + * Transmit interrupt handler. This has gotta be fast! Handling TX + * chars is pretty simple, stuff as many as possible from the TX buffer + * into the sc26198 FIFO. + * In practice it is possible that interrupts are enabled but that the + * port has been hung up. Need to handle not having any TX buffer here, + * this is done by using the side effect that head and tail will also + * be NULL if the buffer has been freed. */ -static int stl_getbrdstruct(unsigned long arg) +static void stl_sc26198txisr(stlport_t *portp) { - stlbrd_t *brdp; + unsigned int ioaddr; + unsigned char mr0; + int len, stlen; + char *head, *tail; - memcpy_fromfs(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t)); - if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS)) - return(-ENODEV); - brdp = stl_brds[stl_dummybrd.brdnr]; - if (brdp == (stlbrd_t *) NULL) - return(-ENODEV); - memcpy_tofs((void *) arg, brdp, sizeof(stlbrd_t)); - return(0); +#if DEBUG + printk("stl_sc26198txisr(portp=%x)\n", (int) portp); +#endif + + ioaddr = portp->ioaddr; + head = portp->tx.head; + tail = portp->tx.tail; + len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + set_bit(ASYI_TXLOW, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + } + + if (len == 0) { + outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR)); + mr0 = inb(ioaddr + XP_DATA); + if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) { + portp->imr &= ~IR_TXRDY; + outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR)); + outb(portp->imr, (ioaddr + XP_DATA)); + clear_bit(ASYI_TXBUSY, &portp->istate); + } else { + mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY); + outb(mr0, (ioaddr + XP_DATA)); + } + } else { + len = MIN(len, SC26198_TXFIFOSIZE); + portp->stats.txtotal += len; + stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); + outb(GTXFIFO, (ioaddr + XP_ADDR)); + outsb((ioaddr + XP_DATA), tail, stlen); + len -= stlen; + tail += stlen; + if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) + tail = portp->tx.buf; + if (len > 0) { + outsb((ioaddr + XP_DATA), tail, len); + tail += len; + } + portp->tx.tail = tail; + } } /*****************************************************************************/ /* - * The "staliomem" device is also required to do some special operations - * on the board and/or ports. In this driver it is mostly used for stats - * collection. + * Receive character interrupt handler. Determine if we have good chars + * or bad chars and then process appropriately. Good chars are easy + * just shove the lot into the RX buffer and set all status byte to 0. + * If a bad RX char then process as required. This routine needs to be + * fast! In practice it is possible that we get an interrupt on a port + * that is closed. This can happen on hangups - since they completely + * shutdown a port not in user context. Need to handle this case. */ -static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) +static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack) { - int brdnr, rc; + struct tty_struct *tty; + unsigned int len, buflen, ioaddr; #if DEBUG - printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg); + printk("stl_sc26198rxisr(portp=%x,iack=%x)\n", (int) portp, iack); #endif - brdnr = MINOR(ip->i_rdev); - if (brdnr >= STL_MAXBRDS) - return(-ENODEV); - rc = 0; - - switch (cmd) { - case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg); - break; - case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg); - break; - case COM_GETBRDSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0) - rc = stl_getbrdstats((combrd_t *) arg); - break; - case COM_READPORT: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0) - rc = stl_getportstruct(arg); - break; - case COM_READBOARD: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0) - rc = stl_getbrdstruct(arg); - break; - default: - rc = -ENOIOCTLCMD; - break; + tty = portp->tty; + ioaddr = portp->ioaddr; + outb(GIBCR, (ioaddr + XP_ADDR)); + len = inb(ioaddr + XP_DATA) + 1; + + if ((iack & IVR_TYPEMASK) == IVR_RXDATA) { + if ((tty == (struct tty_struct *) NULL) || + (tty->flip.char_buf_ptr == (char *) NULL) || + ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + outb(GRXFIFO, (ioaddr + XP_ADDR)); + insb((ioaddr + XP_DATA), &stl_unwanted[0], len); + portp->stats.rxlost += len; + portp->stats.rxtotal += len; + } else { + len = MIN(len, buflen); + if (len > 0) { + outb(GRXFIFO, (ioaddr + XP_ADDR)); + insb((ioaddr + XP_DATA), tty->flip.char_buf_ptr, len); + memset(tty->flip.flag_buf_ptr, 0, len); + tty->flip.flag_buf_ptr += len; + tty->flip.char_buf_ptr += len; + tty->flip.count += len; + tty_schedule_flip(tty); + portp->stats.rxtotal += len; + } + } + } else { + stl_sc26198rxbadchars(portp); } - return(rc); +/* + * If we are TX flow controlled and in IXANY mode then we may need + * to unflow control here. We gotta do this because of the automatic + * flow control modes of the sc26198. + */ + if (test_bit(ASYI_TXFLOWED, &portp->istate)) { + if ((tty != (struct tty_struct *) NULL) && + (tty->termios != (struct termios *) NULL) && + (tty->termios->c_iflag & IXANY)) { + stl_sc26198txunflow(portp, tty); + } + } } /*****************************************************************************/ -int stl_init(void) +/* + * Process an RX bad character. + */ + +static void inline stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch) { - printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion); + struct tty_struct *tty; + unsigned int ioaddr; - stl_initbrds(); + tty = portp->tty; + ioaddr = portp->ioaddr; + + if (status & SR_RXPARITY) + portp->stats.rxparity++; + if (status & SR_RXFRAMING) + portp->stats.rxframing++; + if (status & SR_RXOVERRUN) + portp->stats.rxoverrun++; + if (status & SR_RXBREAK) + portp->stats.rxbreaks++; + + if ((tty != (struct tty_struct *) NULL) && + ((portp->rxignoremsk & status) == 0)) { + if (portp->rxmarkmsk & status) { + if (status & SR_RXBREAK) { + status = TTY_BREAK; + if (portp->flags & ASYNC_SAK) { + do_SAK(tty); + BRDENABLE(portp->brdnr, portp->pagenr); + } + } else if (status & SR_RXPARITY) { + status = TTY_PARITY; + } else if (status & SR_RXFRAMING) { + status = TTY_FRAME; + } else if(status & SR_RXOVERRUN) { + status = TTY_OVERRUN; + } else { + status = 0; + } + } else { + status = 0; + } + + if (tty->flip.char_buf_ptr != (char *) NULL) { + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.flag_buf_ptr++ = status; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + } + tty_schedule_flip(tty); + } + + if (status == 0) + portp->stats.rxtotal++; + } +} + +/*****************************************************************************/ /* - * Allocate a temporary write buffer. + * Process all characters in the RX FIFO of the UART. Check all char + * status bytes as well, and process as required. We need to check + * all bytes in the FIFO, in case some more enter the FIFO while we + * are here. To get the exact character error type we need to switch + * into CHAR error mode (that is why we need to make sure we empty + * the FIFO). */ - stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE); - if (stl_tmpwritebuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE); + +static void stl_sc26198rxbadchars(stlport_t *portp) +{ + unsigned char status, mr1; + char ch; /* - * Set up a character driver for per board stuff. This is mainly used - * to do stats ioctls on the ports. + * To get the precise error type for each character we must switch + * back into CHAR error mode. */ - if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) - printk("STALLION: failed to register serial board device\n"); + mr1 = stl_sc26198getreg(portp, MR1); + stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK)); + + while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) { + stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR); + ch = stl_sc26198getreg(portp, RXFIFO); + stl_sc26198rxbadch(portp, status, ch); + } /* - * Set up the tty driver structure and register us as a driver. - * Also setup the callout tty device. + * To get correct interrupt class we must switch back into BLOCK + * error mode. */ - memset(&stl_serial, 0, sizeof(struct tty_driver)); - stl_serial.magic = TTY_DRIVER_MAGIC; - stl_serial.name = stl_serialname; - stl_serial.major = STL_SERIALMAJOR; - stl_serial.minor_start = 0; - stl_serial.num = STL_MAXBRDS * STL_MAXPORTS; - stl_serial.type = TTY_DRIVER_TYPE_SERIAL; - stl_serial.subtype = STL_DRVTYPSERIAL; - stl_serial.init_termios = stl_deftermios; - stl_serial.flags = TTY_DRIVER_REAL_RAW; - stl_serial.refcount = &stl_refcount; - stl_serial.table = stl_ttys; - stl_serial.termios = stl_termios; - stl_serial.termios_locked = stl_termioslocked; - - stl_serial.open = stl_open; - stl_serial.close = stl_close; - stl_serial.write = stl_write; - stl_serial.put_char = stl_putchar; - stl_serial.flush_chars = stl_flushchars; - stl_serial.write_room = stl_writeroom; - stl_serial.chars_in_buffer = stl_charsinbuffer; - stl_serial.ioctl = stl_ioctl; - stl_serial.set_termios = stl_settermios; - stl_serial.throttle = stl_throttle; - stl_serial.unthrottle = stl_unthrottle; - stl_serial.stop = stl_stop; - stl_serial.start = stl_start; - stl_serial.hangup = stl_hangup; - stl_serial.flush_buffer = stl_flushbuffer; + stl_sc26198setreg(portp, MR1, mr1); +} - stl_callout = stl_serial; - stl_callout.name = stl_calloutname; - stl_callout.major = STL_CALLOUTMAJOR; - stl_callout.subtype = STL_DRVTYPCALLOUT; +/*****************************************************************************/ - if (tty_register_driver(&stl_serial)) - printk("STALLION: failed to register serial driver\n"); - if (tty_register_driver(&stl_callout)) - printk("STALLION: failed to register callout driver\n"); +/* + * Other interrupt handler. This includes modem signals, flow + * control actions, etc. Most stuff is left to off-level interrupt + * processing time. + */ - return(0); +static void stl_sc26198otherisr(stlport_t *portp, unsigned int iack) +{ + unsigned char cir, ipr, xisr; + +#if DEBUG + printk("stl_sc26198otherisr(portp=%x,iack=%x)\n", (int) portp, iack); +#endif + + cir = stl_sc26198getglobreg(portp, CIR); + + switch (cir & CIR_SUBTYPEMASK) { + case CIR_SUBCOS: + ipr = stl_sc26198getreg(portp, IPR); + if (ipr & IPR_DCDCHANGE) { + set_bit(ASYI_DCDCHANGE, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + portp->stats.modem++; + } + break; + case CIR_SUBXONXOFF: + xisr = stl_sc26198getreg(portp, XISR); + if (xisr & XISR_RXXONGOT) { + set_bit(ASYI_TXFLOWED, &portp->istate); + portp->stats.txxoff++; + } + if (xisr & XISR_RXXOFFGOT) { + clear_bit(ASYI_TXFLOWED, &portp->istate); + portp->stats.txxon++; + } + break; + case CIR_SUBBREAK: + stl_sc26198setreg(portp, SCCR, CR_BREAKRESET); + stl_sc26198rxbadchars(portp); + break; + default: + break; + } } /*****************************************************************************/ diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 6e26244d64ee..56d527a2516f 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -294,7 +294,10 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err) struct ext2_super_block * es; if (!dir || !(inode = get_empty_inode ())) + { + *err=-ENOMEM; return NULL; + } sb = dir->i_sb; inode->i_sb = sb; inode->i_flags = sb->s_flags; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d7e2fc621c8b..902d011dfa9d 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -597,7 +597,7 @@ int isofs_bmap(struct inode * inode,int block) * If we are beyond the end of this file, don't give out any * blocks. */ - if( b_off > inode->i_size ) + if( b_off >= inode->i_size ) { off_t max_legal_read_offset; diff --git a/include/linux/cd1400.h b/include/linux/cd1400.h index b6e5b667bdc7..d07d1e61a6fe 100644 --- a/include/linux/cd1400.h +++ b/include/linux/cd1400.h @@ -3,6 +3,7 @@ /* * cd1400.h -- cd1400 UART hardware info. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This program is free software; you can redistribute it and/or modify diff --git a/include/linux/cdk.h b/include/linux/cdk.h index a3d0167912f8..2180e433023c 100644 --- a/include/linux/cdk.h +++ b/include/linux/cdk.h @@ -3,6 +3,7 @@ /* * cdk.h -- CDK interface definitions. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This program is free software; you can redistribute it and/or modify @@ -303,6 +304,10 @@ typedef struct asyport { #define P_DTRFOLLOW 0x20 #define P_FAKEDCD 0x40 +#define P_RXIMIN 0x10000 +#define P_RXITIME 0x20000 +#define P_RXTHOLD 0x40000 + /* * Define a structure to communicate serial port signal and data state * information. diff --git a/include/linux/comstats.h b/include/linux/comstats.h index 8f7591f387b5..066888599ae1 100644 --- a/include/linux/comstats.h +++ b/include/linux/comstats.h @@ -3,6 +3,7 @@ /* * comstats.h -- Serial Port Stats. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This program is free software; you can redistribute it and/or modify diff --git a/include/linux/istallion.h b/include/linux/istallion.h index 1cf916ad47a4..269ef88ba166 100644 --- a/include/linux/istallion.h +++ b/include/linux/istallion.h @@ -3,6 +3,7 @@ /* * istallion.h -- stallion intelligent multiport serial driver. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This program is free software; you can redistribute it and/or modify @@ -101,6 +102,7 @@ typedef struct stlibrd { int nrports; int nrdevs; unsigned int iobase; + int iosize; unsigned long memaddr; void *membase; int memsize; diff --git a/include/linux/sc26198.h b/include/linux/sc26198.h new file mode 100644 index 000000000000..38685e0770bc --- /dev/null +++ b/include/linux/sc26198.h @@ -0,0 +1,533 @@ +/*****************************************************************************/ + +/* + * sc26198.h -- SC26198 UART hardware info. + * + * Copyright (C) 1995-1998 Stallion Technologies (support@stallion.oz.au). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*****************************************************************************/ +#ifndef _SC26198_H +#define _SC26198_H +/*****************************************************************************/ + +/* + * Define the number of async ports per sc26198 uart device. + */ +#define SC26198_PORTS 8 + +/* + * Baud rate timing clocks. All derived from a master 14.7456 MHz clock. + */ +#define SC26198_MASTERCLOCK 14745600L +#define SC26198_DCLK (SC26198_MASTERCLOCK) +#define SC26198_CCLK (SC26198_MASTERCLOCK / 2) +#define SC26198_BCLK (SC26198_MASTERCLOCK / 4) + +/* + * Define internal FIFO sizes for the 26198 ports. + */ +#define SC26198_TXFIFOSIZE 16 +#define SC26198_RXFIFOSIZE 16 + +/*****************************************************************************/ + +/* + * Global register definitions. These registers are global to each 26198 + * device, not specific ports on it. + */ +#define TSTR 0x0d +#define GCCR 0x0f +#define ICR 0x1b +#define WDTRCR 0x1d +#define IVR 0x1f +#define BRGTRUA 0x84 +#define GPOSR 0x87 +#define GPOC 0x8b +#define UCIR 0x8c +#define CIR 0x8c +#define BRGTRUB 0x8d +#define GRXFIFO 0x8e +#define GTXFIFO 0x8e +#define GCCR2 0x8f +#define BRGTRLA 0x94 +#define GPOR 0x97 +#define GPOD 0x9b +#define BRGTCR 0x9c +#define GICR 0x9c +#define BRGTRLB 0x9d +#define GIBCR 0x9d +#define GITR 0x9f + +/* + * Per port channel registers. These are the register offsets within + * the port address space, so need to have the port address (0 to 7) + * inserted in bit positions 4:6. + */ +#define MR0 0x00 +#define MR1 0x01 +#define IOPCR 0x02 +#define BCRBRK 0x03 +#define BCRCOS 0x04 +#define BCRX 0x06 +#define BCRA 0x07 +#define XONCR 0x08 +#define XOFFCR 0x09 +#define ARCR 0x0a +#define RXCSR 0x0c +#define TXCSR 0x0e +#define MR2 0x80 +#define SR 0x81 +#define SCCR 0x81 +#define ISR 0x82 +#define IMR 0x82 +#define TXFIFO 0x83 +#define RXFIFO 0x83 +#define IPR 0x84 +#define IOPIOR 0x85 +#define XISR 0x86 + +/* + * For any given port calculate the address to use to access a specified + * register. This is only used for unusual access, mostly this is done + * through the assembler access routines. + */ +#define SC26198_PORTREG(port,reg) ((((port) & 0x07) << 4) | (reg)) + +/*****************************************************************************/ + +/* + * Global configuration control register bit definitions. + */ +#define GCCR_NOACK 0x00 +#define GCCR_IVRACK 0x02 +#define GCCR_IVRCHANACK 0x04 +#define GCCR_IVRTYPCHANACK 0x06 +#define GCCR_ASYNCCYCLE 0x00 +#define GCCR_SYNCCYCLE 0x40 + +/*****************************************************************************/ + +/* + * Mode register 0 bit definitions. + */ +#define MR0_ADDRNONE 0x00 +#define MR0_AUTOWAKE 0x01 +#define MR0_AUTODOZE 0x02 +#define MR0_AUTOWAKEDOZE 0x03 +#define MR0_SWFNONE 0x00 +#define MR0_SWFTX 0x04 +#define MR0_SWFRX 0x08 +#define MR0_SWFRXTX 0x0c +#define MR0_TXMASK 0x30 +#define MR0_TXEMPTY 0x00 +#define MR0_TXHIGH 0x10 +#define MR0_TXHALF 0x20 +#define MR0_TXRDY 0x00 +#define MR0_ADDRNT 0x00 +#define MR0_ADDRT 0x40 +#define MR0_SWFNT 0x00 +#define MR0_SWFT 0x80 + +/* + * Mode register 1 bit definitions. + */ +#define MR1_CS5 0x00 +#define MR1_CS6 0x01 +#define MR1_CS7 0x02 +#define MR1_CS8 0x03 +#define MR1_PAREVEN 0x00 +#define MR1_PARODD 0x04 +#define MR1_PARENB 0x00 +#define MR1_PARFORCE 0x08 +#define MR1_PARNONE 0x10 +#define MR1_PARSPECIAL 0x18 +#define MR1_ERRCHAR 0x00 +#define MR1_ERRBLOCK 0x20 +#define MR1_ISRUNMASKED 0x00 +#define MR1_ISRMASKED 0x40 +#define MR1_AUTORTS 0x80 + +/* + * Mode register 2 bit definitions. + */ +#define MR2_STOP1 0x00 +#define MR2_STOP15 0x01 +#define MR2_STOP2 0x02 +#define MR2_STOP916 0x03 +#define MR2_RXFIFORDY 0x00 +#define MR2_RXFIFOHALF 0x04 +#define MR2_RXFIFOHIGH 0x08 +#define MR2_RXFIFOFULL 0x0c +#define MR2_AUTOCTS 0x10 +#define MR2_TXRTS 0x20 +#define MR2_MODENORM 0x00 +#define MR2_MODEAUTOECHO 0x40 +#define MR2_MODELOOP 0x80 +#define MR2_MODEREMECHO 0xc0 + +/*****************************************************************************/ + +/* + * Baud Rate Generator (BRG) selector values. + */ +#define BRG_50 0x00 +#define BRG_75 0x01 +#define BRG_150 0x02 +#define BRG_200 0x03 +#define BRG_300 0x04 +#define BRG_450 0x05 +#define BRG_600 0x06 +#define BRG_900 0x07 +#define BRG_1200 0x08 +#define BRG_1800 0x09 +#define BRG_2400 0x0a +#define BRG_3600 0x0b +#define BRG_4800 0x0c +#define BRG_7200 0x0d +#define BRG_9600 0x0e +#define BRG_14400 0x0f +#define BRG_19200 0x10 +#define BRG_28200 0x11 +#define BRG_38400 0x12 +#define BRG_57600 0x13 +#define BRG_115200 0x14 +#define BRG_230400 0x15 +#define BRG_GIN0 0x16 +#define BRG_GIN1 0x17 +#define BRG_CT0 0x18 +#define BRG_CT1 0x19 +#define BRG_RX2TX316 0x1b +#define BRG_RX2TX31 0x1c + +#define SC26198_MAXBAUD 921600 + +/*****************************************************************************/ + +/* + * Command register command definitions. + */ +#define CR_NULL 0x04 +#define CR_ADDRNORMAL 0x0c +#define CR_RXRESET 0x14 +#define CR_TXRESET 0x1c +#define CR_CLEARRXERR 0x24 +#define CR_BREAKRESET 0x2c +#define CR_TXSTARTBREAK 0x34 +#define CR_TXSTOPBREAK 0x3c +#define CR_RTSON 0x44 +#define CR_RTSOFF 0x4c +#define CR_ADDRINIT 0x5c +#define CR_RXERRBLOCK 0x6c +#define CR_TXSENDXON 0x84 +#define CR_TXSENDXOFF 0x8c +#define CR_GANGXONSET 0x94 +#define CR_GANGXOFFSET 0x9c +#define CR_GANGXONINIT 0xa4 +#define CR_GANGXOFFINIT 0xac +#define CR_HOSTXON 0xb4 +#define CR_HOSTXOFF 0xbc +#define CR_CANCELXOFF 0xc4 +#define CR_ADDRRESET 0xdc +#define CR_RESETALLPORTS 0xf4 +#define CR_RESETALL 0xfc + +#define CR_RXENABLE 0x01 +#define CR_TXENABLE 0x02 + +/*****************************************************************************/ + +/* + * Channel status register. + */ +#define SR_RXRDY 0x01 +#define SR_RXFULL 0x02 +#define SR_TXRDY 0x04 +#define SR_TXEMPTY 0x08 +#define SR_RXOVERRUN 0x10 +#define SR_RXPARITY 0x20 +#define SR_RXFRAMING 0x40 +#define SR_RXBREAK 0x80 + +#define SR_RXERRS (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN) + +/*****************************************************************************/ + +/* + * Interrupt status register and interrupt mask register bit definitions. + */ +#define IR_TXRDY 0x01 +#define IR_RXRDY 0x02 +#define IR_RXBREAK 0x04 +#define IR_XONXOFF 0x10 +#define IR_ADDRRECOG 0x20 +#define IR_RXWATCHDOG 0x40 +#define IR_IOPORT 0x80 + +/*****************************************************************************/ + +/* + * Interrupt vector register field definitions. + */ +#define IVR_CHANMASK 0x07 +#define IVR_TYPEMASK 0x18 +#define IVR_CONSTMASK 0xc0 + +#define IVR_RXDATA 0x10 +#define IVR_RXBADDATA 0x18 +#define IVR_TXDATA 0x08 +#define IVR_OTHER 0x00 + +/*****************************************************************************/ + +/* + * BRG timer control register bit definitions. + */ +#define BRGCTCR_DISABCLK0 0x00 +#define BRGCTCR_ENABCLK0 0x08 +#define BRGCTCR_DISABCLK1 0x00 +#define BRGCTCR_ENABCLK1 0x80 + +#define BRGCTCR_0SCLK16 0x00 +#define BRGCTCR_0SCLK32 0x01 +#define BRGCTCR_0SCLK64 0x02 +#define BRGCTCR_0SCLK128 0x03 +#define BRGCTCR_0X1 0x04 +#define BRGCTCR_0X12 0x05 +#define BRGCTCR_0IO1A 0x06 +#define BRGCTCR_0GIN0 0x07 + +#define BRGCTCR_1SCLK16 0x00 +#define BRGCTCR_1SCLK32 0x10 +#define BRGCTCR_1SCLK64 0x20 +#define BRGCTCR_1SCLK128 0x30 +#define BRGCTCR_1X1 0x40 +#define BRGCTCR_1X12 0x50 +#define BRGCTCR_1IO1B 0x60 +#define BRGCTCR_1GIN1 0x70 + +/*****************************************************************************/ + +/* + * Watch dog timer enable register. + */ +#define WDTRCR_ENABALL 0xff + +/*****************************************************************************/ + +/* + * XON/XOFF interrupt status register. + */ +#define XISR_TXCHARMASK 0x03 +#define XISR_TXCHARNORMAL 0x00 +#define XISR_TXWAIT 0x01 +#define XISR_TXXOFFPEND 0x02 +#define XISR_TXXONPEND 0x03 + +#define XISR_TXFLOWMASK 0x0c +#define XISR_TXNORMAL 0x00 +#define XISR_TXSTOPPEND 0x04 +#define XISR_TXSTARTED 0x08 +#define XISR_TXSTOPPED 0x0c + +#define XISR_RXFLOWMASK 0x30 +#define XISR_RXFLOWNONE 0x00 +#define XISR_RXXONSENT 0x10 +#define XISR_RXXOFFSENT 0x20 + +#define XISR_RXXONGOT 0x40 +#define XISR_RXXOFFGOT 0x80 + +/*****************************************************************************/ + +/* + * Current interrupt register. + */ +#define CIR_TYPEMASK 0xc0 +#define CIR_TYPEOTHER 0x00 +#define CIR_TYPETX 0x40 +#define CIR_TYPERXGOOD 0x80 +#define CIR_TYPERXBAD 0xc0 + +#define CIR_RXDATA 0x80 +#define CIR_RXBADDATA 0x40 +#define CIR_TXDATA 0x40 + +#define CIR_CHANMASK 0x07 +#define CIR_CNTMASK 0x38 + +#define CIR_SUBTYPEMASK 0x38 +#define CIR_SUBNONE 0x00 +#define CIR_SUBCOS 0x08 +#define CIR_SUBADDR 0x10 +#define CIR_SUBXONXOFF 0x18 +#define CIR_SUBBREAK 0x28 + +/*****************************************************************************/ + +/* + * Global interrupting channel register. + */ +#define GICR_CHANMASK 0x07 + +/*****************************************************************************/ + +/* + * Global interrupting byte count register. + */ +#define GICR_COUNTMASK 0x0f + +/*****************************************************************************/ + +/* + * Global interrupting type register. + */ +#define GITR_RXMASK 0xc0 +#define GITR_RXNONE 0x00 +#define GITR_RXBADDATA 0x80 +#define GITR_RXGOODDATA 0xc0 +#define GITR_TXDATA 0x20 + +#define GITR_SUBTYPEMASK 0x07 +#define GITR_SUBNONE 0x00 +#define GITR_SUBCOS 0x01 +#define GITR_SUBADDR 0x02 +#define GITR_SUBXONXOFF 0x03 +#define GITR_SUBBREAK 0x05 + +/*****************************************************************************/ + +/* + * Input port change register. + */ +#define IPR_CTS 0x01 +#define IPR_DTR 0x02 +#define IPR_RTS 0x04 +#define IPR_DCD 0x08 +#define IPR_CTSCHANGE 0x10 +#define IPR_DTRCHANGE 0x20 +#define IPR_RTSCHANGE 0x40 +#define IPR_DCDCHANGE 0x80 + +#define IPR_CHANGEMASK 0xf0 + +/*****************************************************************************/ + +/* + * IO port interrupt and output register. + */ +#define IOPR_CTS 0x01 +#define IOPR_DTR 0x02 +#define IOPR_RTS 0x04 +#define IOPR_DCD 0x08 +#define IOPR_CTSCOS 0x10 +#define IOPR_DTRCOS 0x20 +#define IOPR_RTSCOS 0x40 +#define IOPR_DCDCOS 0x80 + +/*****************************************************************************/ + +/* + * IO port configuration register. + */ +#define IOPCR_SETCTS 0x00 +#define IOPCR_SETDTR 0x04 +#define IOPCR_SETRTS 0x10 +#define IOPCR_SETDCD 0x00 + +#define IOPCR_SETSIGS (IOPCR_SETRTS | IOPCR_SETRTS | IOPCR_SETDTR | IOPCR_SETDCD) + +/*****************************************************************************/ + +/* + * General purpose output select register. + */ +#define GPORS_TXC1XA 0x08 +#define GPORS_TXC16XA 0x09 +#define GPORS_RXC16XA 0x0a +#define GPORS_TXC16XB 0x0b +#define GPORS_GPOR3 0x0c +#define GPORS_GPOR2 0x0d +#define GPORS_GPOR1 0x0e +#define GPORS_GPOR0 0x0f + +/*****************************************************************************/ + +/* + * General purpose output register. + */ +#define GPOR_0 0x01 +#define GPOR_1 0x02 +#define GPOR_2 0x04 +#define GPOR_3 0x08 + +/*****************************************************************************/ + +/* + * General purpose output clock register. + */ +#define GPORC_0NONE 0x00 +#define GPORC_0GIN0 0x01 +#define GPORC_0GIN1 0x02 +#define GPORC_0IO3A 0x02 + +#define GPORC_1NONE 0x00 +#define GPORC_1GIN0 0x04 +#define GPORC_1GIN1 0x08 +#define GPORC_1IO3C 0x0c + +#define GPORC_2NONE 0x00 +#define GPORC_2GIN0 0x10 +#define GPORC_2GIN1 0x20 +#define GPORC_2IO3E 0x20 + +#define GPORC_3NONE 0x00 +#define GPORC_3GIN0 0x40 +#define GPORC_3GIN1 0x80 +#define GPORC_3IO3G 0xc0 + +/*****************************************************************************/ + +/* + * General purpose output data register. + */ +#define GPOD_0MASK 0x03 +#define GPOD_0SET1 0x00 +#define GPOD_0SET0 0x01 +#define GPOD_0SETR0 0x02 +#define GPOD_0SETIO3B 0x03 + +#define GPOD_1MASK 0x0c +#define GPOD_1SET1 0x00 +#define GPOD_1SET0 0x04 +#define GPOD_1SETR0 0x08 +#define GPOD_1SETIO3D 0x0c + +#define GPOD_2MASK 0x30 +#define GPOD_2SET1 0x00 +#define GPOD_2SET0 0x10 +#define GPOD_2SETR0 0x20 +#define GPOD_2SETIO3F 0x30 + +#define GPOD_3MASK 0xc0 +#define GPOD_3SET1 0x00 +#define GPOD_3SET0 0x40 +#define GPOD_3SETR0 0x80 +#define GPOD_3SETIO3H 0xc0 + +/*****************************************************************************/ +#endif diff --git a/include/linux/stallion.h b/include/linux/stallion.h index b1aa738bcd90..35274488dadf 100644 --- a/include/linux/stallion.h +++ b/include/linux/stallion.h @@ -3,6 +3,7 @@ /* * stallion.h -- stallion multiport serial driver. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This program is free software; you can redistribute it and/or modify @@ -30,6 +31,7 @@ */ #define STL_MAXBRDS 4 #define STL_MAXPANELS 4 +#define STL_MAXBANKS 8 #define STL_PORTSPERPANEL 16 #define STL_MAXPORTS 64 #define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS) @@ -65,7 +67,7 @@ typedef struct { * is associated with, this makes it (fairly) easy to get back to the * board/panel info for a port. */ -typedef struct { +typedef struct stlport { unsigned long magic; int portnr; int panelnr; @@ -87,8 +89,11 @@ typedef struct { unsigned int sigs; unsigned int rxignoremsk; unsigned int rxmarkmsk; + unsigned int imr; + unsigned int crenable; unsigned long clk; unsigned long hwid; + void *uartp; struct tty_struct *tty; struct wait_queue *open_wait; struct wait_queue *close_wait; @@ -99,34 +104,43 @@ typedef struct { stlrq_t tx; } stlport_t; -typedef struct { +typedef struct stlpanel { unsigned long magic; int panelnr; int brdnr; int pagenr; int nrports; int iobase; + void *uartp; + void (*isr)(struct stlpanel *panelp, unsigned int iobase); unsigned int hwid; unsigned int ackmask; stlport_t *ports[STL_PORTSPERPANEL]; } stlpanel_t; -typedef struct { +typedef struct stlbrd { unsigned long magic; int brdnr; int brdtype; int state; int nrpanels; int nrports; + int nrbnks; int irq; int irqtype; + void (*isr)(struct stlbrd *brdp); unsigned int ioaddr1; unsigned int ioaddr2; + unsigned int iosize1; + unsigned int iosize2; unsigned int iostatus; unsigned int ioctrl; unsigned int ioctrlval; unsigned int hwid; unsigned long clk; + unsigned int bnkpageaddr[STL_MAXBANKS]; + unsigned int bnkstataddr[STL_MAXBANKS]; + stlpanel_t *bnk2panel[STL_MAXBANKS]; stlpanel_t *panels[STL_MAXPANELS]; } stlbrd_t; diff --git a/include/net/route.h b/include/net/route.h index dc067900a4ee..7bf32d0aed6f 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -131,12 +131,10 @@ extern __inline__ unsigned ip_rt_hash_code(__u32 addr) extern __inline__ void ip_rt_put(struct rtable * rt) #ifndef MODULE { - if (rt) - atomic_dec(&rt->rt_refcnt); - - /* If this rtable entry is not in the cache, we'd better free it once the - * refcnt goes to zero, because nobody else will... */ - if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) ) + /* If this rtable entry is not in the cache, we'd better free + * it once the refcnt goes to zero, because nobody else will. + */ + if (rt&&atomic_dec_and_test(&rt->rt_refcnt)&&(rt->rt_flags&RTF_NOTCACHED)) rt_free(rt); } #else diff --git a/init/main.c b/init/main.c index c1c25ff857e1..4bf6f9fd9958 100644 --- a/init/main.c +++ b/init/main.c @@ -610,6 +610,7 @@ static void parse_root_dev(char * line) const int num; } devices[] = { { "nfs", 0x00ff }, + { "loop", 0x0700 }, { "hda", 0x0300 }, { "hdb", 0x0340 }, { "hdc", 0x1600 }, diff --git a/mm/vmscan.c b/mm/vmscan.c index 5818cc5fa5f1..dd6e6a966e7f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -265,14 +265,15 @@ static int swap_out_process(struct task_struct * p, int dma, int wait, int can_d * Go through process' page directory. */ address = p->swap_address; - p->swap_address = 0; /* * Find the proper vm-area */ vma = find_vma(p->mm, address); - if (!vma) + if (!vma) { + p->swap_address = 0; return 0; + } if (address < vma->vm_start) address = vma->vm_start; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index cc01dc7802f1..34b6ad86a518 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -925,6 +925,15 @@ void rt_free(struct rtable * rt) static __inline__ void rt_kick_free_queue(void) { struct rtable *rt, **rtp; +#if RT_CACHE_DEBUG >= 2 + static int in = 0; + + if(in) { + printk("Attempted multiple entry: rt_kick_free_queue\n"); + return; + } + in++; +#endif ip_rt_bh_mask &= ~RT_BH_FREE; @@ -952,6 +961,9 @@ static __inline__ void rt_kick_free_queue(void) } rtp = &rt->rt_next; } +#if RT_CACHE_DEBUG >= 2 + in--; +#endif } void ip_rt_run_bh() @@ -974,8 +986,11 @@ void ip_rt_run_bh() ip_rt_fast_unlock(); } - if (ip_rt_bh_mask & RT_BH_FREE) + if (ip_rt_bh_mask & RT_BH_FREE) { + ip_rt_fast_lock(); rt_kick_free_queue(); + ip_rt_fast_unlock(); + } } restore_flags(flags); } @@ -1528,12 +1543,10 @@ struct rtable * ip_rt_slow_route (__u32 daddr, int local, struct device *dev) void ip_rt_put(struct rtable * rt) { - if (rt) - atomic_dec(&rt->rt_refcnt); - - /* If this rtable entry is not in the cache, we'd better free it once the - * refcnt goes to zero, because nobody else will... */ - if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) ) + /* If this rtable entry is not in the cache, we'd better free + * it once the refcnt goes to zero, because nobody else will. + */ + if (rt&&atomic_dec_and_test(&rt->rt_refcnt)&&(rt->rt_flags&RTF_NOTCACHED)) rt_free(rt); }