From: Linus Torvalds Date: Fri, 23 Nov 2007 20:13:13 +0000 (-0500) Subject: Import 2.1.37pre5 X-Git-Tag: 2.1.37pre5 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=379e85a6f8fd31c688f00639969b5cbb8c32113e;p=history.git Import 2.1.37pre5 --- diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 7473fcf702c2..c76c86e7dfbf 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -2072,6 +2072,20 @@ CONFIG_SCSI_NCR53C406A and read Documentation/modules.txt. The module will be called NCR53c406.o. +Tekram DC390W/U/F (T) SCSI support +CONFIG_SCSI_DC390W + This driver supports the Tekram DC390W/U/F (T) PCI SCSI host adapters with + the NCR/Symbios 53c825/875 chips. If you have a DC390 (T) adaptor with the + Am53C974A chip use the DC390(T) driver. + +Tekram DC390(T) (AMD PCscsi) SCSI support +CONFIG_SCSI_DC390T + This driver supports the Tekram DC390(T) PCI SCSI Hostadapter with + the Am53C974A chip, and perhaps other cards using the same chip. + + This driver does _not_ support the DC390W/U/F adaptor with the + NCR/Symbios chips. + AM53/79C974 PCI SCSI support CONFIG_SCSI_AM53C974 This is support for the AM53/79C974 SCSI host adapters. Please read diff --git a/MAINTAINERS b/MAINTAINERS index be976ea8a2c4..61fde9d11e58 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -336,12 +336,6 @@ L: linux-kernel@vger.rutgers.edu W: http://www-plateau.cs.berkeley.edu/people/chaffee S: Maintained -DIGIBOARD DRIVER: -P: Christoph Lameter -M: clameter@fuller.edu -L: digiboard@list.fuller.edu -S: Maintained - DIGI INTL. EPCA DRIVER: P: Daniel Taylor M: support@dgii.com diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index a6ee5a17c1ed..e3e9d8f09e46 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -42,6 +42,13 @@ CONFIG_SYSVIPC=y CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_EM86=y +# CONFIG_PNP_PARPORT is not set + +# +# Plug and Play support +# +# CONFIG_PNP is not set # # Floppy, IDE, and other block devices @@ -72,9 +79,10 @@ CONFIG_BLK_DEV_RAM=y # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set CONFIG_INET=y -# CONFIG_IP_FORWARD is not set # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set # # (it is safe to leave these untouched) @@ -133,7 +141,6 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set -# CONFIG_SCSI_PPA is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_QLOGIC_FAS is not set CONFIG_SCSI_QLOGIC_ISP=y @@ -155,19 +162,22 @@ CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y +# CONFIG_PCNET32 is not set # CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set CONFIG_DE4X5=y # CONFIG_DEC_ELCP is not set # CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set -# CONFIG_PLIP is not set # CONFIG_PPP is not set # CONFIG_NET_RADIO is not set -# CONFIG_LAPBETHER is not set # CONFIG_SLIP is not set # CONFIG_TR is not set +# CONFIG_LAPBETHER is not set +# CONFIG_X25_ASY is not set # # ISDN subsystem @@ -192,12 +202,16 @@ CONFIG_MSDOS_FS=y CONFIG_PROC_FS=y CONFIG_NFS_FS=y # CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y # CONFIG_SMB_FS is not set CONFIG_ISO9660_FS=y # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set # @@ -206,12 +220,8 @@ CONFIG_ISO9660_FS=y CONFIG_VT=y CONFIG_VT_CONSOLE=y CONFIG_SERIAL=y -# CONFIG_DIGI is not set -# CONFIG_CYCLADES is not set -# CONFIG_STALDRV is not set -# CONFIG_RISCOM8 is not set -# CONFIG_ESPSERIAL is not set -# CONFIG_PRINTER is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set CONFIG_MOUSE=y # CONFIG_ATIXL_BUSMOUSE is not set # CONFIG_BUSMOUSE is not set @@ -224,7 +234,6 @@ CONFIG_PSMOUSE=y # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set -CONFIG_RTC_ARC=y # # Sound diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S index a9e132666ec6..f7621362487e 100644 --- a/arch/alpha/kernel/head.S +++ b/arch/alpha/kernel/head.S @@ -64,22 +64,6 @@ rdusp: ret ($26) .end rdusp - .align 3 - .globl tbi - .ent tbi -tbi: - call_pal PAL_tbi - ret ($26) - .end tbi - - .align 3 - .globl imb - .ent imb -imb: - call_pal PAL_imb - ret ($26) - .end imb - .align 3 .globl rdmces .ent rdmces diff --git a/arch/i386/Makefile b/arch/i386/Makefile index ca3b12590019..7e02bc36f706 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -85,7 +85,7 @@ bzdisk: vmlinux @$(MAKEBOOT) BOOTIMAGE=bzImage zdisk install: vmlinux - @$(MAKEBOOT) install + @$(MAKEBOOT) BOOTIMAGE=zImage install archclean: @$(MAKEBOOT) clean diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index e4e08c82f110..8f23505656a0 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -653,6 +653,8 @@ extern int cpu_idle(void * unused); __initfunc(int start_secondary(void *unused)) { smp_callin(); + while (!smp_commenced) + barrier(); cpu_idle(NULL); } diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 7d99d9400f88..fbe1696d263c 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -240,8 +240,24 @@ out: unlock_kernel(); } +void enable_NMI(void) +{ + unsigned char reason; + unsigned long i; + + reason = inb(0x61); + printk("NMI reason = %02x\n", reason); + reason |= 8; + outb(reason, 0x61); + i = 400000000; + while (--i) ; + reason &= ~8; + outb(reason, 0x61); +} + asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { + show_registers(regs); #ifdef CONFIG_SMP_NMI_INVAL smp_flush_tlb_rcv(); #else @@ -251,6 +267,7 @@ asmlinkage void do_nmi(struct pt_regs * regs, long error_code) printk("power saving mode enabled.\n"); #endif #endif + enable_NMI(); } asmlinkage void do_debug(struct pt_regs * regs, long error_code) diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index f07cade7f04a..401dbc6574dd 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -5285,11 +5285,11 @@ static struct file_operations sbpcd_fops = * bytes above). * */ -__initfunc( #if (SBPCD_ISSUE-1) -static +__initfunc(static void sbpcd_setup(const char *s, int *p)) +#else +__initfunc(void sbpcd_setup(const char *s, int *p)) #endif -void sbpcd_setup(const char *s, int *p)) { setup_done++; msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s); diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 0a1825ee8355..3e0dc6b8763e 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,5 +1,7 @@ +#define BLOCKMOVE static char rcsid[] = -"$Revision: 1.36.3.9 $$Date: 1996/10/07 19:47:13 $"; +"$Revision: 1.36.4.27 $$Date: 1997/03/26 10:30:00 $"; + /* * linux/drivers/char/cyclades.c * @@ -22,11 +24,128 @@ static char rcsid[] = * This module exports the following rs232 io functions: * int cy_init(void); * int cy_open(struct tty_struct *tty, struct file *filp); + * and the following functions for modularization. + * int init_module(void); + * void cleanup_module(void); * * $Log: cyclades.c,v $ - * Revision 1.36.3.9 1996/10/07 19:47:13 bentson - * add MOD_DEC_USE_COUNT in one return from cy_close (as - * noted by Jon Lewis ) + * Revision 1.36.4.27 1997/03/26 10:30:00 daniel + * Changed for suport linux versions 2.1.X. + * Backward compatible with linux versions 2.0.X. + * Corrected illegal use of filler field in + * CH_CTRL struct. + * Deleted some debug messages. + * + * Revision 1.36.4.26 1997/02/27 12:00:00 daniel + * Included check for NULL tty pointer in cyz_poll. + * + * Revision 1.36.4.25 1997/02/26 16:28:30 bentson + * Bill Foster at Blarg! Online services noticed that + * some of the switch elements of -Z modem control + * lacked a closing "break;" + * + * Revision 1.36.4.24 1997/02/24 11:00:00 daniel + * Changed low water threshold for buffer xmit_buf + * + * Revision 1.36.4.23 1996/12/02 21:50:16 bentson + * Marcio provided fix to modem status fetch for -Z + * + * Revision 1.36.4.22 1996/10/28 22:41:17 bentson + * improve mapping of -Z control page (thanks to Steve + * Price for help on this) + * + * Revision 1.36.4.21 1996/09/10 17:00:10 bentson + * shift from cpu-bound to memcopy in cyz_polling operation + * + * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson + * Added support to set and report higher speeds. + * + * Revision 1.36.4.19c 1996/08/09 10:00:00 Marcio Saito + * Some fixes in the HW flow control for the BETA release. + * Don't try to register the IRQ. + * + * Revision 1.36.4.19 1996/08/08 16:23:18 Bentson + * make sure "cyc" appears in all kernel messages; all soft interrupts + * handled by same routine; recognize out-of-band reception; comment + * out some diagnostic messages; leave RTS/CTS flow control to hardware; + * fix race condition in -Z buffer management; only -Y needs to explictly + * flush chars; tidy up some startup messages; + * + * Revision 1.36.4.18 1996/07/25 18:57:31 bentson + * shift MOD_INC_USE_COUNT location to match + * serial.c; purge some diagnostic messages; + * + * Revision 1.36.4.17 1996/07/25 18:01:08 bentson + * enable modem status messages and fetch & process them; note + * time of last activity type for each port; set_line_char now + * supports more than line 0 and treats 0 baud correctly; + * get_modem_info senses rs_status; + * + * Revision 1.36.4.16 1996/07/20 08:43:15 bentson + * barely works--now's time to turn on + * more features 'til it breaks + * + * Revision 1.36.4.15 1996/07/19 22:30:06 bentson + * check more -Z board status; shorten boot message + * + * Revision 1.36.4.14 1996/07/19 22:20:37 bentson + * fix reference to ch_ctrl in startup; verify return + * values from cyz_issue_cmd and cyz_update_channel; + * more stuff to get modem control correct; + * + * Revision 1.36.4.13 1996/07/11 19:53:33 bentson + * more -Z stuff folded in; re-order changes to put -Z stuff + * after -Y stuff (to make changes clearer) + * + * Revision 1.36.4.12 1996/07/11 15:40:55 bentson + * Add code to poll Cyclom-Z. Add code to get & set RS-232 control. + * Add code to send break. Clear firmware ID word at startup (so + * that other code won't talk to inactive board). + * + * Revision 1.36.4.11 1996/07/09 05:28:29 bentson + * add code for -Z in set_line_char + * + * Revision 1.36.4.10 1996/07/08 19:28:37 bentson + * fold more -Z stuff (or in some cases, error messages) + * into driver; add text to "don't know what to do" messages. + * + * Revision 1.36.4.9 1996/07/08 18:38:38 bentson + * moved compile-time flags near top of file; cosmetic changes + * to narrow text (to allow 2-up printing); changed many declarations + * to "static" to limit external symbols; shuffled code order to + * coalesce -Y and -Z specific code, also to put internal functions + * in order of tty_driver structure; added code to recognize -Z + * ports (and for moment, do nothing or report error); add cy_startup + * to parse boot command line for extra base addresses for ISA probes; + * + * Revision 1.36.4.8 1996/06/25 17:40:19 bentson + * reorder some code, fix types of some vars (int vs. long), + * add cy_setup to support user declared ISA addresses + * + * Revision 1.36.4.7 1996/06/21 23:06:18 bentson + * dump ioctl based firmware load (it's now a user level + * program); ensure uninitialzed ports cannot be used + * + * Revision 1.36.4.6 1996/06/20 23:17:19 bentson + * rename vars and restructure some code + * + * Revision 1.36.4.5 1996/06/14 15:09:44 bentson + * get right status back after boot load + * + * Revision 1.36.4.4 1996/06/13 19:51:44 bentson + * successfully loads firmware + * + * Revision 1.36.4.3 1996/06/13 06:08:33 bentson + * add more of the code for the boot/load ioctls + * + * Revision 1.36.4.2 1996/06/11 21:00:51 bentson + * start to add Z functionality--starting with ioctl + * for loading firmware + * + * Revision 1.36.4.1 1996/06/10 18:03:02 bentson + * added code to recognize Z/PCI card at initialization; report + * presence, but card is not initialized (because firmware needs + * to be loaded) * * Revision 1.36.3.8 1996/06/07 16:29:00 bentson * starting minor number at zero; added missing verify_area @@ -38,7 +157,7 @@ static char rcsid[] = * remove unused diagnostic statements; minor 0 is first; * * Revision 1.36.3.6 1996/03/13 13:21:17 marcio - * The kernel function ioremap (available only in later 1.3.xx kernels) + * The kernel function vremap (available only in later 1.3.xx kernels) * allows the access to memory addresses above the RAM. This revision * of the driver supports PCI boards below 1Mb (device id 0x100) and * above 1Mb (device id 0x101). @@ -261,6 +380,52 @@ static char rcsid[] = * */ +/* If you need to install more boards than NR_CARDS, change the constant + in the definition below. No other change is necessary to support up to + eight boards. Beyond that you'll have to extend cy_isa_addresses. */ + +#define NR_CARDS 4 + +/* + If the total number of ports is larger than NR_PORTS, change this + constant in the definition below. No other change is necessary to + support more boards/ports. */ + +#define NR_PORTS 64 + +#define SERIAL_PARANOIA_CHECK +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_DEBUG_OTHER +#undef SERIAL_DEBUG_IO +#undef SERIAL_DEBUG_COUNT +#undef SERIAL_DEBUG_DTR +#undef CYCLOM_16Y_HACK +#undef CYCLOM_ENABLE_MONITORING +#undef CY_PCI_DEBUG + + +#if 0 +#define PAUSE __asm__("nop"); +#else +#define PAUSE ; +#endif + +#define cy_min(a,b) (((a)<(b))?(a):(b)) + +#define CHARS_IN_BUF(buf_ctrl) \ + ((buf_ctrl->rx_put - \ + buf_ctrl->rx_get + \ + buf_ctrl->rx_bufsize) % \ + buf_ctrl->rx_bufsize) + +#define SPACE_IN_BUF(buf_ctrl) \ + ((buf_ctrl->tx_get - \ + buf_ctrl->tx_put + \ + buf_ctrl->tx_bufsize - 1) % \ + buf_ctrl->tx_bufsize) + + #include #include @@ -280,7 +445,7 @@ static char rcsid[] = #include #include -#include +#include #include #include @@ -290,24 +455,34 @@ static char rcsid[] = #include #include -#define small_delay(x) for(j=0;j +#if LINUX_VERSION_CODE >= 131328 -#define SERIAL_PARANOIA_CHECK -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_THROTTLE -#undef SERIAL_DEBUG_OTHER -#undef SERIAL_DEBUG_IO -#undef SERIAL_DEBUG_COUNT -#undef SERIAL_DEBUG_DTR -#undef CYCLOM_16Y_HACK -#undef CYCLOM_ENABLE_MONITORING +#include + +#define memcpy_fromfs copy_from_user +#define memcpy_tofs copy_to_user +#define put_fs_long put_user +#define vremap ioremap + +static unsigned long get_fs_long(unsigned long *addr) +{ + unsigned long result = 0; + int error = get_user (result, addr); + if (error) + printk ("cyclades: get_fs_long: error == %d\n", error); + return result; +} + +#endif #ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +#define IS_CYC_Z(card) ((card).num_chips == 1) -#define WAKEUP_CHARS 256 +#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) #define STD_COM_FLAGS (0) @@ -325,72 +500,39 @@ static int cy_wild_int_mask; static unsigned char *intr_base_addr; -/* This is the address lockup table. The driver will probe for Cyclom-Y/ISA - boards at all addresses in here. If you want the driver to probe addresses - in a different address, add it to this table. - If the driver is probing some other board and causing problems, remove the - address from this table. */ +/* This is the address lookup table. The driver will probe for + Cyclom-Y/ISA boards at all addresses in here. If you want the + driver to probe addresses at a different address, add it to + this table. If the driver is probing some other board and + causing problems, remove the offending address from this table. + The cy_setup function extracts additional addresses from the + boot options line. The form is "cyclades=address,address..." +*/ static unsigned char *cy_isa_addresses[] = { - (unsigned char *) 0xD0000, - (unsigned char *) 0xD2000, - (unsigned char *) 0xD4000, - (unsigned char *) 0xD6000, - (unsigned char *) 0xD8000, - (unsigned char *) 0xDA000, - (unsigned char *) 0xDC000, - (unsigned char *) 0xDE000, + (unsigned char *) 0xD0000, + (unsigned char *) 0xD2000, + (unsigned char *) 0xD4000, + (unsigned char *) 0xD6000, + (unsigned char *) 0xD8000, + (unsigned char *) 0xDA000, + (unsigned char *) 0xDC000, + (unsigned char *) 0xDE000, + 0,0,0,0,0,0,0,0 }; -#define NR_ISA_ADDRESSES (sizeof(cy_isa_addresses)/sizeof(unsigned char *)) +#define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*)) /* This is the per-card data structure containing address, irq, number of - channels, etc. This driver supports a maximum of NR_CARDS cards. If - you need to install more boards, change this constant in the definition - below. No other change is necessary to support more boards. */ - -#define NR_CARDS 4 - + channels, etc. This driver supports a maximum of NR_CARDS cards. +*/ static struct cyclades_card cy_card[NR_CARDS]; /* This is the per-channel data structure containing pointers, flags - and variables for the port. This driver supports a maximum of NR_PORTS. - If the total number of ports is larger than NR_PORTS, change this - constant in the definition below. No other change is necessary to - support more boards/ports. */ - -#define NR_PORTS 64 - + and variables for the port. This driver supports a maximum of NR_PORTS. +*/ static struct cyclades_port cy_port[NR_PORTS]; -/* The Cyclom-Ye has placed the sequential chips in non-sequential - * address order. This look-up table overcomes that problem. - */ -static int cy_chip_offset [] = - { 0x0000, - 0x0400, - 0x0800, - 0x0C00, - 0x0200, - 0x0600, - 0x0A00, - 0x0E00 - }; - -/* PCI related definitions */ - -static unsigned short cy_pci_nboard = 0; -static unsigned short cy_isa_nboard = 0; -static unsigned short cy_nboard = 0; -static unsigned short cy_pci_dev_id[] = { - PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ - PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ - 0 /* end of table */ - }; - -int cy_detect_isa(void); -int cy_detect_pci(void); - -static int cy_next_channel = 0; /* next minor available */ +static int cy_next_channel = 0; /* next minor available */ static int serial_refcount; @@ -401,17 +543,18 @@ static struct termios *serial_termios_locked[NR_PORTS]; /* This is the per-irq data structure, it maps an irq to the corresponding card */ -struct cyclades_card *IRQ_cards[16]; +static struct cyclades_card *IRQ_cards[16]; /* * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, + * lock it in case the memcpy_fromfs blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. + * memory if large numbers of serial ports are open. This buffer is + * allocated when the first cy_open occurs. */ static unsigned char *tmp_buf = 0; static struct semaphore tmp_buf_sem = MUTEX; @@ -420,83 +563,127 @@ static struct semaphore tmp_buf_sem = MUTEX; * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra * are accessed via settings in info->flags. - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - * HI VHI + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI */ static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, - 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, - 0}; + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, + 0}; static char baud_co[] = { /* 25 MHz clock option table */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static char baud_bpr[] = { /* 25 MHz baud rate period table */ - 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, - 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; static char baud_cor3[] = { /* receive threshold */ - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; +/* The Cyclom-Ye has placed the sequential chips in non-sequential + * address order. This look-up table overcomes that problem. + */ +static int cy_chip_offset [] = + { 0x0000, + 0x0400, + 0x0800, + 0x0C00, + 0x0200, + 0x0600, + 0x0A00, + 0x0E00 + }; + +/* PCI related definitions */ -static void shutdown(struct cyclades_port *); -static int startup (struct cyclades_port *); -static void cy_throttle(struct tty_struct *); -static void cy_unthrottle(struct tty_struct *); -static void config_setup(struct cyclades_port *); +static unsigned short cy_pci_nboard = 0; +static unsigned short cy_isa_nboard = 0; +static unsigned short cy_nboard = 0; +static unsigned short cy_pci_dev_id[] = { + PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Hi,/* PCI above 1Mb */ + 0 /* end of table */ + }; + + +static void cy_start(struct tty_struct *); +static void set_line_char(struct cyclades_port *); +static void cy_probe(int, void *, struct pt_regs *); +static void cyz_poll(unsigned long); #ifdef CYCLOM_SHOW_STATUS static void show_status(int); #endif +static int cyz_timeron = 0; +static struct timer_list +cyz_timerlist = { + NULL, NULL, 0, 0, cyz_poll +}; + + +/************************************************** +error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long)); +memcpy_tofs (to, from, count); +*************************************************************** +error = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long *)); +memcpy_fromfs(to, from, count); +**************************************************/ + + static inline int serial_paranoia_check(struct cyclades_port *info, - kdev_t device, const char *routine) + kdev_t device, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; + "cyc Warning: bad magic number for serial struct (%s) in %s\n"; static const char *badinfo = - "Warning: null cyclades_port for (%s) in %s\n"; + "cyc Warning: null cyclades_port for (%s) in %s\n"; static const char *badrange = - "Warning: cyclades_port out of range for (%s) in %s\n"; + "cyc Warning: cyclades_port out of range for (%s) in %s\n"; if (!info) { - printk(badinfo, kdevname(device), routine); - return 1; + printk(badinfo, kdevname(device), routine); + return 1; } if( (long)info < (long)(&cy_port[0]) || (long)(&cy_port[NR_PORTS]) < (long)info ){ - printk(badrange, kdevname(device), routine); - return 1; + printk(badrange, kdevname(device), routine); + return 1; } if (info->magic != CYCLADES_MAGIC) { - printk(badmagic, kdevname(device), routine); - return 1; + printk(badmagic, kdevname(device), routine); + return 1; } #endif - return 0; + return 0; } /* serial_paranoia_check */ + /* The following diagnostic routines allow the driver to spew information on the screen, even (especially!) during interrupts. */ -void +static void SP(char *data){ unsigned long flags; save_flags(flags); cli(); console_print(data); restore_flags(flags); -} -void +}/* SP */ + +static void CP(char data){ unsigned long flags; char scrn[2]; @@ -507,110 +694,14 @@ CP(char data){ restore_flags(flags); }/* CP */ -void CP1(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP1 */ -void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */ -void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */ -void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */ - -/* This routine waits up to 1000 micro-seconds for the previous - command to the Cirrus chip to complete and then issues the - new command. An error is returned if the previous command - didn't finish within the time limit. - */ -u_short -write_cy_cmd(u_char *base_addr, u_char cmd, int index) -{ - unsigned long flags; - volatile int i; - - save_flags(flags); cli(); - /* Check to see that the previous command has completed */ - for(i = 0 ; i < 100 ; i++){ - if (base_addr[CyCCR<driver_data; - unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_stop ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_stop")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<driver_data; - unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_start ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_start")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */ +static void CP16(int data) + { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */ +static void CP32(long data) + { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */ /* @@ -623,54 +714,301 @@ static inline void cy_sched_event(struct cyclades_port *info, int event) { info->event |= 1 << event; /* remember what kind of event and who */ - queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */ + queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */ mark_bh(CYCLADES_BH); /* then trigger event */ } /* cy_sched_event */ -static int probe_ready; - /* - * This interrupt routine is used - * while we are probing for submarines. + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using cy_sched_event(), and they get done here. + * + * This is done through one level of indirection--the task queue. + * When a hardware interrupt service routine wants service by the + * driver's bottom half, it enqueues the appropriate tq_struct (one + * per port) to the tq_cyclades work queue and sets a request flag + * via mark_bh for processing that queue. When the time is right, + * do_cyclades_bh is called (because of the mark_bh) and it requests + * that the work queue be processed. + * + * Although this may seem unwieldy, it gives the system a way to + * pass an argument (in this case the pointer to the cyclades_port + * structure) to the bottom half of the driver. Previous kernels + * had to poll every port to see if that port needed servicing. */ static void -cy_probe(int irq, void *dev_id, struct pt_regs *regs) +do_cyclades_bh(void) { - int save_xir, save_car; - int index = 0; /* probing interrupts is only for ISA */ + run_task_queue(&tq_cyclades); +} /* do_cyclades_bh */ - if (!probe_ready) { - *(intr_base_addr + (Cy_ClrIntr<tty; + if (!tty) return; + + if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); } +} /* do_softint */ - cy_irq_triggered = irq; - cy_triggered |= 1 << irq; - if(intr_base_addr[CySVRR<= jiffies) + ; + + cy_triggered = 0; /* Reset after letting things settle */ + + timeout = jiffies+10; + while (timeout >= jiffies) + ; + + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { + if ((cy_triggered & (1 << i)) && + (irq_lines & (1 << i))) { + wild_interrupts |= mask; + } + } + free_all_interrupts(irq_lines); + restore_flags(flags); + return wild_interrupts; +} /* check_wild_interrupts */ + +/* + * This routine is called by do_auto_irq(); it attempts to determine + * which interrupt a serial port is configured to use. It is not + * fool-proof, but it works a large part of the time. + */ +static int +get_auto_irq(unsigned char *address) +{ + unsigned long timeout; + unsigned char *base_addr; + int index; + + index = 0; /* IRQ probing is only for ISA */ + base_addr = address; + intr_base_addr = address; + + /* + * Enable interrupts and see who answers + */ + cy_irq_triggered = 0; + cli(); + base_addr[CyCAR<= jiffies) { + if (cy_irq_triggered) + break; + } + probe_ready = 0; + return(cy_irq_triggered); +} /* get_auto_irq */ + +/* + * Calls get_auto_irq() multiple times, to make sure we don't get + * faked out by random interrupts + */ +static int +do_auto_irq(unsigned char *address) +{ + int irq_lines = 0; + int irq_try_1 = 0, irq_try_2 = 0; + int retries; + unsigned long flags; + + /* Turn on interrupts (they may be off) */ + save_flags(flags); sti(); + + probe_ready = 0; + + cy_wild_int_mask = check_wild_interrupts(); + + irq_lines = grab_all_interrupts(cy_wild_int_mask); + + for (retries = 0; retries < 5; retries++) { + if (!irq_try_1) + irq_try_1 = get_auto_irq(address); + if (!irq_try_2) + irq_try_2 = get_auto_irq(address); + if (irq_try_1 && irq_try_2) { + if (irq_try_1 == irq_try_2) + break; + irq_try_1 = irq_try_2 = 0; + } + } + restore_flags(flags); + free_all_interrupts(irq_lines); + return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; +} /* do_auto_irq */ + + +/* + * This interrupt routine is used + * while we are probing for submarines. + */ +static void +cy_probe(int irq, void *dev_id, struct pt_regs *regs) +{ + int save_xir, save_car; + int index = 0; /* probing interrupts is only for ISA */ + + if (!probe_ready) { + *(intr_base_addr + (Cy_ClrIntr<num_chips ; chip ++) { - base_addr = (unsigned char *) - (cinfo->base_addr + (cy_chip_offset[chip]<base_addr + (cy_chip_offset[chip]<first_line; @@ -746,82 +1084,84 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) continue; } if (tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - if (data & info->read_status_mask){ - if(data & CyBREAK){ - *tty->flip.flag_buf_ptr++ = - TTY_BREAK; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flags & ASYNC_SAK){ - do_SAK(tty); - } - }else if(data & CyFRAME){ - *tty->flip.flag_buf_ptr++ = - TTY_FRAME; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = - TTY_PARITY; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = - TTY_OVERRUN; - *tty->flip.char_buf_ptr++ = 0; - /* If the flip buffer itself is - overflowing, we still loose - the next incoming character. - */ - if(tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = - TTY_NORMAL; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } - }else{ - *tty->flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } + tty->flip.count++; + if (data & info->read_status_mask){ + if(data & CyBREAK){ + *tty->flip.flag_buf_ptr++ = + TTY_BREAK; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<flags & ASYNC_SAK){ + do_SAK(tty); + } + }else if(data & CyFRAME){ + *tty->flip.flag_buf_ptr++ = + TTY_FRAME; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<flip.flag_buf_ptr++ = + TTY_PARITY; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<flip.flag_buf_ptr++ = + TTY_OVERRUN; + *tty->flip.char_buf_ptr++ = 0; + /* If the flip buffer itself is + overflowing, we still loose + the next incoming character. + */ + if(tty->flip.count + < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = + TTY_NORMAL; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } }else{ - /* there was a software buffer overrun - and nothing could be done about it!!! */ + /* there was a software buffer + overrun and nothing could be + done about it!!! */ } } else { /* normal character reception */ - /* load # characters available from the chip */ + /* load # chars available from the chip */ char_count = base_addr[CyRDCR<mon.int_count; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; + ++info->mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; #endif while(char_count--){ - if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; } - tty->flip.count++; + tty->flip.count++; data = base_addr[CyRDSR<flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = data; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; #ifdef CYCLOM_16Y_HACK - udelay(10L); + udelay(10L); #endif } } - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ base_addr[CyRIR<first_line; save_car = base_addr[CyCAR<xmit_fifo_size; @@ -863,64 +1204,65 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) info->x_char = 0; } - if (info->x_break){ - /* The Cirrus chip requires the "Embedded Transmit - Commands" of start break, delay, and end break - sequences to be sent. The duration of the - break is given in TICs, which runs at HZ - (typically 100) and the PPR runs at 200 Hz, - so the delay is duration * 200/HZ, and thus a - break can run from 1/100 sec to about 5/4 sec. - */ - base_addr[CyTDR<x_break*200/HZ; - base_addr[CyTDR<x_break = 0; - } + if (info->x_break){ + /* The Cirrus chip requires the "Embedded + Transmit Commands" of start break, delay, + and end break sequences to be sent. The + duration of the break is given in TICs, + which runs at HZ (typically 100) and the + PPR runs at 200 Hz, so the delay is + duration * 200/HZ, and thus a break can + run from 1/100 sec to about 5/4 sec. + */ + base_addr[CyTDR<x_break*200/HZ; + base_addr[CyTDR<x_break = 0; + } while (char_count-- > 0){ if (!info->xmit_cnt){ - base_addr[CySRER<xmit_buf == 0){ - base_addr[CySRER<tty->stopped || info->tty->hw_stopped){ - base_addr[CySRER<xmit_buf == 0){ + base_addr[CySRER<tty->stopped || info->tty->hw_stopped){ + base_addr[CySRER<xmit_buf[info->xmit_tail]; if( outch ){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR< 1){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR<first_line]; + info = &cy_port[channel + chip * 4 + + cinfo->first_line]; info->last_active = jiffies; save_car = base_addr[CyCAR<tty == 0){ /* nowhere to put the data, ignore it */ + if(info->tty == 0){/* no place for data, ignore it*/ ; }else{ if((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)){ if(mdm_status & CyDCD){ - cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); - }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) - &&(info->flags & ASYNC_CALLOUT_NOHUP))){ - cy_sched_event(info, Cy_EVENT_HANGUP); + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + cy_sched_event(info, + Cy_EVENT_HANGUP); } } if((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)){ if(info->tty->hw_stopped){ if(mdm_status & CyCTS){ - /* !!! cy_start isn't used because... */ + /* cy_start isn't used + because... !!! */ info->tty->hw_stopped = 0; - base_addr[CySRER<tty->hw_stopped = 1; - base_addr[CySRER<base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return (-1); + } + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + + loc_doorbell = ((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->loc_doorbell; + if (loc_doorbell){ + *cmd = (char)(0xff & loc_doorbell); + *channel = board_ctrl->fwcmd_channel; + *param = board_ctrl->fwcmd_param; + ((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->loc_doorbell = 0xffffffff; + return 1; + } + return 0; +} /* cyz_fetch_msg */ - tty = info->tty; - if (!tty) - return; - if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~(ASYNC_NORMAL_ACTIVE| - ASYNC_CALLOUT_ACTIVE); - } - if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { - wake_up_interruptible(&info->open_wait); +static int +cyz_issue_cmd( struct cyclades_card *cinfo, + u_long channel, u_char cmd, u_long *param) +{ + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + volatile unsigned long *pci_doorbell; + int index; + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return (-1); } - if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + + index = 0; + pci_doorbell = &((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->pci_doorbell; + while( (*pci_doorbell & 0xff) != 0){ + if (index++ == 100){ + return(-1); + } + udelay(50L); } -} /* do_softint */ + board_ctrl->hcmd_channel = channel; + board_ctrl->hcmd_param = param; + *pci_doorbell = (long)cmd; + + return(0); +} /* cyz_issue_cmd */ -/* - * Grab all interrupts in preparation for doing an automatic irq - * detection. dontgrab is a mask of irq's _not_ to grab. Returns a - * mask of irq's which were grabbed and should therefore be freed - * using free_all_interrupts(). - */ static int -grab_all_interrupts(int dontgrab) -{ - int irq_lines = 0; - int i, mask; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if (!(mask & dontgrab) - && !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe", NULL)) { - irq_lines |= mask; - } +cyz_update_channel( struct cyclades_card *cinfo, + u_long channel, u_char mode, u_char cmd) +{ + struct FIRM_ID *firm_id = + (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + struct ZFW_CTRL *zfw_ctrl; + struct CH_CTRL *ch_ctrl; + + if (firm_id->signature != ZFIRM_ID){ + return (-1); } - return irq_lines; -} /* grab_all_interrupts */ + zfw_ctrl = + (struct ZFW_CTRL *)(cinfo->base_addr + firm_id->zfwctrl_addr); + ch_ctrl = zfw_ctrl->ch_ctrl; + + ch_ctrl[channel].op_mode = (long)mode; + + return cyz_issue_cmd(cinfo, channel, cmd, 0L); + +} /* cyz_update_channel */ + -/* - * Release all interrupts grabbed by grab_all_interrupts - */ static void -free_all_interrupts(int irq_lines) +cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int i; - - for (i = 0; i < 16; i++) { - if (irq_lines & (1 << i)) - free_irq(i,NULL); - } -} /* free_all_interrupts */ +} /* cyz_interrupt */ -/* - * This routine returns a bitfield of "wild interrupts". Basically, - * any unclaimed interrupts which is flapping around. - */ -static int -check_wild_interrupts(void) + +static void +cyz_poll(unsigned long arg) { - int i, mask; - int wild_interrupts = 0; - int irq_lines; - unsigned long timeout; - unsigned long flags; - - /*Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + struct BUF_CTRL *buf_ctrl; + struct cyclades_card *cinfo; + struct cyclades_port *info; + struct tty_struct *tty; + int card, port; + int char_count, small_count; + char data; + u_long channel; + u_char cmd; + u_long *param; - irq_lines = grab_all_interrupts(0); - - /* - * Delay for 0.1 seconds -- we use a busy loop since this may - * occur during the bootup sequence - */ - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - cy_triggered = 0; /* Reset after letting things settle */ - - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if ((cy_triggered & (1 << i)) && - (irq_lines & (1 << i))) { - wild_interrupts |= mask; - } + cyz_timerlist.expires = jiffies + 100; + + for (card = 0 ; card < NR_CARDS ; card++){ + cinfo = &cy_card[card]; + if (!IS_CYC_Z(*cinfo)) continue; + + firm_id = (struct FIRM_ID *) + (cinfo->base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + continue; } - free_all_interrupts(irq_lines); - restore_flags(flags); - return wild_interrupts; -} /* check_wild_interrupts */ -/* - * This routine is called by do_auto_irq(); it attempts to determine - * which interrupt a serial port is configured to use. It is not - * fool-proof, but it works a large part of the time. - */ -static int -get_auto_irq(unsigned char *address) -{ - unsigned long timeout; - unsigned char *base_addr; - int index; - - index = 0; /* IRQ probing is only for ISA */ - base_addr = address; - intr_base_addr = address; - - /* - * Enable interrupts and see who answers - */ - cy_irq_triggered = 0; - cli(); - base_addr[CyCAR<= jiffies) { - if (cy_irq_triggered) - break; - } - probe_ready = 0; - return(cy_irq_triggered); -} /* get_auto_irq */ + zfw_ctrl = + (struct ZFW_CTRL *) + (cinfo->base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + + while( cyz_fetch_msg( cinfo, &channel, &cmd, ¶m) == 1){ + char_count = 0; + info = &cy_port[ channel + cinfo->first_line ]; + if((tty = info->tty) == 0) continue; + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + info->jiffies[0] = jiffies; + + switch(cmd){ + case C_CM_PR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_FR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_RXBRK: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_MDCD: + if (info->flags & ASYNC_CHECK_CD){ + if( ch_ctrl[channel].rs_status & C_RS_DCD){ + /* SP("Open Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + /* SP("Hangup\n"); */ + cy_sched_event(info, + Cy_EVENT_HANGUP); + } + } + break; + case C_CM_MCTS: + if (info->flags & ASYNC_CTS_FLOW) { + if(info->tty->hw_stopped){ + if( ch_ctrl[channel].rs_status & C_RS_DCD){ + /* cy_start isn't used because... + HW flow is handled by the board */ + /* SP("Write Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_WRITE_WAKEUP); + } + }else{ + if(!(ch_ctrl[channel].rs_status & C_RS_CTS)){ + /* cy_stop isn't used because + HW flow is handled by the board */ + /* SP("Write stop\n"); */ + } + } + } + break; + case C_CM_MRI: + break; + case C_CM_MDSR: + break; + case C_CM_FATAL: + /* should do something with this !!! */ + break; + } + if(char_count){ + queue_task(&tty->flip.tqueue, &tq_timer); + } + } -/* - * Calls get_auto_irq() multiple times, to make sure we don't get - * faked out by random interrupts - */ -static int -do_auto_irq(unsigned char *address) -{ - int irq_lines = 0; - int irq_try_1 = 0, irq_try_2 = 0; - int retries; - unsigned long flags; + for (port = 0; port < board_ctrl->n_channel; port++){ + info = &cy_port[ port + cinfo->first_line ]; + tty = info->tty; + ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + if ((char_count = CHARS_IN_BUF(buf_ctrl))){ + info->last_active = jiffies; + info->jiffies[1] = jiffies; - probe_ready = 0; +#ifdef CYCLOM_ENABLE_MONITORING + info->mon.int_count++; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + if( tty == 0){ + /* flush received characters */ + buf_ctrl->rx_get = + (buf_ctrl->rx_get + char_count) + % buf_ctrl->rx_bufsize; + /* SP("-"); */ + info->rflush_count++; + }else{ +#ifdef BLOCKMOVE + /* we'd like to use memcpy(t, f, n) and memset(s, c, count) + for performance, but because of buffer boundaries, there + may be several steps to the operation */ + while(0 < (small_count + = cy_min( (buf_ctrl->rx_bufsize - buf_ctrl->rx_get), + cy_min( (TTY_FLIPBUF_SIZE - tty->flip.count), + char_count)))){ + memcpy(tty->flip.char_buf_ptr, + (char *)(cinfo->base_addr + + buf_ctrl->rx_bufaddr + + buf_ctrl->rx_get), + small_count); + tty->flip.char_buf_ptr += small_count; + memset(tty->flip.flag_buf_ptr, + TTY_NORMAL, + small_count); + tty->flip.flag_buf_ptr += small_count; + buf_ctrl->rx_get = + (buf_ctrl->rx_get + small_count) + % buf_ctrl->rx_bufsize; + char_count -= small_count; + tty->flip.count += small_count; + } +#else + while(char_count--){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + break; + } + data = *(char *) (cinfo->base_addr + + buf_ctrl->rx_bufaddr + + buf_ctrl->rx_get); + buf_ctrl->rx_get = + (buf_ctrl->rx_get + 1) + % buf_ctrl->rx_bufsize; + + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; + } +#endif + queue_task(&tty->flip.tqueue, &tq_timer); + } + } - cy_wild_int_mask = check_wild_interrupts(); + if ((char_count = SPACE_IN_BUF(buf_ctrl))){ + if( tty == 0){ + goto ztxdone; + } - irq_lines = grab_all_interrupts(cy_wild_int_mask); - - for (retries = 0; retries < 5; retries++) { - if (!irq_try_1) - irq_try_1 = get_auto_irq(address); - if (!irq_try_2) - irq_try_2 = get_auto_irq(address); - if (irq_try_1 && irq_try_2) { - if (irq_try_1 == irq_try_2) - break; - irq_try_1 = irq_try_2 = 0; + if(info->x_char) { /* send special char */ + data = info->x_char; + + *(char *) (cinfo->base_addr + + buf_ctrl->tx_bufaddr + + buf_ctrl->tx_put) = data; + buf_ctrl->tx_put = + (buf_ctrl->tx_put + 1) + % buf_ctrl->tx_bufsize; + info->x_char = 0; + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } + if (info->x_break){ + printk("cyc cyz_poll shouldn't see x_break\n"); + info->x_break = 0; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#ifdef BLOCKMOVE + while(0 < (small_count + = cy_min( (buf_ctrl->tx_bufsize - buf_ctrl->tx_put), + cy_min ( (PAGE_SIZE - info->xmit_tail), + cy_min( info->xmit_cnt, char_count))))){ + memcpy((char *)(cinfo->base_addr + + buf_ctrl->tx_bufaddr + + buf_ctrl->tx_put), + &info->xmit_buf[info->xmit_tail], + small_count); + buf_ctrl->tx_put = + (buf_ctrl->tx_put + small_count) + % buf_ctrl->tx_bufsize; + char_count -= small_count; + info->xmit_cnt -= small_count; + info->xmit_tail = + (info->xmit_tail + small_count) & (PAGE_SIZE - 1); + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#else + while (info->xmit_cnt && char_count){ + data = info->xmit_buf[info->xmit_tail]; + info->xmit_cnt--; + info->xmit_tail = + (info->xmit_tail + 1) & (PAGE_SIZE - 1); + + *(char *) (cinfo->base_addr + + buf_ctrl->tx_bufaddr + + buf_ctrl->tx_put) = data; + buf_ctrl->tx_put = + (buf_ctrl->tx_put + 1) + % buf_ctrl->tx_bufsize; + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#endif + ztxdone: + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } } } - restore_flags(flags); - free_all_interrupts(irq_lines); - return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; -} /* do_auto_irq */ + + /* poll every 40 ms */ + cyz_timerlist.expires = jiffies + 4; + } + add_timer(&cyz_timerlist); + + return; +} /* cyz_poll */ + + +/********** End of block of Cyclom-Z specific code *********/ +/***********************************************************/ /* This is called whenever a port becomes active; @@ -1217,72 +1724,130 @@ startup(struct cyclades_port * info) int card,chip,channel,index; if (info->flags & ASYNC_INITIALIZED){ - return 0; + return 0; } if (!info->type){ - if (info->tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); - } - return 0; + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + return 0; } if (!info->xmit_buf){ - info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); - if (!info->xmit_buf){ - return -ENOMEM; - } + info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); + if (!info->xmit_buf){ + return -ENOMEM; + } } - config_setup(info); + set_line_char(info); card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) (cy_card[card].base_addr + (cy_chip_offset[chip]<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + base_addr[CyRTPR<default_timeout + ? info->default_timeout + : 0x02); /* 10ms rx timeout */ - write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); - base_addr[CyCAR<flags |= ASYNC_INITIALIZED; + base_addr[CySRER<flags |= ASYNC_INITIALIZED; + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + base_addr = (unsigned char*) (cy_card[card].base_addr); + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return -ENODEV; + } + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + +#ifdef SERIAL_DEBUG_OPEN + printk("cyc startup Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr);/**/ +#endif + + ch_ctrl[channel].op_mode = C_CH_ENABLE; + ch_ctrl[channel].intr_enable = C_IN_MDCD|C_IN_MCTS; + retval = cyz_issue_cmd( &cy_card[card], + channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */ + if (retval != 0){ + printk("cyc:startup(1) retval was %x\n", retval); + } + + /* set timeout !!! */ + /* set RTS and DTR !!! */ + ch_ctrl[channel].rs_control |= + C_RS_RTS | C_RS_DTR ; + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:startup(2) retval was %x\n", retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:startup raising Z DTR\n"); +#endif + + /* enable send, recv, modem !!! */ + + info->flags |= ASYNC_INITIALIZED; if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); + } #ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); + printk(" cyc startup done\n"); #endif - return 0; + return 0; } /* startup */ -void + +static void start_xmit( struct cyclades_port *info ) { unsigned long flags; @@ -1291,18 +1856,24 @@ start_xmit( struct cyclades_port *info ) card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<flags & ASYNC_INITIALIZED)){ - return; + return; } card = info->card; channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<xmit_buf){ - unsigned char * temp; - temp = info->xmit_buf; - info->xmit_buf = 0; - free_page((unsigned long) temp); - } + /* REALLY SHOULD WAIT FOR LAST CHARACTER TO BE + SENT BEFORE DROPPING THE LINE !!! (Perhaps + set some flag that is read when XMTY happens.) + Other choices are to delay some fixed interval + or schedule some later processing. + */ + save_flags(flags); cli(); + if (info->xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } - base_addr[CyCAR<tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1<tty || (info->tty->termios->c_cflag & HUPCL)) { + base_addr[CyMSVR1<tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (unsigned char*) (cy_card[card].base_addr); +#ifdef SERIAL_DEBUG_OPEN + printk("cyc shutdown Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr); +#endif + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return; } - info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + save_flags(flags); cli(); + if (info->xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + ch_ctrl[channel].rs_control &= + ~(C_RS_RTS | C_RS_DTR ); + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:shutdown retval was %x\n", + retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:shutdown dropping Z DTR\n"); +#endif + } + + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + + restore_flags(flags); + } #ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); + printk(" cyc shutdown done\n"); #endif return; } /* shutdown */ + /* - * This routine finds or computes the various line characteristics. + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ */ -static void -config_setup(struct cyclades_port * info) + +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct cyclades_port *info) { + struct wait_queue wait = { current, NULL }; + struct cyclades_card *cinfo; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - unsigned cflag; - int i; - - if (!info->tty || !info->tty->termios){ - return; - } - if (info->line == -1){ - return; - } - cflag = info->tty->termios->c_cflag; + int chip, channel,index; + int retval; + char *base_addr; - /* baud rate */ - i = cflag & CBAUD; -#ifdef CBAUDEX -/* Starting with kernel 1.1.65, there is direct support for - higher baud rates. The following code supports those - changes. The conditional aspect allows this driver to be - used for earlier as well as later kernel versions. (The - mapping is slightly different from serial.c because there - is still the possibility of supporting 75 kbit/sec with - the Cyclades board.) - */ - if (i & CBAUDEX) { - if (i == B57600) - i = 16; - else if(i == B115200) - i = 18; -#ifdef B78600 - else if(i == B78600) - i = 17; -#endif - else - info->tty->termios->c_cflag &= ~CBAUDEX; - } -#endif - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 3; - } - info->tbpr = baud_bpr[i]; /* Tx BPR */ - info->tco = baud_co[i]; /* Tx CO */ - info->rbpr = baud_bpr[i]; /* Rx BPR */ - info->rco = baud_co[i]; /* Rx CO */ - if (baud_table[i] == 134) { - info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; - /* get it right for 134.5 baud */ - } else if (baud_table[i]) { - info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - /* By tradition (is it a standard?) a baud rate of zero - implies the line should be/has been closed. A bit - later in this routine such a test is performed. */ - - /* byte size and parity */ - info->cor5 = 0; - info->cor4 = 0; - info->cor3 = (info->default_threshold - ? info->default_threshold - : baud_cor3[i]); /* receive threshold */ - info->cor2 = CyETC; - switch(cflag & CSIZE){ - case CS5: - info->cor1 = Cy_5_BITS; - break; - case CS6: - info->cor1 = Cy_6_BITS; - break; - case CS7: - info->cor1 = Cy_7_BITS; - break; - case CS8: - info->cor1 = Cy_8_BITS; - break; - } - if(cflag & CSTOPB){ - info->cor1 |= Cy_2_STOP; - } - if (cflag & PARENB){ - if (cflag & PARODD){ - info->cor1 |= CyPARITY_O; + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + if (info->flags & ASYNC_HUP_NOTIFY){ + return -EAGAIN; }else{ - info->cor1 |= CyPARITY_E; + return -ERESTARTSYS; } - }else{ - info->cor1 |= CyPARITY_NONE; } - - /* CTS flow control flag */ - if (cflag & CRTSCTS){ - info->flags |= ASYNC_CTS_FLOW; - info->cor2 |= CyCtsAE; - }else{ - info->flags &= ~ASYNC_CTS_FLOW; - info->cor2 &= ~CyCtsAE; - } - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - /*********************************************** - The hardware option, CyRtsAO, presents RTS when - the chip has characters to send. Since most modems - use RTS as reverse (inbound) flow control, this - option is not used. If inbound flow control is - necessary, DTR can be programmed to provide the - appropriate signals for use with a non-standard - cable. Contact Marcio Saito for details. - ***********************************************/ - - card = info->card; - channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<tco; - base_addr[CyTBPR<tbpr; - base_addr[CyRCOR<rco; - base_addr[CyRBPR<rbpr; - - /* set line characteristics according configuration */ - - base_addr[CySCHR1<tty); - base_addr[CySCHR2<tty); - base_addr[CyCOR1<cor1; - base_addr[CyCOR2<cor2; - base_addr[CyCOR3<cor3; - base_addr[CyCOR4<cor4; - base_addr[CyCOR5<cor5; - - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); - - base_addr[CyCAR<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)){ + return -EBUSY; + } + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } - if (C_CLOCAL(info->tty)) { - base_addr[CySRER<0 modem transitions */ - base_addr[CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<0 modem transitions */ - base_addr[CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<f_flags & O_NONBLOCK) { + if (info->flags & ASYNC_CALLOUT_ACTIVE){ + return -EBUSY; + } + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } - if(i == 0){ /* baud rate is zero, turn off line */ - base_addr[CyMSVR2<count is dropped by one, so that + * cy_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready before block: ttyC%d, count = %d\n", + info->line, info->count);/**/ #endif - }else{ - base_addr[CyMSVR2<count--; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc block_til_ready: (%d): decrementing count to %d\n", + current->pid, info->count); +#endif + info->blocked_open++; + + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + index = cinfo->bus_index; + base_addr = (char *)(cinfo->base_addr + + (cy_chip_offset[chip]<flags & ASYNC_CALLOUT_ACTIVE)){ + base_addr[CyCAR<state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + save_flags(flags); cli(); + base_addr[CyCAR<flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (base_addr[CyMSVR1<signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ #endif + schedule(); } - - if (info->tty){ - clear_bit(TTY_IO_ERROR, &info->tty->flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (char *)(cinfo->base_addr); + firm_id = (struct FIRM_ID *) + (base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return -EINVAL; } - restore_flags(flags); + zfw_ctrl = + (struct ZFW_CTRL *) + (base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + while (1) { + ch_ctrl[channel].rs_control |= + C_RS_RTS | C_RS_DTR ; + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:block_til_ready retval was %x\n", retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:block_til_ready raising Z DTR\n"); +#endif -} /* config_setup */ + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (ch_ctrl[channel].rs_status & C_RS_DCD))) { + break; + } + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + schedule(); + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)){ + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:block_til_ready (%d): incrementing count to %d\n", + current->pid, info->count); +#endif + } + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:block_til_ready after blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} /* block_til_ready */ -static void -cy_put_char(struct tty_struct *tty, unsigned char ch) +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +int +cy_open(struct tty_struct *tty, struct file * filp) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; + struct cyclades_port *info; + int retval, line; -#ifdef SERIAL_DEBUG_IO - printk("cy_put_char ttyC%d\n", info->line); + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (NR_PORTS <= line)){ + return -ENODEV; + } + info = &cy_port[line]; + if (info->line < 0){ + return -ENODEV; + } + + /* If the card's firmware hasn't been loaded, + treat it as absent from the system. This + will make the user pay attention. + */ + if (IS_CYC_Z(cy_card[info->card])) { + struct FIRM_ID *firm_id = + (struct FIRM_ID *) + (cy_card[info->card].base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + printk("Cyclom-Z firmware not yet loaded\n"); + return -ENODEV; + } + } +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_open ttyC%d\n", info->line); /* */ +#endif + if (serial_paranoia_check(info, tty->device, "cy_open")){ + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_open ttyC%d, count = %d\n", + info->line, info->count);/**/ #endif + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_open (%d): incrementing count to %d\n", + current->pid, info->count); +#endif + tty->driver_data = info; + info->tty = tty; - if (serial_paranoia_check(info, tty->device, "cy_put_char")) - return; + /* Some drivers have (incorrect/incomplete) code to test + against a race condition. Should add good code here!!! */ + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!tmp_buf){ + return -ENOMEM; + } + } - if (!tty || !info->xmit_buf) - return; + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + } + /* + * Start up serial port + */ + retval = startup(info); + if (retval){ + return retval; + } - save_flags(flags); cli(); - if (info->xmit_cnt >= PAGE_SIZE - 1) { - restore_flags(flags); - return; - } + MOD_INC_USE_COUNT; - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= PAGE_SIZE - 1; - info->xmit_cnt++; - restore_flags(flags); -} /* cy_put_char */ + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + info->session = current->session; + info->pgrp = current->pgrp; +#ifdef SERIAL_DEBUG_OPEN + printk(" cyc:cy_open done\n");/**/ +#endif + return 0; +} /* cy_open */ + + +/* + * This routine is called when a particular tty device is closed. + */ static void -cy_flush_chars(struct tty_struct *tty) +cy_close(struct tty_struct * tty, struct file * filp) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_chars ttyC%d\n", info->line); /* */ + +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_close ttyC%d\n", info->line); #endif - if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) - return; + if (!info + || serial_paranoia_check(info, tty->device, "cy_close")){ + return; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); +#endif - if (info->xmit_cnt <= 0 || tty->stopped - || tty->hw_stopped || !info->xmit_buf) - return; + save_flags(flags); cli(); - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("cyc:cy_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_close at (%d): decrementing count to %d\n", + current->pid, info->count - 1); +#endif + if (--info->count < 0) { +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cyc_close setting count to 0\n"); +#endif + info->count = 0; + } + if (info->count) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + if (info->flags & ASYNC_INITIALIZED) + tty_wait_until_sent(tty, 5*HZ); /* 5 seconds timeout */ + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); - save_flags(flags); cli(); - base_addr[CyCAR<line); /* */ + printk("cyc:cy_write ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_write")){ - return 0; + return 0; } - + if (!tty || !info->xmit_buf || !tmp_buf){ return 0; } + if (from_user) + down(&tmp_buf_sem); while (1) { - save_flags(flags); cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0){ - restore_flags(flags); - break; - } + save_flags(flags); cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0){ + restore_flags(flags); + break; + } - if (from_user) { - down(&tmp_buf_sem); - copy_from_user(tmp_buf, buf, c); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - up(&tmp_buf_sem); - } else - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - restore_flags(flags); - buf += c; - count -= c; - total += c; + if (from_user) { + memcpy_fromfs(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; +#if 0 + SP("CW"); + CP16(c); + SP(" "); +#endif } + if (from_user) + up(&tmp_buf_sem); if (info->xmit_cnt @@ -1692,21 +2479,110 @@ cy_write(struct tty_struct * tty, int from_user, } /* cy_write */ +/* + * This routine is called by the kernel to write a single + * character to the tty device. If the kernel uses this routine, + * it must call the flush_chars() routine (if defined) when it is + * done stuffing characters into the driver. If there is no room + * in the queue, the character is ignored. + */ +static void +cy_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_put_char ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt >= PAGE_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= PAGE_SIZE - 1; + info->xmit_cnt++; + restore_flags(flags); +#if 0 + SP("+"); +#endif +} /* cy_put_char */ + + +/* + * This routine is called by the kernel after it has written a + * series of characters to the tty device using put_char(). + */ +static void +cy_flush_chars(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; + +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped + || tty->hw_stopped || !info->xmit_buf) + return; + + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<driver_data; - int ret; - + int ret; + #ifdef SERIAL_DEBUG_IO - printk("cy_write_room ttyC%d\n", info->line); /* */ + printk("cyc:cy_write_room ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_write_room")) - return 0; + return 0; ret = PAGE_SIZE - info->xmit_cnt - 1; if (ret < 0) - ret = 0; + ret = 0; return ret; } /* cy_write_room */ @@ -1715,126 +2591,351 @@ static int cy_chars_in_buffer(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - + #ifdef SERIAL_DEBUG_IO - printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */ + printk("cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) - return 0; + return 0; return info->xmit_cnt; } /* cy_chars_in_buffer */ -static void -cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_buffer ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) - return; - save_flags(flags); cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} /* cy_flush_buffer */ +/* + * ------------------------------------------------------------ + * cy_ioctl() and friends + * ------------------------------------------------------------ + */ -/* This routine is called by the upper-layer tty layer to signal - that incoming characters should be throttled or that the - throttle should be released. +/* + * This routine finds or computes the various line characteristics. + * It used to be called config_setup */ static void -cy_throttle(struct tty_struct * tty) +set_line_char(struct cyclades_port * info) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; unsigned char *base_addr; int card,chip,channel,index; + unsigned cflag; + int i; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_throttle ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ - return; + if (!info->tty || !info->tty->termios){ + return; } - - if (I_IXOFF(tty)) { - info->x_char = STOP_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ + if (info->line == -1){ + return; } + cflag = info->tty->termios->c_cflag; card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<line) - (cy_card[card].first_line); + + if (!IS_CYC_Z(cy_card[card])) { + /* baud rate */ + i = cflag & CBAUD; + + if (i & CBAUDEX) { + if (i == B57600) + i = 16; + else if(i == B115200) + i = 18; +#ifdef B76800 + else if(i == B76800) + i = 17; +#endif + else + info->tty->termios->c_cflag &= ~CBAUDEX; + } + + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + } + info->tbpr = baud_bpr[i]; /* Tx BPR */ + info->tco = baud_co[i]; /* Tx CO */ + info->rbpr = baud_bpr[i]; /* Rx BPR */ + info->rco = baud_co[i]; /* Rx CO */ + if (baud_table[i] == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (baud_table[i]) { + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor5 = 0; + info->cor4 = 0; + info->cor3 = (info->default_threshold + ? info->default_threshold + : baud_cor3[i]); /* receive threshold */ + info->cor2 = CyETC; + switch(cflag & CSIZE){ + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if(cflag & CSTOPB){ + info->cor1 |= Cy_2_STOP; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + info->cor1 |= CyPARITY_O; + }else{ + info->cor1 |= CyPARITY_E; + } + }else{ + info->cor1 |= CyPARITY_NONE; + } + + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<tco; + base_addr[CyTBPR<tbpr; + base_addr[CyRCOR<rco; + base_addr[CyRBPR<rbpr; + + /* set line characteristics according configuration */ + + base_addr[CySCHR1<tty); + base_addr[CySCHR2<tty); + base_addr[CyCOR1<cor1; + base_addr[CyCOR2<cor2; + base_addr[CyCOR3<cor3; + base_addr[CyCOR4<cor4; + base_addr[CyCOR5<cor5; + + cyy_issue_cmd(base_addr, + CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); + + base_addr[CyCAR<default_timeout + ? info->default_timeout + : 0x02); /* 10ms rx timeout */ + + if (C_CLOCAL(info->tty)) { + base_addr[CySRER<0 modem transitions */ + base_addr[CyMCOR1<1 modem transitions */ + base_addr[CyMCOR2<0 modem transitions */ + base_addr[CyMCOR1<1 modem transitions */ + base_addr[CyMCOR2<tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + struct BUF_CTRL *buf_ctrl; + int retval; + + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return; + } + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; + buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; - save_flags(flags); cli(); - base_addr[CyCAR<comm_baud = 0; break; + */ + case B50: ch_ctrl->comm_baud = 50; break; + case B75: ch_ctrl->comm_baud = 75; break; + case B110: ch_ctrl->comm_baud = 110; break; + case B134: ch_ctrl->comm_baud = 134; break; + case B150: ch_ctrl->comm_baud = 150; break; + case B200: ch_ctrl->comm_baud = 200; break; + case B300: ch_ctrl->comm_baud = 300; break; + case B600: ch_ctrl->comm_baud = 600; break; + case B1200: ch_ctrl->comm_baud = 1200; break; + case B1800: ch_ctrl->comm_baud = 1800; break; + case B2400: ch_ctrl->comm_baud = 2400; break; + case B4800: ch_ctrl->comm_baud = 4800; break; + case B9600: ch_ctrl->comm_baud = 9600; break; + case B19200: ch_ctrl->comm_baud = 19200; break; + case B38400: + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){ + ch_ctrl->comm_baud = 57600; + }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){ + ch_ctrl->comm_baud = 115200; + }else{ + ch_ctrl->comm_baud = 38400; + } + break; + case B57600: ch_ctrl->comm_baud = 57600; break; +#ifdef B76800 + case B76800: ch_ctrl->comm_baud = 76800; break; +#endif + case B115200: ch_ctrl->comm_baud = 115200; break; + case B230400: ch_ctrl->comm_baud = 230400; break; + case B460800: ch_ctrl->comm_baud = 460800; break; + } + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + ch_ctrl->comm_baud = info->baud; + } - return; -} /* cy_throttle */ + /* byte size and parity */ + switch(cflag & CSIZE){ + case CS5: ch_ctrl->comm_data_l = C_DL_CS5; break; + case CS6: ch_ctrl->comm_data_l = C_DL_CS6; break; + case CS7: ch_ctrl->comm_data_l = C_DL_CS7; break; + case CS8: ch_ctrl->comm_data_l = C_DL_CS8; break; + } + if(cflag & CSTOPB){ + ch_ctrl->comm_data_l |= C_DL_2STOP; + }else{ + ch_ctrl->comm_data_l |= C_DL_1STOP; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + ch_ctrl->comm_parity = C_PR_ODD; + }else{ + ch_ctrl->comm_parity = C_PR_EVEN; + } + }else{ + ch_ctrl->comm_parity = C_PR_NONE; + } + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + ch_ctrl->hw_flow |= C_RS_CTS | C_RS_RTS; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + ch_ctrl->hw_flow &= ~(C_RS_CTS | C_RS_RTS); + } -static void -cy_unthrottle(struct tty_struct * tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; + retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_unthrottle ttyC%d\n", info->line); + /* CD sensitivity */ + if (cflag & CLOCAL){ + info->flags &= ~ASYNC_CHECK_CD; + }else{ + info->flags |= ASYNC_CHECK_CD; + } + + if(i == 0){ /* baud rate is zero, turn off line */ + ch_ctrl->rs_control &= ~C_RS_DTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char dropping Z DTR\n"); +#endif + }else{ + ch_ctrl->rs_control |= C_RS_DTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char raising Z DTR\n"); #endif + } - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ - return; - } + retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } - if (I_IXOFF(tty)) { - info->x_char = START_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } } - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card * 0x100 + info->line - cinfo->first_line; tmp.irq = cinfo->irq; tmp.flags = info->flags; - tmp.baud_base = 0; /*!!!*/ tmp.close_delay = info->close_delay; + tmp.baud_base = info->baud; tmp.custom_divisor = 0; /*!!!*/ tmp.hub6 = 0; /*!!!*/ - copy_to_user(retinfo,&tmp,sizeof(*retinfo)); + memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); return 0; } /* get_serial_info */ + static int set_serial_info(struct cyclades_port * info, struct serial_struct * new_info) @@ -1867,18 +2969,19 @@ set_serial_info(struct cyclades_port * info, struct cyclades_port old_info; if (!new_info) - return -EFAULT; - copy_from_user(&new_serial,new_info,sizeof(new_serial)); + return -EFAULT; + memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { - if ((new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != - (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - goto check_and_exit; + if ((new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != + (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->baud = new_serial.baud_base; + goto check_and_exit; } @@ -1888,19 +2991,21 @@ set_serial_info(struct cyclades_port * info, */ info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); + (new_serial.flags & ASYNC_FLAGS)); + info->baud = new_serial.baud_base; info->close_delay = new_serial.close_delay; check_and_exit: if (info->flags & ASYNC_INITIALIZED){ - config_setup(info); - return 0; + set_line_char(info); + return 0; }else{ return startup(info); } } /* set_serial_info */ + static int get_modem_info(struct cyclades_port * info, unsigned int *value) { @@ -1908,259 +3013,423 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) unsigned char *base_addr; unsigned long flags; unsigned char status; + unsigned long lstatus; unsigned int result; + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<signature == ZFIRM_ID){ + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + lstatus = ch_ctrl[channel].rs_status; + result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) + | ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) + | ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) + | ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) + | ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) + | ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); + }else{ + result = 0; + return -ENODEV; + } - result = ((status & CyRTS) ? TIOCM_RTS : 0) - | ((status & CyDTR) ? TIOCM_DTR : 0) - | ((status & CyDCD) ? TIOCM_CAR : 0) - | ((status & CyRI) ? TIOCM_RNG : 0) - | ((status & CyDSR) ? TIOCM_DSR : 0) - | ((status & CyCTS) ? TIOCM_CTS : 0); - put_user(result,(unsigned int *) value); + } + put_fs_long(result,(unsigned long *) value); return 0; } /* get_modem_info */ + static int set_modem_info(struct cyclades_port * info, unsigned int cmd, unsigned int *value) { - int card,chip,channel,index; - unsigned char *base_addr; - unsigned long flags; - unsigned int arg; - int error; - - error = get_user(arg, value); - if (error) - return error; + int card,chip,channel,index; + unsigned char *base_addr; + unsigned long flags; + unsigned int arg = get_fs_long((unsigned long *) value); + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<signature == ZFIRM_ID){ + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + ch_ctrl[channel].rs_control |= C_RS_RTS; + } + if (arg & TIOCM_DTR){ + ch_ctrl[channel].rs_control |= C_RS_DTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<card], + channel, C_CM_IOCTLM,0L); + if (retval != 0){ + printk("cyc:set_modem_info retval at %d was %x\n", + __LINE__, retval); + } + } return 0; } /* set_modem_info */ + static void send_break( struct cyclades_port * info, int duration) -{ /* Let the transmit ISR take care of this (since it - requires stuffing characters into the output stream). - */ - info->x_break = duration; - if (!info->xmit_cnt ) { - start_xmit(info); +{ + + if (!IS_CYC_Z(cy_card[info->card])) { + /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + info->x_break = duration; + if (!info->xmit_cnt ) { + start_xmit(info); + } + } else { + /* For the moment we ignore the duration parameter!!! + A better implementation will use C_CM_SET_BREAK + and C_CM_CLR_BREAK with the appropriate delay. + */ +#if 0 +this appears to wedge the output data stream +int retval; + retval = cyz_issue_cmd(&cy_card[info->card], + (info->line) - (cy_card[info->card].first_line), + C_CM_SENDBRK, 0L); + if (retval != 0){ + printk("cyc:send_break retval at %d was %x\n", + __LINE__, retval); + } +#endif } } /* send_break */ + static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { - copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); - info->mon.int_count = 0; - info->mon.char_count = 0; - info->mon.char_max = 0; - info->mon.char_last = 0; - return 0; -} + memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor)); + info->mon.int_count = 0; + info->mon.char_count = 0; + info->mon.char_max = 0; + info->mon.char_last = 0; + return 0; +}/* get_mon_info */ + static int set_threshold(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<cor3 &= ~CyREC_FIFO; + info->cor3 |= value & CyREC_FIFO; + base_addr[CyCOR3<cor3; + cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); + } else { + // Nothing to do! + } + return 0; +}/* set_threshold */ - info->cor3 &= ~CyREC_FIFO; - info->cor3 |= value & CyREC_FIFO; - base_addr[CyCOR3<cor3; - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); - return 0; -} static int get_threshold(struct cyclades_port * info, unsigned long *value) { - unsigned char *base_addr; - int card,channel,chip,index; - unsigned long tmp; + unsigned char *base_addr; + int card,channel,chip,index; + unsigned long tmp; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<default_threshold = value & 0x0f; - return 0; -} + info->default_threshold = value & 0x0f; + return 0; +}/* set_default_threshold */ + static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - put_user(info->default_threshold,value); - return 0; -} + put_fs_long(info->default_threshold,value); + return 0; +}/* get_default_threshold */ + static int set_timeout(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<default_timeout = value & 0xff; - return 0; -} + info->default_timeout = value & 0xff; + return 0; +}/* set_default_timeout */ + static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - put_user(info->default_timeout,value); - return 0; -} + put_fs_long(info->default_timeout,value); + return 0; +}/* get_default_timeout */ + +/* + * This routine allows the tty driver to implement device- + * specific ioctl's. If the ioctl number passed in cmd is + * not recognized by the driver, it should return ENOIOCTLCMD. + */ static int cy_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -2170,7 +3439,8 @@ cy_ioctl(struct tty_struct *tty, struct file * file, int ret_val = 0; #ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */ + printk("cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", + info->line, cmd, arg); /* */ #endif switch (cmd) { @@ -2182,7 +3452,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, break; } ret_val = get_mon_info(info, (struct cyclades_monitor *)arg); - break; + break; case CYGETTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2190,11 +3460,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_threshold(info, (unsigned long *)arg); - break; + ret_val = get_threshold(info, (unsigned long *)arg); + break; case CYSETTHRESH: ret_val = set_threshold(info, (unsigned long)arg); - break; + break; case CYGETDEFTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2202,11 +3472,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_default_threshold(info, (unsigned long *)arg); - break; + ret_val = get_default_threshold(info, (unsigned long *)arg); + break; case CYSETDEFTHRESH: ret_val = set_default_threshold(info, (unsigned long)arg); - break; + break; case CYGETTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2214,11 +3484,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_timeout(info, (unsigned long *)arg); - break; + ret_val = get_timeout(info, (unsigned long *)arg); + break; case CYSETTIMEOUT: ret_val = set_timeout(info, (unsigned long)arg); - break; + break; case CYGETDEFTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2226,23 +3496,23 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_default_timeout(info, (unsigned long *)arg); - break; + ret_val = get_default_timeout(info, (unsigned long *)arg); + break; case CYSETDEFTIMEOUT: ret_val = set_default_timeout(info, (unsigned long)arg); - break; + break; case TCSBRK: /* SVID version: non-zero arg --> no break */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); if (!arg) send_break(info, HZ/4); /* 1/4 second */ break; case TCSBRKP: /* support for POSIX tcsendbreak() */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); send_break(info, arg ? arg*(HZ/10) : HZ/4); break; @@ -2254,19 +3524,31 @@ cy_ioctl(struct tty_struct *tty, struct file * file, /* The following commands are incompletely implemented!!! */ case TIOCGSOFTCAR: - ret_val = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned int *) arg); - break; + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned int *)); + if (error){ + ret_val = error; + break; + } + put_fs_long(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + break; case TIOCSSOFTCAR: - ret_val = get_user(arg,(unsigned int *) arg); - if (ret_val) - break; + error = verify_area(VERIFY_READ, (void *) arg + ,sizeof(unsigned long *)); + if (error) { + ret_val = error; + break; + } + + arg = get_fs_long((unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); break; case TIOCMGET: error = verify_area(VERIFY_WRITE, (void *) arg - ,sizeof(unsigned int)); + ,sizeof(unsigned int *)); if (error){ ret_val = error; break; @@ -2294,31 +3576,35 @@ cy_ioctl(struct tty_struct *tty, struct file * file, (struct serial_struct *) arg); break; default: - ret_val = -ENOIOCTLCMD; + ret_val = -ENOIOCTLCMD; } #ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl done\n"); + printk(" cyc:cy_ioctl done\n"); #endif return ret_val; } /* cy_ioctl */ - - +/* + * This routine allows the tty driver to be notified when + * device's termios settings have changed. Note that a + * well-designed tty driver should be prepared to accept the case + * where old == NULL, and try to do something rational. + */ static void cy_set_termios(struct tty_struct *tty, struct termios * old_termios) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; #ifdef SERIAL_DEBUG_OTHER - printk("cy_set_termios ttyC%d\n", info->line); + printk("cyc:cy_set_termios ttyC%d\n", info->line); #endif if (tty->termios->c_cflag == old_termios->c_cflag) return; - config_setup(info); + set_line_char(info); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -2335,353 +3621,243 @@ cy_set_termios(struct tty_struct *tty, struct termios * old_termios) } /* cy_set_termios */ +/* + * void (*set_ldisc)(struct tty_struct *tty); + * + * This routine allows the tty driver to be notified when the + * device's termios settings have changed. + * + */ + + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled because the input + buffers are close to full. + */ static void -cy_close(struct tty_struct * tty, struct file * filp) +cy_throttle(struct tty_struct * tty) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close ttyC%d\n", info->line); +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); #endif - if (!info - || serial_paranoia_check(info, tty->device, "cy_close")){ - return; + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_close ttyC%d, count = %d\n", info->line, info->count); -#endif - - save_flags(flags); cli(); - /* If the TTY is being hung up, nothing to do */ - if (tty_hung_up_p(filp)) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; - } - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("cy_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count - 1); -#endif - if (--info->count < 0) { -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->count = 0; + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ } - if (info->count) - { - MOD_DEC_USE_COUNT; + + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<flags |= ASYNC_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & ASYNC_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; - if (info->flags & ASYNC_INITIALIZED) - tty_wait_until_sent(tty, 30*HZ); /* 30 seconds timeout */ - shutdown(info); - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - info->event = 0; - info->tty = 0; - if (info->blocked_open) { - if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); - } - wake_up_interruptible(&info->open_wait); + } else { + // Nothing to do! } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| - ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close done\n"); -#endif - MOD_DEC_USE_COUNT; - restore_flags(flags); return; -} /* cy_close */ - -/* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -void -cy_hangup(struct tty_struct *tty) -{ - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_hangup ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_hangup")) - return; - - shutdown(info); - info->event = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): setting count to 0\n", __LINE__, current->pid); -#endif - info->tty = 0; - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); - wake_up_interruptible(&info->open_wait); -} /* cy_hangup */ - +} /* cy_throttle */ /* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ + * This routine notifies the tty driver that it should signal + * that characters can now be sent to the tty without fear of + * overrunning the input buffers of the line disciplines. */ - -static int -block_til_ready(struct tty_struct *tty, struct file * filp, - struct cyclades_port *info) +static void +cy_unthrottle(struct tty_struct * tty) { - struct wait_queue wait = { current, NULL }; - struct cyclades_card *cinfo; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; - int chip, channel,index; - int retval; - char *base_addr; + unsigned char *base_addr; + int card,chip,channel,index; - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); - if (info->flags & ASYNC_HUP_NOTIFY){ - return -EAGAIN; - }else{ - return -ERESTARTSYS; - } - } +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); +#endif - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { - if (info->flags & ASYNC_NORMAL_ACTIVE){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_SESSION_LOCKOUT) && - (info->session != current->session)){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_PGRP_LOCKOUT) && - (info->pgrp != current->pgrp)){ - return -EBUSY; - } - info->flags |= ASYNC_CALLOUT_ACTIVE; - return 0; + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; } - /* - * If non-blocking mode is set, then make the check up front - * and then exit. - */ - if (filp->f_flags & O_NONBLOCK) { - if (info->flags & ASYNC_CALLOUT_ACTIVE){ - return -EBUSY; - } - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; + if (I_IXOFF(tty)) { + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ } - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * cy_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - info->count--; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count); -#endif - info->blocked_open++; - - cinfo = &cy_card[info->card]; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - index = cinfo->bus_index; - base_addr = (char *) (cinfo->base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<flags & ASYNC_CALLOUT_ACTIVE)){ - base_addr[CyCAR<state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) - || !(info->flags & ASYNC_INITIALIZED) ){ - if (info->flags & ASYNC_HUP_NOTIFY) { - retval = -EAGAIN; - }else{ - retval = -ERESTARTSYS; - } - break; - } save_flags(flags); cli(); base_addr[CyCAR<flags & ASYNC_CALLOUT_ACTIVE) - && !(info->flags & ASYNC_CLOSING) - && (C_CLOCAL(tty) - || (base_addr[CyMSVR1<signal & ~current->blocked) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)){ - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); -#endif + }else{ + // Nothing to do! } - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} /* block_til_ready */ -/* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. - */ -int -cy_open(struct tty_struct *tty, struct file * filp) + return; +} /* cy_unthrottle */ + + +/* cy_start and cy_stop provide software output flow control as a + function of XON/XOFF, software CTS, and other such stuff. +*/ +static void +cy_stop(struct tty_struct *tty) { - struct cyclades_port *info; - int retval, line; + struct cyclades_card *cinfo; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int chip,channel,index; + unsigned long flags; - line = MINOR(tty->device) - tty->driver.minor_start; - if ((line < 0) || (NR_PORTS <= line)){ - return -ENODEV; - } - info = &cy_port[line]; - if (info->line < 0){ - return -ENODEV; +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_stop ttyC%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_stop")) + return; + + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + index = cinfo->bus_index; + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<driver_data; + unsigned char *base_addr; + int chip,channel,index; + unsigned long flags; + #ifdef SERIAL_DEBUG_OTHER - printk("cy_open ttyC%d\n", info->line); /* */ -#endif - if (serial_paranoia_check(info, tty->device, "cy_open")){ - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/ -#endif - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); + printk("cyc:cy_start ttyC%d\n", info->line); /* */ #endif - tty->driver_data = info; - info->tty = tty; - if (!tmp_buf) { - tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); - if (!tmp_buf){ - return -ENOMEM; - } + if (serial_paranoia_check(info, tty->device, "cy_start")) + return; + + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + index = cinfo->bus_index; + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - } - /* - * Start up serial port - */ - retval = startup(info); - if (retval){ - return retval; - } + return; +} /* cy_start */ - MOD_INC_USE_COUNT; - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open returning after block_til_ready with %d\n", - retval); +/* + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void +cy_hangup(struct tty_struct *tty) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_hangup ttyC%d\n", info->line); /* */ #endif - return retval; - } - info->session = current->session; - info->pgrp = current->pgrp; + if (serial_paranoia_check(info, tty->device, "cy_hangup")) + return; + + shutdown(info); + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_hangup (%d): setting count to 0\n", current->pid); +#endif + info->tty = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open done\n");/**/ + +static void +cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */ #endif - return 0; -} /* cy_open */ + if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} /* cy_flush_buffer */ /* @@ -2692,21 +3868,11 @@ cy_open(struct tty_struct *tty, struct file * filp) * --------------------------------------------------------------------- */ -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static inline void -show_version(void) -{ - printk("Cyclom driver %s\n",rcsid); -} /* show_version */ -/* initialize chips on card -- return number of valid +/* initialize chips on Cyclom-Y card -- return number of valid chips (which is number of ports/4) */ __initfunc(static int -cy_init_card(unsigned char *true_base_addr,int index)) +cyy_init_card(unsigned char *true_base_addr,int index)) { unsigned int chip_number; unsigned char* base_addr; @@ -2716,7 +3882,8 @@ cy_init_card(unsigned char *true_base_addr,int index)) udelay(500L); for(chip_number=0; chip_number= 131328 + cy_isa_address = vremap((unsigned int)cy_isa_address,0x2000); +#endif + cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0); + if (cy_isa_nchan == 0) { + continue; + } + + /* find out the board's irq by probing */ + cy_isa_irq = do_auto_irq(cy_isa_address); + if (cy_isa_irq == 0) { + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but the IRQ could not be detected.\n"); + continue; + } + + if((cy_next_channel+cy_isa_nchan) > NR_PORTS) { + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but no more channels are available.\n"); + return(nboard); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but no more cards can be used .\n"); + return(nboard); + } + + /* allocate IRQ */ + if(request_irq(cy_isa_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but could not allocate IRQ#%d.\n", + cy_isa_irq); + return(nboard); + } + + /* set cy_card */ + cy_card[j].base_addr = (int) cy_isa_address; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_isa_irq; + cy_card[j].bus_index = 0; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_isa_nchan/4; + IRQ_cards[cy_isa_irq] = &cy_card[j]; + nboard++; + + /* print message */ + printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, ", + j+1, (unsigned int) cy_isa_address, + (unsigned int)(cy_isa_address + 0x1fff), + cy_isa_irq); + printk("%d channels starting from port %d.\n", + cy_isa_nchan, cy_next_channel); + cy_next_channel += cy_isa_nchan; + } + return(nboard); + +} /* cy_detect_isa */ + +/* + * --------------------------------------------------------------------- + * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. + * sets global variables and return the number of PCI boards found. + * --------------------------------------------------------------------- + */ +__initfunc(static int +cy_detect_pci(void)) +{ +#ifdef CONFIG_PCI + unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; + unsigned long pci_intr_ctrl; + unsigned char cy_pci_irq; + unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; + unsigned short i,j,cy_pci_nchan; + unsigned short device_id,dev_index = 0,board_index = 0; + + if(pcibios_present() == 0) { /* PCI bus not present */ + return(0); + } + for (i = 0; i < NR_CARDS; i++) { + /* look for a Cyclades card by vendor and device id */ + while((device_id = cy_pci_dev_id[dev_index]) != 0) { + if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, + device_id,board_index, + &cyy_bus, &cyy_dev_fn) != 0) + { + dev_index++; /* try next device id */ + board_index = 0; + } else { + board_index++; + break; /* found a board */ + } + } + + /* read PCI configuration area */ + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_INTERRUPT_LINE, &cy_pci_irq); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_0, &cy_pci_addr0); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_1, &cy_pci_addr1); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_2, &cy_pci_addr2); + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_REVISION_ID, &cyy_rev_id); + if (device_id == 0){ + break; + }else if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) + || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Y/PCI:found winaddr=0x%x ioaddr=0x%x\n", + cy_pci_addr2, cy_pci_addr1); +#endif + cy_pci_addr1 &= 0xfffffffc; + cy_pci_addr2 &= 0xfffffff0; + +#if LINUX_VERSION_CODE < 131328 + if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ +#endif + cy_pci_addr2 = + (unsigned int) vremap(cy_pci_addr2,CyPCI_Ywin); + +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n", + cy_pci_addr2, cy_pci_addr1); +#endif + cy_pci_nchan = 4 * cyy_init_card((unsigned char *) + cy_pci_addr2,1); + if(cy_pci_nchan == 0) { + printk("Cyclom-Y PCI host card with "); + printk("no Serial-Modules at 0x%x.\n", + (unsigned int) cy_pci_addr2); + continue; + } + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclom-Y/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + return(i); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + return(i); + } + + /* allocate IRQ */ + if(request_irq(cy_pci_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but could not allocate IRQ%d.\n", + cy_pci_irq); + return(i); + } + + /* set cy_card */ + cy_card[j].base_addr = (int) cy_pci_addr2; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_pci_nchan/4; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* enable interrupts in the PCI interface */ + outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68); + pci_intr_ctrl = (unsigned long) + (inw(cy_pci_addr1+0x68) + | inw(cy_pci_addr1+0x6a)<<16); + + /* print message */ + printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, ", + j+1, cy_pci_addr2, (cy_pci_addr2 + CyPCI_Ywin - 1), + (int)cy_pci_irq); + printk("%d channels starting from port %d.\n", + cy_pci_nchan, cy_next_channel); + + cy_next_channel += cy_pci_nchan; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ + /* print message */ + printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); + printk("Cyclom-Z/PCI not supported for low addresses\n"); + break; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ +#ifdef CY_PCI_DEBUG + printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); +#endif + cy_pci_addr2 &= 0xfffffff0; + cy_pci_addr2 = (unsigned int) vremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zwin)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + cy_pci_addr0 &= 0xfffffff0; + cy_pci_addr0 = (unsigned int) vremap( + cy_pci_addr0 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zctl)) + + (cy_pci_addr0 & (PAGE_SIZE-1)); +#ifdef CY_PCI_DEBUG + printk("Cyclom-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); + ((struct RUNTIME_9060 *)(cy_pci_addr0)) + ->loc_addr_base = WIN_CREG; + PAUSE + printk("Cyclom-Z/PCI: FPGA id %lx, ver %lx\n", + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); + ((struct RUNTIME_9060 *)(cy_pci_addr0)) + ->loc_addr_base = WIN_RAM; +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; + + /* This must be a Cyclom-8Zo/PCI. The extendable + version will have a different device_id and will + be allocated its maximum number of ports. */ + cy_pci_nchan = 8; + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + (unsigned int) cy_pci_addr2); + printk("for Cyclom-Z/PCI at 0x%x.\n", + cy_pci_irq); + return(i); + } + } + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = 1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Zwin - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Zwin - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + } + return(i); +#else + return(0); +#endif /* ifdef CONFIG_PCI */ +} /* cy_detect_pci */ + + +/* + * This routine prints out the appropriate serial driver version number + * and identifies which options were configured into this driver. + */ +static inline void +show_version(void) +{ + char *rcsvers, *rcsdate, *tmp; + rcsvers = strchr(rcsid, ' '); rcsvers++; + tmp = strchr(rcsvers, ' '); *tmp++ = '\0'; + rcsdate = strchr(tmp, ' '); rcsdate++; + tmp = strrchr(rcsdate, ' '); *tmp = '\0'; + printk("Cyclom driver %s %s\n", + rcsvers, rcsdate); + printk("\tbuilt %s %s\n", + __DATE__, __TIME__); +} /* show_version */ + /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -2783,15 +4307,18 @@ cy_init_card(unsigned char *true_base_addr,int index)) device driver because the Cyclom is more properly a multiplexer, not just an aggregation of serial ports on one card. - If there are more cards with more ports than have been statically - allocated above, a warning is printed and the extra ports are ignored. + If there are more cards with more ports than have been + statically allocated above, a warning is printed and the + extra ports are ignored. */ + __initfunc(int cy_init(void)) { - struct cyclades_port *info; + struct cyclades_port *info; struct cyclades_card *cinfo; - int board,port,i; + int number_z_boards = 0; + int board,port,i; show_version(); @@ -2807,7 +4334,7 @@ cy_init(void)) cy_serial_driver.subtype = SERIAL_TYPE_NORMAL; cy_serial_driver.init_termios = tty_std_termios; cy_serial_driver.init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; + B9600 | CS8 | CREAD | HUPCL | CLOCAL; cy_serial_driver.flags = TTY_DRIVER_REAL_RAW; cy_serial_driver.refcount = &serial_refcount; cy_serial_driver.table = serial_table; @@ -2839,27 +4366,27 @@ cy_init(void)) cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; if (tty_register_driver(&cy_serial_driver)) - panic("Couldn't register Cyclom serial driver\n"); + panic("Couldn't register Cyclom serial driver\n"); if (tty_register_driver(&cy_callout_driver)) - panic("Couldn't register Cyclom callout driver\n"); + panic("Couldn't register Cyclom callout driver\n"); init_bh(CYCLADES_BH, do_cyclades_bh); for (i = 0; i < 16; i++) { - IRQ_cards[i] = 0; + IRQ_cards[i] = 0; } for (i = 0; i < NR_CARDS; i++) { - /* base_addr=0 indicates board not found */ - cy_card[i].base_addr = 0; + /* base_addr=0 indicates board not found */ + cy_card[i].base_addr = 0; } /* the code below is responsible to find the boards. Each different type of board has its own detection routine. If a board is found, the next cy_card structure available is set by the detection - routine. These functions are responsible for checking the availability - of cy_card and cy_port data structures and updating the - cy_next_channel. */ + routine. These functions are responsible for checking the + availability of cy_card and cy_port data structures and updating + the cy_next_channel. */ /* look for isa boards */ cy_isa_nboard = cy_detect_isa(); @@ -2871,289 +4398,195 @@ cy_init(void)) /* invalidate remaining cy_card structures */ for (i = 0 ; i < NR_CARDS ; i++) { - if (cy_card[i].base_addr == 0) { - cy_card[i].first_line = -1; - } + if (cy_card[i].base_addr == 0) { + cy_card[i].first_line = -1; + cy_card[i].ctl_addr = 0; + cy_card[i].irq = 0; + cy_card[i].bus_index = 0; + cy_card[i].first_line = 0; + cy_card[i].num_chips = 0; + } } /* invalidate remaining cy_port structures */ for (i = cy_next_channel ; i < NR_PORTS ; i++) { - cy_port[i].line = -1; - cy_port[i].magic = -1; + cy_port[i].line = -1; + cy_port[i].magic = -1; } /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { - cinfo = &cy_card[board]; - for (port = cinfo->first_line ; - port < cinfo->first_line + 4*cinfo->num_chips ; - port++) - { - info = &cy_port[port]; - info->magic = CYCLADES_MAGIC; - info->type = PORT_CIRRUS; - info->card = board; - info->line = port; - info->flags = STD_COM_FLAGS; - info->tty = 0; - info->xmit_fifo_size = 12; - info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; - info->cor2 = CyETC; - info->cor3 = 0x08; /* _very_ small receive threshold */ - info->cor4 = 0; - info->cor5 = 0; - info->tbpr = baud_bpr[13]; /* Tx BPR */ - info->tco = baud_co[13]; /* Tx CO */ - info->rbpr = baud_bpr[13]; /* Rx BPR */ - info->rco = baud_co[13]; /* Rx CO */ - info->close_delay = 0; - info->x_char = 0; - info->event = 0; - info->count = 0; + cinfo = &cy_card[board]; + if (cinfo->num_chips == 1){ /* Cyclom-8Zo/PCI */ + number_z_boards++; + for (port = cinfo->first_line ; + port < cinfo->first_line + 8; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_STARTECH; + info->card = board; + info->line = port; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = 0; + info->cor1 = 0; + info->cor2 = 0; + info->cor3 = 0; + info->cor4 = 0; + info->cor5 = 0; + info->tbpr = 0; + info->tco = 0; + info->rbpr = 0; + info->rco = 0; + info->close_delay = 0; + info->x_char = 0; + info->event = 0; + info->count = 0; #ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->blocked_open = 0; - info->default_threshold = 0; - info->default_timeout = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->callout_termios =cy_callout_driver.init_termios; - info->normal_termios = cy_serial_driver.init_termios; - info->open_wait = 0; - info->close_wait = 0; - /* info->session */ - /* info->pgrp */ - info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK - | CyPARITY| CyFRAME| CyOVERRUN; - /* info->timeout */ - } + printk("cyc:cy_init(1) setting Z count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = 0; + /* info->timeout */ + /* Bentson's vars */ + info->jiffies[0] = 0; + info->jiffies[1] = 0; + info->jiffies[2] = 0; + info->rflush_count = 0; + } + continue; + }else{ /* Cyclom-Y of some kind*/ + for (port = cinfo->first_line ; + port < cinfo->first_line + 4*cinfo->num_chips ; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_CIRRUS; + info->card = board; + info->line = port; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = 12; + info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = 0x08; /* _very_ small rcv threshold */ + info->cor4 = 0; + info->cor5 = 0; + info->tbpr = baud_bpr[13]; /* Tx BPR */ + info->tco = baud_co[13]; /* Tx CO */ + info->rbpr = baud_bpr[13]; /* Rx BPR */ + info->rco = baud_co[13]; /* Rx CO */ + info->close_delay = 0; + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_init(2) setting Y count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = + CyTIMEOUT| CySPECHAR| CyBREAK + | CyPARITY| CyFRAME| CyOVERRUN; + /* info->timeout */ + } + } + } + + if ( number_z_boards && !cyz_timeron){ + cyz_timeron++; + cyz_timerlist.expires = jiffies + 1; + add_timer(&cyz_timerlist); +#ifdef CY_PCI_DEBUG + printk("Cyclom-Z polling initialized\n"); +#endif } + return 0; } /* cy_init */ #ifdef MODULE +/* See linux/drivers/char/riscom.c for ideas on how to + pass additional base addresses to the driver!!! */ int init_module(void) { return(cy_init()); -} +} /* init_module */ void cleanup_module(void) { - unsigned long flags; int i; + unsigned long flags; + + + if (cyz_timeron){ + cyz_timeron = 0; + del_timer(&cyz_timerlist); + } save_flags(flags); cli(); - remove_bh(CYCLADES_BH); + remove_bh(CYCLADES_BH); if (tty_unregister_driver(&cy_callout_driver)) - printk("Couldn't unregister Cyclom callout driver\n"); + printk("Couldn't unregister Cyclom callout driver\n"); if (tty_unregister_driver(&cy_serial_driver)) - printk("Couldn't unregister Cyclom serial driver\n"); + printk("Couldn't unregister Cyclom serial driver\n"); restore_flags(flags); for (i = 0; i < NR_CARDS; i++) { - if (cy_card[i].base_addr != 0) - { - free_irq(cy_card[i].irq,NULL); - } + if (cy_card[i].base_addr != 0 + && cy_card[i].irq) + { + free_irq(cy_card[i].irq,NULL); + } } -} -#endif - -/* - * --------------------------------------------------------------------- - * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. - * sets global variables and return the number of ISA boards found. - * --------------------------------------------------------------------- - */ -__initfunc(int -cy_detect_isa()) -{ - unsigned int cy_isa_irq,nboard; - unsigned char *cy_isa_address; - unsigned short i,j,cy_isa_nchan; - - nboard = 0; - - /* scan the address table probing for Cyclom-Y/ISA boards */ - for (i = 0 ; i < NR_ISA_ADDRESSES ; i++) { - cy_isa_address = cy_isa_addresses[i]; - if (cy_isa_address == 0x0000) { - return(nboard); - } - - /* probe for CD1400... */ - cy_isa_nchan = 4 * cy_init_card(cy_isa_address,0); - if (cy_isa_nchan == 0) { - continue; - } - - /* find out the board's irq by probing */ - cy_isa_irq = do_auto_irq(cy_isa_address); - if (cy_isa_irq == 0) { - printk("Cyclom-Y/ISA found at 0x%x but the IRQ could not be detected.\n", - (unsigned int) cy_isa_address); - continue; - } - - if((cy_next_channel+cy_isa_nchan) > NR_PORTS) { - printk("Cyclom-Y/ISA found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/ISA found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - - /* allocate IRQ */ - if(request_irq(cy_isa_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/ISA found at 0x%x but could not allocate interrupt IRQ#%d.\n", - (unsigned int) cy_isa_address,cy_isa_irq); - return(nboard); - } - - /* set cy_card */ - cy_card[j].base_addr = (int) cy_isa_address; - cy_card[j].irq = (int) cy_isa_irq; - cy_card[j].bus_index = 0; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_isa_nchan/4; - IRQ_cards[cy_isa_irq] = &cy_card[j]; - nboard++; - - /* print message */ - printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_isa_address, - (unsigned int)(cy_isa_address + 0x1fff), - cy_isa_irq,cy_isa_nchan,cy_next_channel); - cy_next_channel += cy_isa_nchan; - } - return(nboard); - -} - -/* - * --------------------------------------------------------------------- - * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. - * sets global variables and return the number of PCI boards found. - * --------------------------------------------------------------------- - */ -__initfunc(int -cy_detect_pci()) +} /* cleanup_module */ +#else +/* called by linux/init/main.c to parse command line options */ +void +cy_setup(char *str, int *ints) { -#ifdef CONFIG_PCI - unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; - unsigned long pci_intr_ctrl; - unsigned char cy_pci_irq; - unsigned int cy_pci_address, cy_pci_io; - unsigned short i,j,cy_pci_nchan; - unsigned short device_id,dev_index = 0,board_index = 0; - - if(pcibios_present() == 0) { /* PCI bus not present */ - return(0); - } - for (i = 0; i < NR_CARDS; i++) { - /* look for a Cyclom-Y card by vendor and device id */ - while((device_id = cy_pci_dev_id[dev_index]) != 0) { - if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, - device_id,board_index, - &cyy_bus, &cyy_dev_fn) != 0) - { - dev_index++; /* try next device id */ - board_index = 0; - } else { - board_index++; - break; /* found a board */ - } - } - if (device_id == 0) break; - - /* read PCI configuration area */ - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_INTERRUPT_LINE, &cy_pci_irq); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_1, &cy_pci_io); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_2, &cy_pci_address); - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_REVISION_ID, &cyy_rev_id); - cy_pci_address &= 0xfffffff0; - if ((ulong)cy_pci_address >= 0x100000) { /* above 1M? */ - cy_pci_address = - (unsigned int) ioremap(cy_pci_address,0x4000); - } - cy_pci_io &= 0xfffffffc; - cy_pci_nchan = 4 * cy_init_card((unsigned char *) - cy_pci_address,1); - if(cy_pci_nchan == 0) { - printk("Cyclom-Y PCI host card with no Serial-Modules at 0x%x.\n", - (unsigned int) cy_pci_address); - continue; - } - if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { - printk("Cyclom-Y/PCI found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } -#ifdef CY_PCI_DEBUG - printk("Cyclom-Ye/PCI #%d (bus=0x0%x, pci_id=0x%x, rev_id=%d).\n", - i+1,cyy_bus,cyy_dev_fn,cyy_rev_id); - printk("Cyclom-Ye/PCI: found at 0x%x, IRQ%d, ioaddr = 0x%lx.\n", - cy_pci_address,(int)cy_pci_irq,cy_pci_io); -#endif - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/PCI found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } + int i, j; - /* allocate IRQ */ - if(request_irq(cy_pci_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/PCI found at 0x%x but could not allocate interrupt IRQ%d.\n", - (unsigned int) cy_pci_address,cy_pci_irq); - return(i); - } + for (i = 0 ; i < NR_ISA_ADDRS ; i++) { + if (cy_isa_addresses[i] == 0) break; + } + for (j = 1; j <= ints[0]; j++){ + if ( i < NR_ISA_ADDRS ){ + cy_isa_addresses[i++] = (unsigned char *)(ints[j]); + } + } - /* set cy_card */ - cy_card[j].base_addr = (int) cy_pci_address; - cy_card[j].irq = (int) cy_pci_irq; - cy_card[j].bus_index = 1; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_pci_nchan/4; - IRQ_cards[cy_pci_irq] = &cy_card[j]; - - /* enable interrupts in the PCI interface */ - outw(inw(cy_pci_io+0x68)|0x0900,cy_pci_io+0x68); - pci_intr_ctrl = (unsigned long)(inw(cy_pci_io+0x68) | inw(cy_pci_io+0x6a)<<16); - - /* print message */ - printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_pci_address, - (unsigned int)(cy_pci_address + 0x3fff), - (int)cy_pci_irq,cy_pci_nchan,cy_next_channel); - - cy_next_channel += cy_pci_nchan; - } - return(i); -#else - return(0); -#endif /* ifdef CONFIG_PCI */ -} +} /* cy_setup */ +#endif #ifdef CYCLOM_SHOW_STATUS @@ -3181,7 +4614,8 @@ show_status(int line_num) printk(" cy_port\n"); printk(" card line flags = %d %d %x\n", info->card, info->line, info->flags); - printk(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", + printk(" *tty read_status_mask timeout xmit_fifo_size ", + printk("= %lx %x %x %x\n", (long)info->tty, info->read_status_mask, info->timeout, info->xmit_fifo_size); printk(" cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n", @@ -3198,59 +4632,60 @@ show_status(int line_num) save_flags(flags); cli(); - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]< +/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */ +#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) + +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50) +#define VERSION_ELF_1_2_13 +#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95) +#define VERSION_1_3_85 +#else +#define VERSION_2_0_0 +#endif + +/* + * AMD 53C974 driver, header file + */ + +#ifndef DC390_H +#define DC390_H + +#if defined(HOSTS_C) || defined(MODULE) + +#ifdef VERSION_2_0_0 +#include +#else +#include +#endif + +extern int DC390_detect(Scsi_Host_Template *psht); +extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); +extern int DC390_abort(Scsi_Cmnd *cmd); + +#ifdef VERSION_2_0_0 +extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags); +#else +extern int DC390_reset(Scsi_Cmnd *cmd); +#endif + +#ifdef VERSION_ELF_1_2_13 +extern int DC390_bios_param(Disk *disk, int devno, int geom[]); +#else +extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]); +#endif + +#ifdef MODULE +static int DC390_release(struct Scsi_Host *); +#else +#define DC390_release NULL +#endif + +#ifndef VERSION_ELF_1_2_13 +extern struct proc_dir_entry proc_scsi_tmscsim; +extern int tmscsim_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout); +#endif + +#ifdef VERSION_2_0_0 + +#define DC390_T { \ + NULL, /* *next */ \ + NULL, /* *usage_count */ \ + &proc_scsi_tmscsim, /* *proc_dir */ \ + tmscsim_proc_info, /* (*proc_info)() */ \ + "Tekram DC390(T) V1.10 Dec-05-1996", /* *name */ \ + DC390_detect, \ + DC390_release, /* (*release)() */ \ + NULL, /* *(*info)() */ \ + NULL, /* (*command)() */ \ + DC390_queue_command, \ + DC390_abort, \ + DC390_reset, \ + NULL, /* slave attach */\ + DC390_bios_param, \ + 10,/* can queue(-1) */ \ + 7, /* id(-1) */ \ + SG_ALL, \ + 2, /* cmd per lun(2) */ \ + 0, /* present */ \ + 0, /* unchecked isa dma */ \ + DISABLE_CLUSTERING \ + } +#endif + + +#ifdef VERSION_1_3_85 + +#define DC390_T { \ + NULL, /* *next */ \ + NULL, /* *usage_count */ \ + &proc_scsi_tmscsim, /* *proc_dir */ \ + tmscsim_proc_info, /* (*proc_info)() */ \ + "Tekram DC390(T) V1.10 Dec-05-1996", /* *name */ \ + DC390_detect, \ + DC390_release, /* (*release)() */ \ + NULL, /* *(*info)() */ \ + NULL, /* (*command)() */ \ + DC390_queue_command, \ + DC390_abort, \ + DC390_reset, \ + NULL, /* slave attach */\ + DC390_bios_param, \ + 10,/* can queue(-1) */ \ + 7, /* id(-1) */ \ + SG_ALL, \ + 2, /* cmd per lun(2) */ \ + 0, /* present */ \ + 0, /* unchecked isa dma */ \ + DISABLE_CLUSTERING \ + } +#endif + + +#ifdef VERSION_ELF_1_2_13 + +#define DC390_T { \ + NULL, \ + NULL, \ + "Tekram DC390(T) V1.10 Dec-05-1996",\ + DC390_detect, \ + DC390_release, \ + NULL, /* info */ \ + NULL, /* command, deprecated */ \ + DC390_queue_command, \ + DC390_abort, \ + DC390_reset, \ + NULL, /* slave attach */\ + DC390_bios_param, \ + 10,/* can queue(-1) */ \ + 7, /* id(-1) */ \ + 16,/* old (SG_ALL) */ \ + 2, /* cmd per lun(2) */ \ + 0, /* present */ \ + 0, /* unchecked isa dma */ \ + DISABLE_CLUSTERING \ + } +#endif + +#endif /* defined(HOSTS_C) || defined(MODULE) */ + +#endif /* DC390_H */ diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 0c1e2df31f19..289e96d31cbb 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -158,6 +158,10 @@ #include "NCR53c406a.h" #endif +#ifdef CONFIG_SCSI_DC390T +#include "dc390.h" +#endif + #ifdef CONFIG_SCSI_AM53C974 #include "AM53C974.h" #endif @@ -310,6 +314,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #ifdef CONFIG_SCSI_EATA EATA, #endif +#ifdef CONFIG_SCSI_DC390T + DC390_T, +#endif #ifdef CONFIG_SCSI_AM53C974 AM53C974, #endif diff --git a/drivers/scsi/scsiiom.c b/drivers/scsi/scsiiom.c new file mode 100644 index 000000000000..97801d755374 --- /dev/null +++ b/drivers/scsi/scsiiom.c @@ -0,0 +1,1540 @@ +/*********************************************************************** + * FILE NAME : SCSIIOM.C * + * BY : C.L. Huang, ching@tekram.com.tw * + * Description: Device Driver for Tekram DC-390 (T) PCI SCSI * + * Bus Master Host Adapter * + ***********************************************************************/ + + +static USHORT +DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ) +{ + USHORT ioport, rc; + UCHAR bval, bval1, i, cnt; + PUCHAR ptr; + ULONG wlval; + + pSRB->TagNumber = 31; + ioport = pACB->IOPortBase; + bval = pDCB->UnitSCSIID; + outb(bval,ioport+Scsi_Dest_ID); + bval = pDCB->SyncPeriod; + outb(bval,ioport+Sync_Period); + bval = pDCB->SyncOffset; + outb(bval,ioport+Sync_Offset); + bval = pDCB->CtrlR1; + outb(bval,ioport+CtrlReg1); + bval = pDCB->CtrlR3; + outb(bval,ioport+CtrlReg3); + bval = pDCB->CtrlR4; + outb(bval,ioport+CtrlReg4); + bval = CLEAR_FIFO_CMD; /* Flush FIFO */ + outb(bval,ioport+ScsiCmd); + + pSRB->ScsiPhase = SCSI_NOP0; + bval = pDCB->IdentifyMsg; + if( !(pDCB->SyncMode & EN_ATN_STOP) ) + { + if( (pSRB->CmdBlock[0] == INQUIRY) || + (pSRB->CmdBlock[0] == REQUEST_SENSE) || + (pSRB->SRBFlag & AUTO_REQSENSE) ) + { + bval &= 0xBF; /* NO disconnection */ + outb(bval,ioport+ScsiFifo); + bval1 = SELECT_W_ATN; + pSRB->SRBState = SRB_START_; + if( pDCB->SyncMode & SYNC_ENABLE ) + { + if( !(pDCB->IdentifyMsg & 7) || + (pSRB->CmdBlock[0] != INQUIRY) ) + { + bval1 = SEL_W_ATN_STOP; + pSRB->SRBState = SRB_MSGOUT; + } + } + } + else + { + if(pDCB->SyncMode & EN_TAG_QUEUING) + { + outb(bval,ioport+ScsiFifo); + bval = MSG_SIMPLE_QTAG; + outb(bval,ioport+ScsiFifo); + wlval = 1; + bval = 0; + while( wlval & pDCB->TagMask ) + { + wlval = wlval << 1; + bval++; + } + outb(bval,ioport+ScsiFifo); + pDCB->TagMask |= wlval; + pSRB->TagNumber = bval; + bval1 = SEL_W_ATN2; + pSRB->SRBState = SRB_START_; + } + else + { + outb(bval,ioport+ScsiFifo); + bval1 = SELECT_W_ATN; + pSRB->SRBState = SRB_START_; + } + } + + if( pSRB->SRBFlag & AUTO_REQSENSE ) + { + bval = REQUEST_SENSE; + outb(bval,ioport+ScsiFifo); + bval = pDCB->IdentifyMsg << 5; + outb(bval,ioport+ScsiFifo); + bval = 0; + outb(bval,ioport+ScsiFifo); + outb(bval,ioport+ScsiFifo); + bval = sizeof(pSRB->pcmd->sense_buffer); + outb(bval,ioport+ScsiFifo); + bval = 0; + outb(bval,ioport+ScsiFifo); + } + else + { + cnt = pSRB->ScsiCmdLen; + ptr = (PUCHAR) pSRB->CmdBlock; + for(i=0; iCmdBlock[0] == INQUIRY) || + (pSRB->CmdBlock[0] == REQUEST_SENSE) || + (pSRB->SRBFlag & AUTO_REQSENSE) ) + { + bval &= 0xBF; + outb(bval,ioport+ScsiFifo); + bval1 = SELECT_W_ATN; + pSRB->SRBState = SRB_START_; + if( pDCB->SyncMode & SYNC_ENABLE ) + { + if( !(pDCB->IdentifyMsg & 7) || + (pSRB->CmdBlock[0] != INQUIRY) ) + { + bval1 = SEL_W_ATN_STOP; + pSRB->SRBState = SRB_MSGOUT; + } + } + } + else + { + if(pDCB->SyncMode & EN_TAG_QUEUING) + { + outb(bval,ioport+ScsiFifo); + pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG; + wlval = 1; + bval = 0; + while( wlval & pDCB->TagMask ) + { + wlval = wlval << 1; + bval++; + } + pDCB->TagMask |= wlval; + pSRB->TagNumber = bval; + pSRB->MsgOutBuf[1] = bval; + pSRB->MsgCnt = 2; + bval1 = SEL_W_ATN_STOP; + pSRB->SRBState = SRB_START_; + } + else + { + outb(bval,ioport+ScsiFifo); + pSRB->MsgOutBuf[0] = MSG_NOP; + pSRB->MsgCnt = 1; + pSRB->SRBState = SRB_START_; + bval1 = SEL_W_ATN_STOP; + } + } + } + bval = inb( ioport+Scsi_Status ); + if( bval & INTERRUPT ) + { + pSRB->SRBState = SRB_READY; + pDCB->TagMask &= ~( 1 << pSRB->TagNumber ); + rc = 1; + } + else + { + pSRB->ScsiPhase = SCSI_NOP1; + pACB->pActiveDCB = pDCB; + pDCB->pActiveSRB = pSRB; + rc = 0; + outb(bval1,ioport+ScsiCmd); + } + return( rc ); +} + + +#ifndef VERSION_ELF_1_2_13 +static void +DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs) +#else +static void +DC390_Interrupt( int irq, struct pt_regs *regs) +#endif +{ + PACB pACB; + PDCB pDCB; + PSRB pSRB; + USHORT ioport = 0; + USHORT phase, i; + void (*stateV)( PACB, PSRB, PUCHAR ); + UCHAR istate = 0; + UCHAR sstatus=0, istatus; + + pACB = pACB_start; + if( pACB == NULL ) + return; + for( i=0; i < adapterCnt; i++ ) + { + if( pACB->IRQLevel == (UCHAR) irq ) + { + ioport = pACB->IOPortBase; + sstatus = inb( ioport+Scsi_Status ); + if( sstatus & INTERRUPT ) + break; + else + pACB = pACB->pNextACB; + } + else + { + pACB = pACB->pNextACB; + } + } + +#ifdef DC390_DEBUG1 + printk("sstatus=%2x,",sstatus); +#endif + + if( pACB == (PACB )-1 ) + { + printk("DC390: Spurious interrupt detected!\n"); + return; + } + + istate = inb( ioport+Intern_State ); + istatus = inb( ioport+INT_Status ); + +#ifdef DC390_DEBUG1 + printk("Istatus=%2x,",istatus); +#endif + + if(istatus & DISCONNECTED) + { + DC390_Disconnect( pACB ); + return; + } + + if(istatus & RESELECTED) + { + DC390_Reselect( pACB ); + return; + } + + if(istatus & INVALID_CMD) + { + DC390_InvalidCmd( pACB ); + return; + } + + if(istatus & SCSI_RESET) + { + DC390_ScsiRstDetect( pACB ); + return; + } + + if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) ) + { + pDCB = pACB->pActiveDCB; + pSRB = pDCB->pActiveSRB; + if( pDCB ) + { + if( pDCB->DCBFlag & ABORT_DEV_ ) + EnableMsgOut( pACB, pSRB ); + } + + phase = (USHORT) pSRB->ScsiPhase; + stateV = (void *) DC390_phase0[phase]; + stateV( pACB, pSRB, &sstatus ); + + pSRB->ScsiPhase = sstatus & 7; + phase = (USHORT) sstatus & 7; + stateV = (void *) DC390_phase1[phase]; + stateV( pACB, pSRB, &sstatus ); + } +} + + +static void +DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR sstatus, bval; + USHORT ioport; + PSGL psgl; + ULONG ResidCnt, xferCnt; + + ioport = pACB->IOPortBase; + sstatus = *psstatus; + + if( !(pSRB->SRBState & SRB_XFERPAD) ) + { + if( sstatus & PARITY_ERR ) + pSRB->SRBStatus |= PARITY_ERROR; + + if( sstatus & COUNT_2_ZERO ) + { + bval = inb(ioport+DMA_Status); + while( !(bval & DMA_XFER_DONE) ) + bval = inb(ioport+DMA_Status); + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if( pSRB->SGIndex < pSRB->SGcount ) + { + pSRB->pSegmentList++; + psgl = pSRB->pSegmentList; + +#ifndef VERSION_ELF_1_2_13 + pSRB->SGPhysAddr = virt_to_phys( psgl->address ); +#else + pSRB->SGPhysAddr = (ULONG) psgl->address; +#endif + pSRB->SGToBeXferLen = (ULONG) psgl->length; + } + else + pSRB->SGToBeXferLen = 0; + } + else + { + bval = inb( ioport+Current_Fifo ); + bval &= 0x1f; + ResidCnt = (ULONG) inb(ioport+CtcReg_High); + ResidCnt = ResidCnt << 8; + ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid); + ResidCnt = ResidCnt << 8; + ResidCnt |= (ULONG) inb(ioport+CtcReg_Low); + ResidCnt += (ULONG) bval; + + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + } + } + bval = WRITE_DIRECTION+DMA_IDLE_CMD; + outb( bval, ioport+DMA_Cmd); +} + +static void +DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR sstatus, bval; + USHORT i, ioport, residual; + PSGL psgl; + ULONG ResidCnt, xferCnt; + PUCHAR ptr; + + + ioport = pACB->IOPortBase; + sstatus = *psstatus; + + if( !(pSRB->SRBState & SRB_XFERPAD) ) + { + if( sstatus & PARITY_ERR ) + pSRB->SRBStatus |= PARITY_ERROR; + + if( sstatus & COUNT_2_ZERO ) + { + bval = inb(ioport+DMA_Status); + while( !(bval & DMA_XFER_DONE) ) + bval = inb(ioport+DMA_Status); + + bval = READ_DIRECTION+DMA_IDLE_CMD; + outb( bval, ioport+DMA_Cmd); + + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if( pSRB->SGIndex < pSRB->SGcount ) + { + pSRB->pSegmentList++; + psgl = pSRB->pSegmentList; + +#ifndef VERSION_ELF_1_2_13 + pSRB->SGPhysAddr = virt_to_phys( psgl->address ); +#else + pSRB->SGPhysAddr = (ULONG) psgl->address; +#endif + pSRB->SGToBeXferLen = (ULONG) psgl->length; + } + else + pSRB->SGToBeXferLen = 0; + } + else /* phase changed */ + { + residual = 0; + bval = inb(ioport+Current_Fifo); + while( bval & 0x1f ) + { + if( (bval & 0x1f) == 1 ) + { + for(i=0; i< 0x100; i++) + { + bval = inb(ioport+Current_Fifo); + if( !(bval & 0x1f) ) + goto din_1; + else if( i == 0x0ff ) + { + residual = 1; /* ;1 residual byte */ + goto din_1; + } + } + } + else + bval = inb(ioport+Current_Fifo); + } +din_1: + bval = READ_DIRECTION+DMA_BLAST_CMD; + outb(bval, ioport+DMA_Cmd); + for(i=0; i<0x8000; i++) + { + bval = inb(ioport+DMA_Status); + if(bval & BLAST_COMPLETE) + break; + } + bval = READ_DIRECTION+DMA_IDLE_CMD; + outb(bval, ioport+DMA_Cmd); + + ResidCnt = (ULONG) inb(ioport+CtcReg_High); + ResidCnt = ResidCnt << 8; + ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid); + ResidCnt = ResidCnt << 8; + ResidCnt |= (ULONG) inb(ioport+CtcReg_Low); + + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + + if( residual ) + { + bval = inb(ioport+ScsiFifo); /* get residual byte */ +#ifndef VERSION_ELF_1_2_13 + ptr = (PUCHAR) phys_to_virt( pSRB->SGPhysAddr ); +#else + ptr = (PUCHAR) pSRB->SGPhysAddr; +#endif + *ptr = bval; + pSRB->SGPhysAddr++; + pSRB->TotalXferredLen++; + pSRB->SGToBeXferLen--; + } + } + } +} + +static void +DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ +} + +static void +DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR bval; + USHORT ioport; + + ioport = pACB->IOPortBase; + bval = inb(ioport+ScsiFifo); + pSRB->TargetStatus = bval; + bval++; + bval = inb(ioport+ScsiFifo); /* get message */ + pSRB->EndMessage = bval; + + *psstatus = SCSI_NOP0; + pSRB->SRBState = SRB_COMPLETED; + bval = MSG_ACCEPTED_CMD; + outb(bval, ioport+ScsiCmd); +} + +static void +DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) ) + *psstatus = SCSI_NOP0; +} + +static void +DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR bval; + USHORT ioport, wval, wval1; + PDCB pDCB; + PSRB psrb; + + ioport = pACB->IOPortBase; + pDCB = pACB->pActiveDCB; + + bval = inb( ioport+ScsiFifo ); + if( !(pSRB->SRBState & SRB_MSGIN_MULTI) ) + { + if(bval == MSG_DISCONNECT) + { + pSRB->SRBState = SRB_DISCONNECT; + } + else if( bval == MSG_SAVE_PTR ) + goto min6; + else if( (bval == MSG_EXTENDED) || ((bval >= MSG_SIMPLE_QTAG) && + (bval <= MSG_ORDER_QTAG)) ) + { + pSRB->SRBState |= SRB_MSGIN_MULTI; + pSRB->MsgInBuf[0] = bval; + pSRB->MsgCnt = 1; + pSRB->pMsgPtr = &pSRB->MsgInBuf[1]; + } + else if(bval == MSG_REJECT_) + { + bval = RESET_ATN_CMD; + outb(bval, ioport+ScsiCmd); + if( pSRB->SRBState & DO_SYNC_NEGO) + goto set_async; + } + else if( bval == MSG_RESTORE_PTR) + goto min6; + else + goto min6; + } + else + { /* minx: */ + + *pSRB->pMsgPtr = bval; + pSRB->MsgCnt++; + pSRB->pMsgPtr++; + if( (pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG) && + (pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG) ) + { + if( pSRB->MsgCnt == 2) + { + pSRB->SRBState = 0; + bval = pSRB->MsgInBuf[1]; + pSRB = pDCB->pGoingSRB; + psrb = pDCB->pGoingLast; + if( pSRB ) + { + for( ;; ) + { + if(pSRB->TagNumber != bval) + { + if( pSRB == psrb ) + goto mingx0; + pSRB = pSRB->pNextSRB; + } + else + break; + } + if( pDCB->DCBFlag & ABORT_DEV_ ) + { + pSRB->SRBState = SRB_ABORT_SENT; + EnableMsgOut( pACB, pSRB ); + } + if( !(pSRB->SRBState & SRB_DISCONNECT) ) + goto mingx0; + pDCB->pActiveSRB = pSRB; + pSRB->SRBState = SRB_DATA_XFER; + } + else + { +mingx0: + pSRB = pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; + EnableMsgOut2( pACB, pSRB ); + } + } + } + else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) ) + { + pSRB->SRBState &= ~(SRB_MSGIN_MULTI+DO_SYNC_NEGO); + if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1) ) + { /* reject_msg: */ + pSRB->MsgCnt = 1; + pSRB->MsgInBuf[0] = MSG_REJECT_; + bval = SET_ATN_CMD; + outb(bval, ioport+ScsiCmd); + } + else if( !(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4]) ) + { +set_async: + pDCB = pSRB->pSRBDCB; + pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE); + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + pDCB->CtrlR3 = FAST_CLK; /* ;non_fast */ + pDCB->CtrlR4 &= 0x3f; + pDCB->CtrlR4 |= EATER_25NS; /* ; 25ns glitch eater */ + goto re_prog; + } + else + { /* set_sync: */ + + pDCB = pSRB->pSRBDCB; + pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE; + pDCB->SyncOffset &= 0x0f0; + pDCB->SyncOffset |= pSRB->MsgInBuf[4]; + pDCB->NegoPeriod = pSRB->MsgInBuf[3]; + wval = (USHORT) pSRB->MsgInBuf[3]; + wval = wval << 2; + wval--; + wval1 = wval / 25; + if( (wval1 * 25) != wval) + wval1++; + bval = FAST_CLK+FAST_SCSI; + pDCB->CtrlR4 &= 0x3f; + if(wval1 >= 8) + { + wval1--; + bval = FAST_CLK; /* ;fast clock/normal scsi */ + pDCB->CtrlR4 |= EATER_25NS; /* ;25 ns glitch eater */ + } + pDCB->CtrlR3 = bval; + pDCB->SyncPeriod = (UCHAR)wval1; +re_prog: + bval = pDCB->SyncPeriod; + outb(bval, ioport+Sync_Period); + bval = pDCB->SyncOffset; + outb(bval, ioport+Sync_Offset); + bval = pDCB->CtrlR3; + outb(bval, ioport+CtrlReg3); + bval = pDCB->CtrlR4; + outb(bval, ioport+CtrlReg4); + SetXferRate( pACB, pDCB); + } + } + } +min6: + *psstatus = SCSI_NOP0; + bval = MSG_ACCEPTED_CMD; + outb(bval, ioport+ScsiCmd); +} + +static void +DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir) +{ + PSGL psgl; + UCHAR bval; + USHORT ioport; + ULONG lval; + + + ioport = pACB->IOPortBase; + if( pSRB->SGIndex < pSRB->SGcount ) + { + bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */ + outb( bval, ioport+DMA_Cmd); + if( !pSRB->SGToBeXferLen ) + { + psgl = pSRB->pSegmentList; +#ifndef VERSION_ELF_1_2_13 + pSRB->SGPhysAddr = virt_to_phys( psgl->address ); +#else + pSRB->SGPhysAddr = (ULONG) psgl->address; +#endif + pSRB->SGToBeXferLen = (ULONG) psgl->length; + } + lval = pSRB->SGToBeXferLen; + bval = (UCHAR) lval; + outb(bval,ioport+CtcReg_Low); + lval = lval >> 8; + bval = (UCHAR) lval; + outb(bval,ioport+CtcReg_Mid); + lval = lval >> 8; + bval = (UCHAR) lval; + outb(bval,ioport+CtcReg_High); + + lval = pSRB->SGToBeXferLen; + outl(lval, ioport+DMA_XferCnt); + + lval = pSRB->SGPhysAddr; + outl( lval, ioport+DMA_XferAddr); + + bval = DMA_COMMAND+INFO_XFER_CMD; + outb(bval, ioport+ScsiCmd); + + pSRB->SRBState = SRB_DATA_XFER; + + bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */ + outb(bval, ioport+DMA_Cmd); + + bval = DMA_START_CMD | ioDir; /* ;+EN_DMA_INT */ + outb(bval, ioport+DMA_Cmd); + } + else /* xfer pad */ + { + if( pSRB->SGcount ) + { + pSRB->AdaptStatus = H_OVER_UNDER_RUN; + pSRB->SRBStatus |= OVER_RUN; + } + bval = 0; + outb(bval,ioport+CtcReg_Low); + outb(bval,ioport+CtcReg_Mid); + outb(bval,ioport+CtcReg_High); + + pSRB->SRBState |= SRB_XFERPAD; + bval = DMA_COMMAND+XFER_PAD_BYTE; + outb(bval, ioport+ScsiCmd); +/* + bval = DMA_IDLE_CMD | ioDir; ;+EN_DMA_INT + outb(bval, ioport+DMA_Cmd); + bval = DMA_START_CMD | ioDir; ;+EN_DMA_INT + outb(bval, ioport+DMA_Cmd); +*/ + } +} + + +static void +DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR ioDir; + + ioDir = WRITE_DIRECTION; + DataIO_Comm( pACB, pSRB, ioDir); +} + +static void +DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR ioDir; + + ioDir = READ_DIRECTION; + DataIO_Comm( pACB, pSRB, ioDir); +} + +static void +DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + PDCB pDCB; + UCHAR bval; + PUCHAR ptr; + USHORT ioport, i, cnt; + + + ioport = pACB->IOPortBase; + bval = RESET_ATN_CMD; + outb(bval, ioport+ScsiCmd); + bval = CLEAR_FIFO_CMD; + outb(bval, ioport+ScsiCmd); + if( !(pSRB->SRBFlag & AUTO_REQSENSE) ) + { + cnt = (USHORT) pSRB->ScsiCmdLen; + ptr = (PUCHAR) pSRB->CmdBlock; + for(i=0; i < cnt; i++) + { + outb(*ptr, ioport+ScsiFifo); + ptr++; + } + } + else + { + bval = REQUEST_SENSE; + outb(bval, ioport+ScsiFifo); + pDCB = pACB->pActiveDCB; + bval = pDCB->IdentifyMsg << 5; + outb(bval, ioport+ScsiFifo); + bval = 0; + outb(bval, ioport+ScsiFifo); + outb(bval, ioport+ScsiFifo); + bval = sizeof(pSRB->pcmd->sense_buffer); + outb(bval, ioport+ScsiFifo); + bval = 0; + outb(bval, ioport+ScsiFifo); + } + pSRB->SRBState = SRB_COMMAND; + bval = INFO_XFER_CMD; + outb(bval, ioport+ScsiCmd); +} + +static void +DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR bval; + USHORT ioport; + + ioport = pACB->IOPortBase; + bval = CLEAR_FIFO_CMD; + outb(bval, ioport+ScsiCmd); + pSRB->SRBState = SRB_STATUS; + bval = INITIATOR_CMD_CMPLTE; + outb(bval, ioport+ScsiCmd); +} + +static void +DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR bval; + USHORT ioport, i, cnt; + PUCHAR ptr; + PDCB pDCB; + + ioport = pACB->IOPortBase; + bval = CLEAR_FIFO_CMD; + outb(bval, ioport+ScsiCmd); + pDCB = pACB->pActiveDCB; + if( !(pSRB->SRBState & SRB_MSGOUT) ) + { + cnt = pSRB->MsgCnt; + if( cnt ) + { + ptr = (PUCHAR) pSRB->MsgOutBuf; + for(i=0; i < cnt; i++) + { + outb(*ptr, ioport+ScsiFifo); + ptr++; + } + pSRB->MsgCnt = 0; + if( (pDCB->DCBFlag & ABORT_DEV_) && + (pSRB->MsgOutBuf[0] == MSG_ABORT) ) + pSRB->SRBState = SRB_ABORT_SENT; + } + else + { + bval = MSG_ABORT; /* ??? MSG_NOP */ + if( (pSRB->CmdBlock[0] == INQUIRY ) || + (pSRB->CmdBlock[0] == REQUEST_SENSE) || + (pSRB->SRBFlag & AUTO_REQSENSE) ) + { + if( pDCB->SyncMode & SYNC_ENABLE ) + goto mop1; + } + outb(bval, ioport+ScsiFifo); + } + bval = INFO_XFER_CMD; + outb( bval, ioport+ScsiCmd); + } + else + { +mop1: + bval = MSG_EXTENDED; + outb(bval, ioport+ScsiFifo); + bval = 3; /* ;length of extended msg */ + outb(bval, ioport+ScsiFifo); + bval = 1; /* ; sync nego */ + outb(bval, ioport+ScsiFifo); + bval = pDCB->NegoPeriod; + outb(bval, ioport+ScsiFifo); + bval = SYNC_NEGO_OFFSET; + outb(bval, ioport+ScsiFifo); + pSRB->SRBState |= DO_SYNC_NEGO; + bval = INFO_XFER_CMD; + outb(bval, ioport+ScsiCmd); + } +} + +static void +DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ + UCHAR bval; + USHORT ioport; + + ioport = pACB->IOPortBase; + bval = CLEAR_FIFO_CMD; + outb(bval, ioport+ScsiCmd); + if( !(pSRB->SRBState & SRB_MSGIN) ) + { + pSRB->SRBState &= SRB_DISCONNECT; + pSRB->SRBState |= SRB_MSGIN; + } + bval = INFO_XFER_CMD; + outb(bval, ioport+ScsiCmd); +} + +static void +DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ +} + +static void +DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus) +{ +} + + +static void +SetXferRate( PACB pACB, PDCB pDCB ) +{ + UCHAR bval; + USHORT cnt, i; + PDCB ptr; + + if( !(pDCB->IdentifyMsg & 0x07) ) + { + if( pACB->scan_devices ) + { + CurrSyncOffset = pDCB->SyncOffset; + } + else + { + ptr = pACB->pLinkDCB; + cnt = pACB->DeviceCnt; + bval = pDCB->UnitSCSIID; + for(i=0; iUnitSCSIID == bval ) + { + ptr->SyncPeriod = pDCB->SyncPeriod; + ptr->SyncOffset = pDCB->SyncOffset; + ptr->CtrlR3 = pDCB->CtrlR3; + ptr->CtrlR4 = pDCB->CtrlR4; + ptr->SyncMode = pDCB->SyncMode; + } + ptr = ptr->pNextDCB; + } + } + } + return; +} + + +static void +DC390_Disconnect( PACB pACB ) +{ + PDCB pDCB; + PSRB pSRB, psrb; + ULONG flags; + USHORT ioport, i, cnt; + UCHAR bval; + +#ifdef DC390_DEBUG0 + printk("DISC,"); +#endif + + save_flags(flags); + cli(); + ioport = pACB->IOPortBase; + pDCB = pACB->pActiveDCB; + if (!pDCB) + { +#ifdef DC390_DEBUG0 + printk("ACB:%08lx->ActiveDCB:%08lx !,",(ULONG)pACB,(ULONG)pDCB); +#endif + restore_flags(flags); return; + } + pSRB = pDCB->pActiveSRB; + pACB->pActiveDCB = 0; + pSRB->ScsiPhase = SCSI_NOP0; + bval = EN_SEL_RESEL; + outb(bval, ioport+ScsiCmd); + if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) + { + pSRB->SRBState = 0; + DoWaitingSRB( pACB ); + } + else if( pSRB->SRBState & SRB_ABORT_SENT ) + { + pDCB->TagMask = 0; + pDCB->DCBFlag = 0; + cnt = pDCB->GoingSRBCnt; + pDCB->GoingSRBCnt = 0; + pSRB = pDCB->pGoingSRB; + for( i=0; i < cnt; i++) + { + psrb = pSRB->pNextSRB; + pSRB->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = pSRB; + pSRB = psrb; + } + pDCB->pGoingSRB = 0; + DoWaitingSRB( pACB ); + } + else + { + if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) || + !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) ) + { /* Selection time out */ + if( !(pACB->scan_devices) ) + { + pSRB->SRBState = SRB_READY; + RewaitSRB( pDCB, pSRB); + } + else + { + pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT; + goto disc1; + } + } + else if( pSRB->SRBState & SRB_DISCONNECT ) + { + DoWaitingSRB( pACB ); + } + else if( pSRB->SRBState & SRB_COMPLETED ) + { +disc1: + if(pDCB->MaxCommand > 1) + { + bval = pSRB->TagNumber; + pDCB->TagMask &= (~(1 << bval)); /* free tag mask */ + } + pDCB->pActiveSRB = 0; + pSRB->SRBState = SRB_FREE; + SRBdone( pACB, pDCB, pSRB); + } + } + restore_flags(flags); + return; +} + + +static void +DC390_Reselect( PACB pACB ) +{ + PDCB pDCB, pdcb; + PSRB pSRB; + USHORT ioport, wval; + UCHAR bval, bval1; + + +#ifdef DC390_DEBUG0 + printk("RSEL,"); +#endif + ioport = pACB->IOPortBase; + pDCB = pACB->pActiveDCB; + if( pDCB ) + { /* Arbitration lost but Reselection win */ + pSRB = pDCB->pActiveSRB; + if( !( pACB->scan_devices ) ) + { + pSRB->SRBState = SRB_READY; + RewaitSRB( pDCB, pSRB); + } + } + bval = inb(ioport+ScsiFifo); /* get ID */ + bval = bval ^ pACB->HostID_Bit; + wval = 0; + bval1 = 1; + for(;;) + { + if( !(bval & bval1) ) + { + bval1 = bval1 << 1; + wval++; + } + else + break; + } + wval |= ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8; /* get LUN */ + pDCB = pACB->pLinkDCB; + pdcb = pDCB; + while( wval != *((PUSHORT) &pDCB->UnitSCSIID) ) + { + pDCB = pDCB->pNextDCB; + if( pDCB == pdcb ) + return; + } + pACB->pActiveDCB = pDCB; + if( pDCB->SyncMode & EN_TAG_QUEUING ) + { + pSRB = pACB->pTmpSRB; + pDCB->pActiveSRB = pSRB; + } + else + { + pSRB = pDCB->pActiveSRB; + if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) ) + { + pSRB= pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + EnableMsgOut( pACB, pSRB ); + } + else + { + if( pDCB->DCBFlag & ABORT_DEV_ ) + { + pSRB->SRBState = SRB_ABORT_SENT; + EnableMsgOut( pACB, pSRB ); + } + else + pSRB->SRBState = SRB_DATA_XFER; + } + } + pSRB->ScsiPhase = SCSI_NOP0; + bval = pDCB->UnitSCSIID; + outb( bval, ioport+Scsi_Dest_ID); + bval = pDCB->SyncPeriod; + outb(bval, ioport+Sync_Period); + bval = pDCB->SyncOffset; + outb( bval, ioport+Sync_Offset); + bval = pDCB->CtrlR1; + outb(bval, ioport+CtrlReg1); + bval = pDCB->CtrlR3; + outb(bval, ioport+CtrlReg3); + bval = pDCB->CtrlR4; /* ; Glitch eater */ + outb(bval, ioport+CtrlReg4); + bval = MSG_ACCEPTED_CMD; /* ;to rls the /ACK signal */ + outb(bval, ioport+ScsiCmd); +} + + +static void +SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ) +{ + PSRB psrb; + UCHAR bval, bval1, i, j, status; + PSCSICMD pcmd; + PSCSI_INQDATA ptr; + USHORT disable_tag; + ULONG flags; + PSGL ptr2; + ULONG swlval; + + pcmd = pSRB->pcmd; + status = pSRB->TargetStatus; + if(pSRB->SRBFlag & AUTO_REQSENSE) + { + pSRB->SRBFlag &= ~AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = SCSI_STAT_CHECKCOND; + if(status == SCSI_STAT_CHECKCOND) + { + pcmd->result = DID_BAD_TARGET << 16; + goto ckc_e; + } + if(pSRB->RetryCnt == 0) + { + *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; + pSRB->TotalXferredLen = pSRB->Segment1[1]; + if( (pSRB->TotalXferredLen) && + (pSRB->TotalXferredLen >= pcmd->underflow) ) + pcmd->result |= (DID_OK << 16); + else + pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | + SCSI_STAT_CHECKCOND; +#ifdef DC390_DEBUG0 + printk("Cmd=%2x,Result=%8x,XferL=%8x,",pSRB->CmdBlock[0], + (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen); +#endif + goto ckc_e; + } + else + { + pSRB->RetryCnt--; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; + *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1]; + if( pSRB->CmdBlock[0] == TEST_UNIT_READY ) + { + pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | + SCSI_STAT_CHECKCOND; + goto ckc_e; + } + pcmd->result |= (DRIVER_SENSE << 24); + pSRB->SGcount = (UCHAR) pSRB->Segment1[0]; + pSRB->ScsiCmdLen = (UCHAR) (pSRB->Segment1[0] >> 8); + pSRB->SGIndex = 0; + pSRB->TotalXferredLen = 0; + pSRB->SGToBeXferLen = 0; + if( pcmd->use_sg ) + pSRB->pSegmentList = (PSGL) pcmd->request_buffer; + else if( pcmd->request_buffer ) + { + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; + pSRB->Segmentx.length = pcmd->request_bufflen; + } + if( DC390_StartSCSI( pACB, pDCB, pSRB ) ) + RewaitSRB( pDCB, pSRB ); + return; + } + } + if( status ) + { + if( status == SCSI_STAT_CHECKCOND) + { + if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) ) + { + bval = pSRB->SGcount; + swlval = 0; + ptr2 = pSRB->pSegmentList; + for( i=pSRB->SGIndex; i < bval; i++) + { + swlval += ptr2->length; + ptr2++; + } +#ifdef DC390_DEBUG0 + printk("XferredLen=%8x,NotXferLen=%8x,", + (UINT) pSRB->TotalXferredLen, (UINT) swlval); +#endif + } + RequestSense( pACB, pDCB, pSRB ); + return; + } + else if( status == SCSI_STAT_QUEUEFULL ) + { + bval = (UCHAR) pDCB->GoingSRBCnt; + bval--; + pDCB->MaxCommand = bval; + RewaitSRB( pDCB, pSRB ); + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + return; + } + else if(status == SCSI_STAT_SEL_TIMEOUT) + { + pSRB->AdaptStatus = H_SEL_TIMEOUT; + pSRB->TargetStatus = 0; + pcmd->result = DID_BAD_TARGET << 16; + } + else + { + pSRB->AdaptStatus = 0; + if( pSRB->RetryCnt ) + { + pSRB->RetryCnt--; + pSRB->TargetStatus = 0; + pSRB->SGIndex = 0; + pSRB->TotalXferredLen = 0; + pSRB->SGToBeXferLen = 0; + if( pcmd->use_sg ) + pSRB->pSegmentList = (PSGL) pcmd->request_buffer; + else if( pcmd->request_buffer ) + { + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; + pSRB->Segmentx.length = pcmd->request_bufflen; + } + if( DC390_StartSCSI( pACB, pDCB, pSRB ) ) + RewaitSRB( pDCB, pSRB ); + return; + } + else + { + pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) | + (ULONG) status; + } + } + } + else + { + status = pSRB->AdaptStatus; + if(status & H_OVER_UNDER_RUN) + { + pSRB->TargetStatus = 0; + pcmd->result |= (DID_OK << 16) | (pSRB->EndMessage << 8); + } + else if( pSRB->SRBStatus & PARITY_ERROR) + { + pcmd->result |= (DID_PARITY << 16) | (pSRB->EndMessage << 8); + } + else /* No error */ + { + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pcmd->result |= (DID_OK << 16); + } + } + +ckc_e: + if( pACB->scan_devices ) + { + if( pSRB->CmdBlock[0] == TEST_UNIT_READY ) + { + if(pcmd->result != (DID_OK << 16)) + { + if( pcmd->result & SCSI_STAT_CHECKCOND ) + { + goto RTN_OK; + } + else + { + pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun); + pPrevDCB->pNextDCB = pACB->pLinkDCB; + if( (pcmd->target == pACB->max_id) && + ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) ) + { + pACB->scan_devices = 0; + } + } + } + else + { +RTN_OK: + pPrevDCB->pNextDCB = pDCB; + pDCB->pNextDCB = pACB->pLinkDCB; + if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) ) + pACB->scan_devices = END_SCAN; + } + } + else if( pSRB->CmdBlock[0] == INQUIRY ) + { + if( (pcmd->target == pACB->max_id) && + (pcmd->lun == pACB->max_lun) ) + { + pACB->scan_devices = 0; + } + ptr = (PSCSI_INQDATA) (pcmd->request_buffer); + if( pcmd->use_sg ) + ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address); + bval1 = ptr->DevType & SCSI_DEVTYPE; + if(bval1 == SCSI_NODEV) + { + pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun); + pPrevDCB->pNextDCB = pACB->pLinkDCB; + } + else + { + pACB->DeviceCnt++; + pPrevDCB = pDCB; + pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390_DCB )); + pDCB->DevType = bval1; + if(bval1 == TYPE_DISK || bval1 == TYPE_MOD) + { + if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) && + (ptr->Flags & SCSI_INQ_CMDQUEUE) && + (pDCB->DevMode & TAG_QUEUING_) && + (pDCB->DevMode & EN_DISCONNECT_) ) + { + disable_tag = 0; + for(i=0; iMaxCommand = pACB->TagMaxNum; + pDCB->SyncMode |= EN_TAG_QUEUING; + pDCB->TagMask = 0; + } + else + { + pDCB->SyncMode |= EN_ATN_STOP; + } + } + } + } + } + } + + save_flags( flags ); + cli(); +/* ReleaseSRB( pDCB, pSRB ); */ + + if(pSRB == pDCB->pGoingSRB ) + { + pDCB->pGoingSRB = pSRB->pNextSRB; + } + else + { + psrb = pDCB->pGoingSRB; + while( psrb->pNextSRB != pSRB ) + psrb = psrb->pNextSRB; + psrb->pNextSRB = pSRB->pNextSRB; + if( pSRB == pDCB->pGoingLast ) + pDCB->pGoingLast = psrb; + } + pSRB->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = pSRB; + pDCB->GoingSRBCnt--; + + DoWaitingSRB( pACB ); + restore_flags(flags); + +/* Notify cmd done */ + pcmd->scsi_done( pcmd ); + + if( pDCB->QIORBCnt ) + DoNextCmd( pACB, pDCB ); + return; +} + + +static void +DoingSRB_Done( PACB pACB ) +{ + PDCB pDCB, pdcb; + PSRB psrb, psrb2; + USHORT cnt, i; + PSCSICMD pcmd; + + pDCB = pACB->pLinkDCB; + pdcb = pDCB; + do + { + cnt = pdcb->GoingSRBCnt; + psrb = pdcb->pGoingSRB; + for( i=0; ipNextSRB; + pcmd = psrb->pcmd; + pcmd->result = DID_RESET << 16; + +/* ReleaseSRB( pDCB, pSRB ); */ + + psrb->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = psrb; + + pcmd->scsi_done( pcmd ); + psrb = psrb2; + } + pdcb->GoingSRBCnt = 0;; + pdcb->pGoingSRB = NULL; + pdcb->TagMask = 0; + pdcb = pdcb->pNextDCB; + } + while( pdcb != pDCB ); +} + + +static void +DC390_ResetSCSIBus( PACB pACB ) +{ + USHORT ioport; + UCHAR bval; + ULONG flags; + + save_flags(flags); + cli(); + pACB->ACBFlag |= RESET_DEV; + ioport = pACB->IOPortBase; + + bval = DMA_IDLE_CMD; + outb(bval,ioport+DMA_Cmd); + + bval = RST_SCSI_BUS_CMD; + outb(bval,ioport+ScsiCmd); + + restore_flags(flags); + return; +} + + +static void +DC390_ScsiRstDetect( PACB pACB ) +{ + ULONG wlval, flags; + USHORT ioport; + UCHAR bval; + +#ifdef DC390_DEBUG0 + printk("RST_DETEC"); +#endif + save_flags(flags); + sti(); + wlval = jiffies + HZ; + while( jiffies < wlval ); /* delay 1 sec */ + + cli(); + ioport = pACB->IOPortBase; + bval = DMA_IDLE_CMD; + outb(bval,ioport+DMA_Cmd); + bval = CLEAR_FIFO_CMD; + outb(bval,ioport+ScsiCmd); + + if( pACB->ACBFlag & RESET_DEV ) + pACB->ACBFlag |= RESET_DONE; + else + { + pACB->ACBFlag |= RESET_DETECT; + + ResetDevParam( pACB ); +/* DoingSRB_Done( pACB ); ???? */ + RecoverSRB( pACB ); + pACB->pActiveDCB = NULL; + pACB->ACBFlag = 0; + DoWaitingSRB( pACB ); + } + restore_flags(flags); + return; +} + + +static void +RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ) +{ + PSCSICMD pcmd; + + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0])); + pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4])); + pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount); + pSRB->Segment1[1] = pSRB->TotalXferredLen; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + + pcmd = pSRB->pcmd; + + pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer); + pSRB->Segmentx.length = sizeof(pcmd->sense_buffer); + pSRB->pSegmentList = &pSRB->Segmentx; + pSRB->SGcount = 1; + pSRB->SGIndex = 0; + + *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003; + pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5; + *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer); + pSRB->ScsiCmdLen = 6; + + pSRB->TotalXferredLen = 0; + pSRB->SGToBeXferLen = 0; + if( DC390_StartSCSI( pACB, pDCB, pSRB ) ) + RewaitSRB( pDCB, pSRB ); +} + + +static void +EnableMsgOut2( PACB pACB, PSRB pSRB ) +{ + USHORT ioport; + UCHAR bval; + + ioport = pACB->IOPortBase; + pSRB->MsgCnt = 1; + bval = SET_ATN_CMD; + outb(bval, ioport+ScsiCmd); +} + + +static void +EnableMsgOut( PACB pACB, PSRB pSRB ) +{ + pSRB->MsgOutBuf[0] = MSG_ABORT; + EnableMsgOut2( pACB, pSRB ); +} + + +static void +DC390_InvalidCmd( PACB pACB ) +{ + UCHAR bval; + USHORT ioport; + PSRB pSRB; + + pSRB = pACB->pActiveDCB->pActiveSRB; + if( pSRB->SRBState & (SRB_START_+SRB_MSGOUT) ) + { + ioport = pACB->IOPortBase; + bval = CLEAR_FIFO_CMD; + outb(bval,(ioport+ScsiCmd)); + } +} + diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c new file mode 100644 index 000000000000..f5ceb137174f --- /dev/null +++ b/drivers/scsi/tmscsim.c @@ -0,0 +1,1928 @@ +/*********************************************************************** + * FILE NAME : TMSCSIM.C * + * BY : C.L. Huang, ching@tekram.com.tw * + * Description: Device Driver for Tekram DC-390(T) PCI SCSI * + * Bus Master Host Adapter * + * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. * + ***********************************************************************/ +/* Minor enhancements and bugfixes by * + * Kurt Garloff * + ***********************************************************************/ +/* HISTORY: * + * * + * REV# DATE NAME DESCRIPTION * + * 1.00 04/24/96 CLH First release * + * 1.01 06/12/96 CLH Fixed bug of Media Change for Removable * + * Device, scan all LUN. Support Pre2.0.10 * + * 1.02 06/18/96 CLH Fixed bug of Command timeout ... * + * 1.03 09/25/96 KG Added tmscsim_proc_info() * + * 1.04 10/11/96 CLH Updating for support KV 2.0.x * + * 1.05 10/18/96 KG Fixed bug in DC390_abort(null ptr deref)* + * 1.06 10/25/96 KG Fixed module support * + * 1.07 11/09/96 KG Fixed tmscsim_proc_info() * + * 1.08 11/18/96 KG Fixed null ptr in DC390_Disconnect() * + * 1.09 11/30/96 KG Added register the allocated IO space * + * 1.10 12/05/96 CLH Modified tmscsim_proc_info(), and reset * + * pending interrupt in DC390_detect() * + ***********************************************************************/ + + +#define DC390_DEBUG + +#define SCSI_MALLOC + +#ifdef MODULE +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */ +#include "../block/blk.h" +#else +#include +#endif + +#include "scsi.h" +#include "hosts.h" +#include "tmscsim.h" +#include "constants.h" +#include "sd.h" +#include + +#include "dc390.h" + +#define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI + + +#ifndef VERSION_ELF_1_2_13 +struct proc_dir_entry proc_scsi_tmscsim ={ + PROC_SCSI_DC390T, 7 ,"tmscsim", + S_IFDIR | S_IRUGO | S_IXUGO, 2 + }; +#endif + +static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus); + +static void SetXferRate( PACB pACB, PDCB pDCB ); +static void DC390_Disconnect( PACB pACB ); +static void DC390_Reselect( PACB pACB ); +static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void DoingSRB_Done( PACB pACB ); +static void DC390_ScsiRstDetect( PACB pACB ); +static void DC390_ResetSCSIBus( PACB pACB ); +static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void EnableMsgOut2( PACB pACB, PSRB pSRB ); +static void EnableMsgOut( PACB pACB, PSRB pSRB ); +static void DC390_InvalidCmd( PACB pACB ); + +int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ); +void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ); + +#ifdef MODULE +static int DC390_release(struct Scsi_Host *host); +static int DC390_shutdown (struct Scsi_Host *host); +#endif + + +static PSHT pSHT_start = NULL; +static PSH pSH_start = NULL; +static PSH pSH_current = NULL; +static PACB pACB_start= NULL; +static PACB pACB_current = NULL; +static PDCB pPrevDCB = NULL; +static USHORT adapterCnt = 0; +static USHORT InitialTime = 0; +static USHORT CurrSyncOffset = 0; +static ULONG mech1addr; +static UCHAR mech2bus, mech2Agent, mech2CfgSPenR; + +static PVOID DC390_phase0[]={ + DC390_DataOut_0, + DC390_DataIn_0, + DC390_Command_0, + DC390_Status_0, + DC390_Nop_0, + DC390_Nop_0, + DC390_MsgOut_0, + DC390_MsgIn_0, + DC390_Nop_1 + }; + +static PVOID DC390_phase1[]={ + DC390_DataOutPhase, + DC390_DataInPhase, + DC390_CommandPhase, + DC390_StatusPhase, + DC390_Nop_0, + DC390_Nop_0, + DC390_MsgOutPhase, + DC390_MsgInPhase, + DC390_Nop_1, + }; + +UCHAR eepromBuf[MAX_ADAPTER_NUM][128]; + + +UCHAR clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20}; + +UCHAR baddevname1[2][28] ={ + "SEAGATE ST3390N 9546", + "HP C3323-300 4269"}; + +#define BADDEVCNT 2 + + +/*********************************************************************** + * + * + * + **********************************************************************/ +static void +QLinkcmd( PSCSICMD cmd, PDCB pDCB ) +{ + ULONG flags; + PSCSICMD pcmd; + + save_flags(flags); + cli(); + + if( !pDCB->QIORBCnt ) + { + pDCB->pQIORBhead = cmd; + pDCB->pQIORBtail = cmd; + pDCB->QIORBCnt++; + cmd->next = NULL; + } + else + { + pcmd = pDCB->pQIORBtail; + pcmd->next = cmd; + pDCB->pQIORBtail = cmd; + pDCB->QIORBCnt++; + cmd->next = NULL; + } + + restore_flags(flags); +} + + +static PSCSICMD +Getcmd( PDCB pDCB ) +{ + ULONG flags; + PSCSICMD pcmd; + + save_flags(flags); + cli(); + + pcmd = pDCB->pQIORBhead; + pDCB->pQIORBhead = pcmd->next; + pcmd->next = NULL; + pDCB->QIORBCnt--; + + restore_flags(flags); + return( pcmd ); +} + + +static PSRB +GetSRB( PACB pACB ) +{ + ULONG flags; + PSRB pSRB; + + save_flags(flags); + cli(); + + pSRB = pACB->pFreeSRB; + if( pSRB ) + { + pACB->pFreeSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + } + restore_flags(flags); + return( pSRB ); +} + + +static void +RewaitSRB0( PDCB pDCB, PSRB pSRB ) +{ + PSRB psrb1; + ULONG flags; + + save_flags(flags); + cli(); + + if( (psrb1 = pDCB->pWaitingSRB) ) + { + pSRB->pNextSRB = psrb1; + pDCB->pWaitingSRB = pSRB; + } + else + { + pSRB->pNextSRB = NULL; + pDCB->pWaitingSRB = pSRB; + pDCB->pWaitLast = pSRB; + } + restore_flags(flags); +} + + +static void +RewaitSRB( PDCB pDCB, PSRB pSRB ) +{ + PSRB psrb1; + ULONG flags; + UCHAR bval; + + save_flags(flags); + cli(); + pDCB->GoingSRBCnt--; + psrb1 = pDCB->pGoingSRB; + if( pSRB == psrb1 ) + { + pDCB->pGoingSRB = psrb1->pNextSRB; + } + else + { + while( pSRB != psrb1->pNextSRB ) + psrb1 = psrb1->pNextSRB; + psrb1->pNextSRB = pSRB->pNextSRB; + if( pSRB == pDCB->pGoingLast ) + pDCB->pGoingLast = psrb1; + } + if( (psrb1 = pDCB->pWaitingSRB) ) + { + pSRB->pNextSRB = psrb1; + pDCB->pWaitingSRB = pSRB; + } + else + { + pSRB->pNextSRB = NULL; + pDCB->pWaitingSRB = pSRB; + pDCB->pWaitLast = pSRB; + } + + bval = pSRB->TagNumber; + pDCB->TagMask &= (~(1 << bval)); /* Free TAG number */ + restore_flags(flags); +} + + +static void +DoWaitingSRB( PACB pACB ) +{ + ULONG flags; + PDCB ptr, ptr1; + PSRB pSRB; + + save_flags(flags); + cli(); + + if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) ) + { + ptr = pACB->pDCBRunRobin; + if( !ptr ) + { + ptr = pACB->pLinkDCB; + pACB->pDCBRunRobin = ptr; + } + ptr1 = ptr; + for( ;ptr1; ) + { + pACB->pDCBRunRobin = ptr1->pNextDCB; + if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) || + !( pSRB = ptr1->pWaitingSRB ) ) + { + if(pACB->pDCBRunRobin == ptr) + break; + ptr1 = ptr1->pNextDCB; + } + else + { + if( !DC390_StartSCSI(pACB, ptr1, pSRB) ) + { + ptr1->GoingSRBCnt++; + if( ptr1->pWaitLast == pSRB ) + { + ptr1->pWaitingSRB = NULL; + ptr1->pWaitLast = NULL; + } + else + { + ptr1->pWaitingSRB = pSRB->pNextSRB; + } + pSRB->pNextSRB = NULL; + + if( ptr1->pGoingSRB ) + ptr1->pGoingLast->pNextSRB = pSRB; + else + ptr1->pGoingSRB = pSRB; + ptr1->pGoingLast = pSRB; + } + break; + } + } + } + restore_flags(flags); + return; +} + + +static void +SRBwaiting( PDCB pDCB, PSRB pSRB) +{ + if( pDCB->pWaitingSRB ) + { + pDCB->pWaitLast->pNextSRB = pSRB; + pDCB->pWaitLast = pSRB; + pSRB->pNextSRB = NULL; + } + else + { + pDCB->pWaitingSRB = pSRB; + pDCB->pWaitLast = pSRB; + } +} + + +static void +SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB ) +{ + ULONG flags; + PDCB pDCB; + + save_flags(flags); + cli(); + + pDCB = pSRB->pSRBDCB; + if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) || + (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) ) + { + SRBwaiting(pDCB, pSRB); + goto SND_EXIT; + } + + if( pDCB->pWaitingSRB ) + { + SRBwaiting(pDCB, pSRB); +/* pSRB = GetWaitingSRB(pDCB); */ + pSRB = pDCB->pWaitingSRB; + pDCB->pWaitingSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + } + + if( !DC390_StartSCSI(pACB, pDCB, pSRB) ) + { + pDCB->GoingSRBCnt++; + if( pDCB->pGoingSRB ) + { + pDCB->pGoingLast->pNextSRB = pSRB; + pDCB->pGoingLast = pSRB; + } + else + { + pDCB->pGoingSRB = pSRB; + pDCB->pGoingLast = pSRB; + } + } + else + RewaitSRB0( pDCB, pSRB ); + +SND_EXIT: + restore_flags(flags); + return; +} + + +/*********************************************************************** + * Function : static int DC390_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - function called on completion, with + * a pointer to the command descriptor. + * + * Returns : 0 + * + ***********************************************************************/ + +int +DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) +{ + USHORT ioport, i; + Scsi_Cmnd *pcmd; + struct Scsi_Host *psh; + PACB pACB; + PDCB pDCB; + PSRB pSRB; + ULONG flags; + PUCHAR ptr,ptr1; + + psh = cmd->host; + pACB = (PACB ) psh->hostdata; + ioport = pACB->IOPortBase; + +#ifdef DC390_DEBUG0 +/* if(pACB->scan_devices) */ + printk("Cmd=%2x,ID=%d,LUN=%d,",cmd->cmnd[0],cmd->target,cmd->lun); +#endif + + if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) ) + { + pACB->scan_devices = 0; + pPrevDCB->pNextDCB = pACB->pLinkDCB; + } + else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) ) + { + pACB->scan_devices = 0; + pPrevDCB->pNextDCB = pACB->pLinkDCB; + } + + if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) ) + { +/* printk("DC390: Ignore target %d lun %d\n", + cmd->target, cmd->lun); */ + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return( 0 ); + } + + if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) + { + if( pACB->DeviceCnt < MAX_DEVICES ) + { + pACB->DCBmap[cmd->target] |= (1 << cmd->lun); + pDCB = pACB->pDCB_free; +#ifdef DC390_DEBUG0 + printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target); +#endif + DC390_initDCB( pACB, pDCB, cmd ); + } + else /* ???? */ + { +/* printk("DC390: Ignore target %d lun %d\n", + cmd->target, cmd->lun); */ + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return(0); + } + } + else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) + { +/* printk("DC390: Ignore target %d lun %d\n", + cmd->target, cmd->lun); */ + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return(0); + } + else + { + pDCB = pACB->pLinkDCB; + while( (pDCB->UnitSCSIID != cmd->target) || + (pDCB->UnitSCSILUN != cmd->lun) ) + { + pDCB = pDCB->pNextDCB; + } +#ifdef DC390_DEBUG0 + printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target); +#endif + } + + cmd->scsi_done = done; + cmd->result = 0; + + save_flags(flags); + cli(); + + if( pDCB->QIORBCnt ) + { + QLinkcmd( cmd, pDCB ); + pcmd = Getcmd( pDCB ); + } + else + pcmd = cmd; + + pSRB = GetSRB( pACB ); + + if( !pSRB ) + { + QLinkcmd( pcmd, pDCB ); + restore_flags(flags); + return(0); + } + +/* BuildSRB(pSRB); */ + + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + ptr = (PUCHAR) pSRB->CmdBlock; + ptr1 = (PUCHAR) pcmd->cmnd; + pSRB->ScsiCmdLen = pcmd->cmd_len; + for(i=0; i< pcmd->cmd_len; i++) + { + *ptr = *ptr1; + ptr++; + ptr1++; + } + if( pcmd->use_sg ) + { + pSRB->SGcount = (UCHAR) pcmd->use_sg; + pSRB->pSegmentList = (PSGL) pcmd->request_buffer; + } + else if( pcmd->request_buffer ) + { + pSRB->SGcount = 1; + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; + pSRB->Segmentx.length = pcmd->request_bufflen; + } + else + pSRB->SGcount = 0; + + pSRB->SGIndex = 0; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + if( pDCB->DevType != TYPE_TAPE ) + pSRB->RetryCnt = 1; + else + pSRB->RetryCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->SRBState = 0; + pSRB->TotalXferredLen = 0; + pSRB->SGPhysAddr = 0; + pSRB->SGToBeXferLen = 0; + pSRB->ScsiPhase = 0; + pSRB->EndMessage = 0; + SendSRB( pcmd, pACB, pSRB ); + + restore_flags(flags); + return(0); +} + + +static void +DoNextCmd( PACB pACB, PDCB pDCB ) +{ + Scsi_Cmnd *pcmd; + PSRB pSRB; + ULONG flags; + PUCHAR ptr,ptr1; + USHORT i; + + + if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) + return; + save_flags(flags); + cli(); + + pcmd = Getcmd( pDCB ); + pSRB = GetSRB( pACB ); + if( !pSRB ) + { + QLinkcmd( pcmd, pDCB ); + restore_flags(flags); + return; + } + + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + ptr = (PUCHAR) pSRB->CmdBlock; + ptr1 = (PUCHAR) pcmd->cmnd; + pSRB->ScsiCmdLen = pcmd->cmd_len; + for(i=0; i< pcmd->cmd_len; i++) + { + *ptr = *ptr1; + ptr++; + ptr1++; + } + if( pcmd->use_sg ) + { + pSRB->SGcount = (UCHAR) pcmd->use_sg; + pSRB->pSegmentList = (PSGL) pcmd->request_buffer; + } + else if( pcmd->request_buffer ) + { + pSRB->SGcount = 1; + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; + pSRB->Segmentx.length = pcmd->request_bufflen; + } + else + pSRB->SGcount = 0; + + pSRB->SGIndex = 0; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + if( pDCB->DevType != TYPE_TAPE ) + pSRB->RetryCnt = 1; + else + pSRB->RetryCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->SRBState = 0; + pSRB->TotalXferredLen = 0; + pSRB->SGPhysAddr = 0; + pSRB->SGToBeXferLen = 0; + pSRB->ScsiPhase = 0; + pSRB->EndMessage = 0; + SendSRB( pcmd, pACB, pSRB ); + + restore_flags(flags); + return; +} + + +/*********************************************************************** + * Function: + * DC390_bios_param + * + * Description: + * Return the disk geometry for the given SCSI device. + ***********************************************************************/ +#ifdef VERSION_ELF_1_2_13 +int DC390_bios_param(Disk *disk, int devno, int geom[]) +#else +int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]) +#endif +{ + int heads, sectors, cylinders; + PACB pACB; + + pACB = (PACB) disk->device->host->hostdata; + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + if ( cylinders > 1024) + { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (255 * 63); + } + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return (0); +} + + +/*********************************************************************** + * Function : int DC390_abort (Scsi_Cmnd *cmd) + * + * Purpose : Abort an errant SCSI command + * + * Inputs : cmd - command to abort + * + * Returns : 0 on success, -1 on failure. + ***********************************************************************/ + +int +DC390_abort (Scsi_Cmnd *cmd) +{ + ULONG flags; + PACB pACB; + PDCB pDCB, pdcb; + PSRB pSRB, psrb; + USHORT count, i; + PSCSICMD pcmd, pcmd1; + int status; + + +#ifdef DC390_DEBUG0 + printk("DC390 : Abort Cmd."); +#endif + + save_flags(flags); + cli(); + + pACB = (PACB) cmd->host->hostdata; + pDCB = pACB->pLinkDCB; + pdcb = pDCB; + while( (pDCB->UnitSCSIID != cmd->target) || + (pDCB->UnitSCSILUN != cmd->lun) ) + { + pDCB = pDCB->pNextDCB; + if( pDCB == pdcb ) + goto NOT_RUN; + } + + if( pDCB->QIORBCnt ) + { + pcmd = pDCB->pQIORBhead; + if( pcmd == cmd ) + { + pDCB->pQIORBhead = pcmd->next; + pcmd->next = NULL; + pDCB->QIORBCnt--; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + for( count = pDCB->QIORBCnt, i=0; inext == cmd ) + { + pcmd1 = pcmd->next; + pcmd->next = pcmd1->next; + pcmd1->next = NULL; + pDCB->QIORBCnt--; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + else + { + pcmd = pcmd->next; + } + } + } + + pSRB = pDCB->pWaitingSRB; + if( !pSRB ) + goto ON_GOING; + if( pSRB->pcmd == cmd ) + { + pDCB->pWaitingSRB = pSRB->pNextSRB; + goto IN_WAIT; + } + else + { + psrb = pSRB; + if( !(psrb->pNextSRB) ) + goto ON_GOING; + while( psrb->pNextSRB->pcmd != cmd ) + { + psrb = psrb->pNextSRB; + if( !(psrb->pNextSRB) ) + goto ON_GOING; + } + pSRB = psrb->pNextSRB; + psrb->pNextSRB = pSRB->pNextSRB; + if( pSRB == pDCB->pWaitLast ) + pDCB->pWaitLast = psrb; /* No check for psrb == NULL ? */ +IN_WAIT: + pSRB->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = pSRB; + cmd->next = NULL; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + +ON_GOING: + pSRB = pDCB->pGoingSRB; + for( count = pDCB->GoingSRBCnt, i=0; ipcmd != cmd ) + pSRB = pSRB->pNextSRB; + else + { + if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) ) + { + status = SCSI_ABORT_BUSY; + goto ABO_X; + } + else + { + status = SCSI_ABORT_SNOOZE; + goto ABO_X; + } + } + } + +NOT_RUN: + status = SCSI_ABORT_NOT_RUNNING; + +ABO_X: + cmd->result = DID_ABORT << 16; + cmd->scsi_done(cmd); + restore_flags(flags); + return( status ); +} + + +static void +ResetDevParam( PACB pACB ) +{ + PDCB pDCB, pdcb; + + pDCB = pACB->pLinkDCB; + if( pDCB == NULL ) + return; + pdcb = pDCB; + do + { + pDCB->SyncMode &= ~SYNC_NEGO_DONE; + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + pDCB->CtrlR3 = FAST_CLK; + pDCB->CtrlR4 &= NEGATE_REQACKDATA; + pDCB->CtrlR4 |= EATER_25NS; + pDCB = pDCB->pNextDCB; + } + while( pdcb != pDCB ); +} + + +static void +RecoverSRB( PACB pACB ) +{ + PDCB pDCB, pdcb; + PSRB psrb, psrb2; + USHORT cnt, i; + + pDCB = pACB->pLinkDCB; + if( pDCB == NULL ) + return; + pdcb = pDCB; + do + { + cnt = pdcb->GoingSRBCnt; + psrb = pdcb->pGoingSRB; + for (i=0; ipNextSRB; +/* RewaitSRB( pDCB, psrb ); */ + if( pdcb->pWaitingSRB ) + { + psrb2->pNextSRB = pdcb->pWaitingSRB; + pdcb->pWaitingSRB = psrb2; + } + else + { + pdcb->pWaitingSRB = psrb2; + pdcb->pWaitLast = psrb2; + psrb2->pNextSRB = NULL; + } + } + pdcb->GoingSRBCnt = 0; + pdcb->pGoingSRB = NULL; + pdcb->TagMask = 0; + pdcb = pdcb->pNextDCB; + } + while( pdcb != pDCB ); +} + + +/*********************************************************************** + * Function : int DC390_reset (Scsi_Cmnd *cmd, ...) + * + * Purpose : perform a hard reset on the SCSI bus + * + * Inputs : cmd - command which caused the SCSI RESET + * + * Returns : 0 on success. + ***********************************************************************/ + +#ifdef VERSION_2_0_0 +int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags) +#else +int DC390_reset (Scsi_Cmnd *cmd) +#endif +{ + USHORT ioport; + unsigned long flags; + PACB pACB; + UCHAR bval; + USHORT i; + + +#ifdef DC390_DEBUG1 + printk("DC390: RESET,"); +#endif + + pACB = (PACB ) cmd->host->hostdata; + ioport = pACB->IOPortBase; + save_flags(flags); + cli(); + bval = inb(ioport+CtrlReg1); + bval |= DIS_INT_ON_SCSI_RST; + outb(bval,ioport+CtrlReg1); /* disable interrupt */ + DC390_ResetSCSIBus( pACB ); + for( i=0; i<500; i++ ) + udelay(1000); + bval = inb(ioport+CtrlReg1); + bval &= ~DIS_INT_ON_SCSI_RST; + outb(bval,ioport+CtrlReg1); /* re-enable interrupt */ + + bval = DMA_IDLE_CMD; + outb(bval,ioport+DMA_Cmd); + bval = CLEAR_FIFO_CMD; + outb(bval,ioport+ScsiCmd); + + ResetDevParam( pACB ); + DoingSRB_Done( pACB ); + pACB->pActiveDCB = NULL; + + pACB->ACBFlag = 0; + DoWaitingSRB( pACB ); + + restore_flags(flags); +#ifdef DC390_DEBUG1 + printk("DC390: RESET1,"); +#endif + return( SCSI_RESET_SUCCESS ); +} + + +#include "scsiiom.c" + + +/*********************************************************************** + * Function : static void DC390_initDCB + * + * Purpose : initialize the internal structures for a given DCB + * + * Inputs : cmd - pointer to this scsi cmd request block structure + * + ***********************************************************************/ +void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ) +{ + PEEprom prom; + UCHAR bval; + USHORT index; + + if( pACB->DeviceCnt == 0 ) + { + pACB->pLinkDCB = pDCB; + pACB->pDCBRunRobin = pDCB; + pDCB->pNextDCB = pDCB; + pPrevDCB = pDCB; + } + else + pPrevDCB->pNextDCB = pDCB; + + pDCB->pDCBACB = pACB; + pDCB->QIORBCnt = 0; + pDCB->UnitSCSIID = cmd->target; + pDCB->UnitSCSILUN = cmd->lun; + pDCB->pWaitingSRB = NULL; + pDCB->pGoingSRB = NULL; + pDCB->GoingSRBCnt = 0; + pDCB->pActiveSRB = NULL; + pDCB->TagMask = 0; + pDCB->MaxCommand = 1; + pDCB->AdaptIndex = pACB->AdapterIndex; + index = pACB->AdapterIndex; + pDCB->DCBFlag = 0; + + prom = (PEEprom) &eepromBuf[index][cmd->target << 2]; + pDCB->DevMode = prom->EE_MODE1; + pDCB->AdpMode = eepromBuf[index][EE_MODE2]; + + if( pDCB->DevMode & EN_DISCONNECT_ ) + bval = 0xC0; + else + bval = 0x80; + bval |= cmd->lun; + pDCB->IdentifyMsg = bval; + + pDCB->SyncMode = 0; + if( pDCB->DevMode & SYNC_NEGO_ ) + { + if( !(cmd->lun) || CurrSyncOffset ) + pDCB->SyncMode = SYNC_ENABLE; + } + + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + pDCB->NegoPeriod = (clock_period1[prom->EE_SPEED] * 25) >> 2; + + pDCB->CtrlR1 = pACB->AdaptSCSIID; + if( pDCB->DevMode & PARITY_CHK_ ) + pDCB->CtrlR1 |= PARITY_ERR_REPO; + + pDCB->CtrlR3 = FAST_CLK; + + pDCB->CtrlR4 = EATER_25NS; + if( pDCB->AdpMode & ACTIVE_NEGATION) + pDCB->CtrlR4 |= NEGATE_REQACKDATA; +} + + +/*********************************************************************** + * Function : static void DC390_initSRB + * + * Purpose : initialize the internal structures for a given SRB + * + * Inputs : psrb - pointer to this scsi request block structure + * + ***********************************************************************/ +void DC390_initSRB( PSRB psrb ) +{ +#ifndef VERSION_ELF_1_2_13 +#ifdef DC390_DEBUG0 + printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb)); +#endif + psrb->PhysSRB = virt_to_bus( psrb ); +#else + psrb->PhysSRB = (ULONG) psrb; +#endif +} + + +void DC390_linkSRB( PACB pACB ) +{ + USHORT count, i; + PSRB psrb; + + count = pACB->SRBCount; + + for( i=0; i< count; i++) + { + if( i != count - 1) + pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1]; + else + pACB->SRB_array[i].pNextSRB = NULL; + psrb = (PSRB) &pACB->SRB_array[i]; + DC390_initSRB( psrb ); + } +} + + +/*********************************************************************** + * Function : static void DC390_initACB + * + * Purpose : initialize the internal structures for a given SCSI host + * + * Inputs : psh - pointer to this host adapter's structure + * + ***********************************************************************/ +void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) +{ + PACB pACB; + USHORT i; + + psh->can_queue = MAX_CMD_QUEUE; + psh->cmd_per_lun = MAX_CMD_PER_LUN; + psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID]; + psh->io_port = io_port; + psh->n_io_port = 0x80; + psh->irq = Irq; + + pACB = (PACB) psh->hostdata; + +#ifndef VERSION_ELF_1_2_13 + psh->max_id = 8; +#ifdef CONFIG_SCSI_MULTI_LUN + if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) + psh->max_lun = 8; + else +#endif + psh->max_lun = 1; +#endif + + pACB->max_id = 7; + if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] ) + pACB->max_id--; +#ifdef CONFIG_SCSI_MULTI_LUN + if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) + pACB->max_lun = 7; + else +#endif + pACB->max_lun = 0; + + pACB->pScsiHost = psh; + pACB->IOPortBase = (USHORT) io_port; + pACB->pLinkDCB = NULL; + pACB->pDCBRunRobin = NULL; + pACB->pActiveDCB = NULL; + pACB->pFreeSRB = pACB->SRB_array; + pACB->SRBCount = MAX_SRB_CNT; + pACB->AdapterIndex = index; + pACB->status = 0; + pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID]; + pACB->HostID_Bit = (1 << pACB->AdaptSCSIID); + pACB->AdaptSCSILUN = 0; + pACB->DeviceCnt = 0; + pACB->IRQLevel = Irq; + pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2; + pACB->ACBFlag = 0; + pACB->scan_devices = 1; + pACB->Gmode2 = eepromBuf[index][EE_MODE2]; + if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) + pACB->LUNchk = 1; + pACB->pDCB_free = &pACB->DCB_array[0]; + DC390_linkSRB( pACB ); + pACB->pTmpSRB = &pACB->TmpSRB; + DC390_initSRB( pACB->pTmpSRB ); + for(i=0; iDCBmap[i] = 0; +} + + +/*********************************************************************** + * Function : static int DC390_initAdapter + * + * Purpose : initialize the SCSI chip ctrl registers + * + * Inputs : psh - pointer to this host adapter's structure + * + ***********************************************************************/ +int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) +{ + USHORT ioport; + UCHAR bval; + PACB pACB, pacb; + USHORT used_irq = 0; + + pacb = pACB_start; + if( pacb != NULL ) + { + for ( ; (pacb != (PACB) -1) ; ) + { + if( pacb->IRQLevel == Irq ) + { + used_irq = 1; + break; + } + else + pacb = pacb->pNextACB; + } + } + + if( !used_irq ) + { +#ifdef VERSION_ELF_1_2_13 + if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim")) +#else + if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim", NULL)) +#endif + { + printk("DC390: register IRQ error!\n"); + return( -1 ); + } + } + + request_region(io_port,psh->n_io_port,"tmscsim"); + + ioport = (USHORT) io_port; + + pACB = (PACB) psh->hostdata; + bval = SEL_TIMEOUT; /* 250ms selection timeout */ + outb(bval,ioport+Scsi_TimeOut); + + bval = CLK_FREQ_40MHZ; /* Conversion factor = 0 , 40MHz clock */ + outb(bval,ioport+Clk_Factor); + + bval = NOP_CMD; /* NOP cmd - clear command register */ + outb(bval,ioport+ScsiCmd); + + bval = EN_FEATURE+EN_SCSI2_CMD; /* Enable Feature and SCSI-2 */ + outb(bval,ioport+CtrlReg2); + + bval = FAST_CLK; /* fast clock */ + outb(bval,ioport+CtrlReg3); + + bval = EATER_25NS; + if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION ) + bval |= NEGATE_REQACKDATA; + outb(bval,ioport+CtrlReg4); + + bval = DIS_INT_ON_SCSI_RST; /* Disable SCSI bus reset interrupt */ + outb(bval,ioport+CtrlReg1); + + return(0); +} + + +void +DC390_EnableCfg( USHORT mechnum, UCHAR regval ) +{ + ULONG wlval; + + if(mechnum == 2) + { + outb(mech2bus, PCI_CFG2_FORWARD_REG); + outb(mech2CfgSPenR, PCI_CFG2_ENABLE_REG); + } + else + { + regval &= 0xFC; + wlval = mech1addr; + wlval |= (((ULONG)regval) & 0xff); + outl(wlval, PCI_CFG1_ADDRESS_REG); + } +} + + +void +DC390_DisableCfg( USHORT mechnum ) +{ + + if(mechnum == 2) + outb(0, PCI_CFG2_ENABLE_REG); + else + outl(0, PCI_CFG1_ADDRESS_REG); +} + + +UCHAR +DC390_inByte( USHORT mechnum, UCHAR regval ) +{ + UCHAR bval; + ULONG wval; + ULONG flags; + + save_flags(flags); + cli(); + DC390_EnableCfg( mechnum, regval ); + if(mechnum == 2) + { + wval = mech2Agent; + wval <<= 8; + wval |= ((USHORT) regval) & 0xff; + bval = inb(wval); + } + else + { + regval &= 3; + bval = inb(PCI_CFG1_DATA_REG | regval); + } + DC390_DisableCfg(mechnum); + restore_flags(flags); + return(bval); +} + + +USHORT +DC390_inWord( USHORT mechnum, UCHAR regval ) +{ + USHORT wval; + ULONG flags; + + save_flags(flags); + cli(); + DC390_EnableCfg(mechnum,regval); + if(mechnum == 2) + { + wval = mech2Agent; + wval <<= 8; + wval |= regval; + wval = inw(wval); + } + else + { + regval &= 3; + wval = inw(PCI_CFG1_DATA_REG | regval); + } + DC390_DisableCfg(mechnum); + restore_flags(flags); + return(wval); +} + + +ULONG +DC390_inDword(USHORT mechnum, UCHAR regval ) +{ + ULONG wlval; + ULONG flags; + USHORT wval; + + save_flags(flags); + cli(); + DC390_EnableCfg(mechnum,regval); + if(mechnum == 2) + { + wval = mech2Agent; + wval <<= 8; + wval |= regval; + wlval = inl(wval); + } + else + { + wlval = inl(PCI_CFG1_DATA_REG); + } + DC390_DisableCfg(mechnum); + restore_flags(flags); + return(wlval); +} + + +void +DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval ) +{ + + USHORT wval; + ULONG flags; + + save_flags(flags); + cli(); + DC390_EnableCfg(mechnum,regval); + if(mechnum == 2) + { + wval = mech2Agent; + wval <<= 8; + wval |= regval; + outb(bval, wval); + } + else + { + regval &= 3; + outb(bval, PCI_CFG1_DATA_REG | regval); + } + DC390_DisableCfg(mechnum); + restore_flags(flags); +} + + +void +DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval ) +{ + + UCHAR bval; + + bval = 0; + if(mode == ENABLE_CE) + *regval = 0xc0; + else + *regval = 0x80; + DC390_OutB(mechnum,*regval,bval); + if(mode == DISABLE_CE) + DC390_OutB(mechnum,*regval,bval); + udelay(160); +} + + +void +DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry ) +{ + UCHAR bval; + + bval = 0; + if(Carry) + { + bval = 0x40; + *regval = 0x80; + DC390_OutB(mechnum,*regval,bval); + } + udelay(160); + bval |= 0x80; + DC390_OutB(mechnum,*regval,bval); + udelay(160); + bval = 0; + DC390_OutB(mechnum,*regval,bval); + udelay(160); +} + + +UCHAR +DC390_EEpromInDO( USHORT mechnum ) +{ + UCHAR bval,regval; + + regval = 0x80; + bval = 0x80; + DC390_OutB(mechnum,regval,bval); + udelay(160); + bval = 0x40; + DC390_OutB(mechnum,regval,bval); + udelay(160); + regval = 0x0; + bval = DC390_inByte(mechnum,regval); + if(bval == 0x22) + return(1); + else + return(0); +} + + +USHORT +EEpromGetData1( USHORT mechnum ) +{ + UCHAR i; + UCHAR carryFlag; + USHORT wval; + + wval = 0; + for(i=0; i<16; i++) + { + wval <<= 1; + carryFlag = DC390_EEpromInDO(mechnum); + wval |= carryFlag; + } + return(wval); +} + + +void +DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd ) +{ + UCHAR i,j; + USHORT carryFlag; + + carryFlag = 1; + j = 0x80; + for(i=0; i<9; i++) + { + DC390_EEpromOutDI(mechnum,regval,carryFlag); + carryFlag = (EEpromCmd & j) ? 1 : 0; + j >>= 1; + } +} + + +void +DC390_ReadEEprom( USHORT mechnum, USHORT index ) +{ + UCHAR regval,cmd; + PUSHORT ptr; + USHORT i; + + ptr = (PUSHORT) &eepromBuf[index][0]; + cmd = EEPROM_READ; + for(i=0; i<0x40; i++) + { + DC390_EnDisableCE(ENABLE_CE, mechnum, ®val); + DC390_Prepare(mechnum, ®val, cmd); + *ptr = EEpromGetData1(mechnum); + ptr++; + cmd++; + DC390_EnDisableCE(DISABLE_CE,mechnum,®val); + } +} + + +USHORT +DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index ) +{ + USHORT wval, rc, *ptr; + UCHAR i; + + DC390_ReadEEprom( MechNum, index ); + wval = 0; + ptr = (PUSHORT) &eepromBuf[index][0]; + for(i=0; i<128 ;i+=2, ptr++) + wval += *ptr; + if( wval == 0x1234 ) + rc = 0; + else + rc = -1; + return( rc ); +} + + +USHORT +DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum ) +{ + USHORT devnum; + + devnum = BusDevFunNum; + + if(Mechnum == 2) + { + if(devnum & 0x80) + return(-1); + mech2bus = (UCHAR)((devnum & 0xff00) >> 8); /* Bus num */ + mech2Agent = ((UCHAR)(devnum & 0xff)) >> 3; /* Dev num */ + mech2Agent |= 0xc0; + mech2CfgSPenR = ((UCHAR)(devnum & 0xff)) & 0x07; /* Fun num */ + mech2CfgSPenR = (mech2CfgSPenR << 1) | 0x20; + } + else /* use mech #1 method */ + { + mech1addr = 0x80000000 | ((ULONG)devnum << 8); + } + return(0); +} + +/*********************************************************************** + * Function : static int DC390_init (struct Scsi_Host *host) + * + * Purpose : initialize the internal structures for a given SCSI host + * + * Inputs : host - pointer to this host adapter's structure/ + * + * Preconditions : when this function is called, the chip_type + * field of the pACB structure MUST have been set. + ***********************************************************************/ + +static int +DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum) +{ + PSH psh; + PACB pACB; + + if( !DC390_CheckEEpromCheckSum( MechNum, index) ) + { + psh = scsi_register( psht, sizeof(DC390_ACB) ); + if( !psh ) + return( -1 ); + if( !pSH_start ) + { + pSH_start = psh; + pSH_current = psh; + } + else + { + pSH_current->next = psh; + pSH_current = psh; + } + +#ifdef DC390_DEBUG0 + printk("DC390: pSH = %8x,", (UINT) psh); + printk("DC390: Index %02i,", index); +#endif + + DC390_initACB( psh, io_port, Irq, index ); + if( !DC390_initAdapter( psh, io_port, Irq, index ) ) + { + pACB = (PACB) psh->hostdata; + if( !pACB_start ) + { + pACB_start = pACB; + pACB_current = pACB; + pACB->pNextACB = (PACB) -1; + } + else + { + pACB_current->pNextACB = pACB; + pACB_current = pACB; + pACB->pNextACB = (PACB) -1; + } + +#ifdef DC390_DEBUG0 + printk("DC390: pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n", + (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array); + printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n", + sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) ); +#endif + + } + else + { + pSH_start = NULL; + scsi_unregister( psh ); + return( -1 ); + } + return( 0 ); + } + else + { + printk("DC390_init: EEPROM reading error!\n"); + return( -1 ); + } +} + + +/*********************************************************************** + * Function : int DC390_detect(Scsi_Host_Template *psht) + * + * Purpose : detects and initializes AMD53C974 SCSI chips + * that were autoprobed, overridden on the LILO command line, + * or specified at compile time. + * + * Inputs : psht - template for this SCSI adapter + * + * Returns : number of host adapters detected + * + ***********************************************************************/ + +int +DC390_detect(Scsi_Host_Template *psht) +{ +#ifdef FOR_PCI_OK + UCHAR pci_bus, pci_device_fn; + int error = 0; + USHORT chipType = 0; + USHORT i; +#endif + + UCHAR irq; + UCHAR istatus; +#ifndef VERSION_ELF_1_2_13 + UINT io_port; +#else + ULONG io_port; +#endif + USHORT adaptCnt = 0; /* Number of boards detected */ + USHORT pci_index = 0; /* Device index to PCI BIOS calls */ + USHORT MechNum, BusDevFunNum; + ULONG wlval; + +#ifndef VERSION_ELF_1_2_13 + psht->proc_dir = &proc_scsi_tmscsim; +#endif + + InitialTime = 1; + pSHT_start = psht; + pACB_start = NULL; + + MechNum = 1; + for( ; (MechNum < 3) && (!adaptCnt); MechNum++) + { + BusDevFunNum = 0; + for (; adaptCnt < MAX_ADAPTER_NUM ;) + { + if( !DC390_ToMech( MechNum, BusDevFunNum) ) + { + wlval = DC390_inDword( MechNum, PCI_VENDOR_ID); + if(wlval == ( (PCI_DEVICE_ID_AMD53C974 << 16)+ + PCI_VENDOR_ID_AMD) ) + { + io_port =DC390_inDword(MechNum,PCI_BASE_ADDRESS_0) & 0xFFFE; + irq = DC390_inByte( MechNum, PCI_INTERRUPT_LINE); +#ifdef DC390_DEBUG0 + printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq); +#endif + if( !DC390_init(psht, io_port, irq, pci_index, MechNum) ) + { + adaptCnt++; + pci_index++; + istatus = inb( (USHORT)io_port+INT_Status ); /* Reset Pending INT */ +#ifdef DC390_DEBUG0 + printk("DC390: Mech=%2x,\n",(UCHAR) MechNum); +#endif + } + } + } + if( BusDevFunNum != 0xfff8 ) + BusDevFunNum += 8; /* next device # */ + else + break; + } + } + +#ifdef FOR_PCI_OK + if ( pcibios_present() ) + { + for (i = 0; i < MAX_ADAPTER_NUM; ++i) + { + if( !pcibios_find_device( PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD53C974, + pci_index, &pci_bus, &pci_device_fn) ) + { + chipType = PCI_DEVICE_ID_AMD53C974; + pci_index++; + } + + if( chipType ) + { + + error = pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &io_port); + error |= pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + if( error ) + { + printk("DC390_detect: reading configuration registers error!\n"); + InitialTime = 0; + return( 0 ); + } + + (USHORT) io_port = (USHORT) io_port & 0xFFFE; +#ifdef DC390_DEBUG0 + printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq); +#endif + if( !DC390_init(psht, io_port, irq, i) ) + adaptCnt++; + chipType = 0; + } + else + break; + } + } +#endif + + InitialTime = 0; + adapterCnt = adaptCnt; + return( adaptCnt ); +} + + +#ifndef VERSION_ELF_1_2_13 + +/******************************************************************** + * Function: tmscsim_set_info() + * + * Purpose: Set adapter info (!) + * + * Not yet implemented + * + *******************************************************************/ + +int tmscsim_set_info(char *buffer, int length, struct Scsi_Host *shpnt) +{ + return(-ENOSYS); /* Currently this is a no-op */ +} + +/******************************************************************** + * Function: tmscsim_proc_info(char* buffer, char **start, + * off_t offset, int length, int hostno, int inout) + * + * Purpose: return SCSI Adapter/Device Info + * + * Input: buffer: Pointer to a buffer where to write info + * start : + * offset: + * hostno: Host adapter index + * inout : Read (=0) or set(!=0) info + * + * Output: buffer: contains info + * length; length of info in buffer + * + * return value: length + * + ********************************************************************/ + +/* KG: proc_info taken from driver aha152x.c */ + +#undef SPRINTF +#define SPRINTF(args...) pos += sprintf(pos, ## args) + +#define YESNO(YN)\ +if (YN) SPRINTF(" Yes ");\ +else SPRINTF(" No ") + +int tmscsim_proc_info(char *buffer, char **start, + off_t offset, int length, int hostno, int inout) +{ + int dev, spd, spd1; + char *pos = buffer; + PSH shpnt; + PACB acbpnt; + PDCB dcbpnt; + unsigned long flags; +/* Scsi_Cmnd *ptr; */ + + acbpnt = pACB_start; + + while(acbpnt != (PACB)-1) + { + shpnt = acbpnt->pScsiHost; + if (shpnt->host_no == hostno) break; + acbpnt = acbpnt->pNextACB; + } + + if (acbpnt == (PACB)-1) return(-ESRCH); + if(!shpnt) return(-ESRCH); + + if(inout) // Has data been written to the file ? + return(tmscsim_set_info(buffer, length, shpnt)); + + SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, "); + SPRINTF("Driver Version 1.10, 1996/12/05\n"); + + save_flags(flags); + cli(); + + SPRINTF("SCSI Host Nr %i, ", shpnt->host_no); + SPRINTF("DC390 Adapter Nr %i\n", acbpnt->AdapterIndex); + SPRINTF("IOPortBase 0x%04x, ", acbpnt->IOPortBase); + SPRINTF("IRQLevel 0x%02x\n", acbpnt->IRQLevel); + + SPRINTF("MaxID %i, MaxLUN %i, ",acbpnt->max_id, acbpnt->max_lun); + SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN); + + SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status); + + SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt); + + SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs\n"); + + dcbpnt = acbpnt->pLinkDCB; + for (dev = 0; dev < acbpnt->DeviceCnt; dev++) + { + SPRINTF("%02i %02i %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN); + YESNO(dcbpnt->DevMode & PARITY_CHK_); + YESNO(dcbpnt->SyncMode & SYNC_NEGO_DONE); + YESNO(dcbpnt->DevMode & EN_DISCONNECT_); + YESNO(dcbpnt->DevMode & SEND_START_); + YESNO(dcbpnt->SyncMode & EN_TAG_QUEUING); + SPRINTF(" %03i ns ", (dcbpnt->NegoPeriod) << 2); + if (dcbpnt->SyncOffset & 0x0f) + { + spd = 1000/(dcbpnt->NegoPeriod <<2); + spd1 = 1000%(dcbpnt->NegoPeriod <<2); + spd1 = (spd1 * 10)/(dcbpnt->NegoPeriod <<2); + SPRINTF(" %2i.%1i M %02i\n", spd, spd1, (dcbpnt->SyncOffset & 0x0f)); + } + else SPRINTF("\n"); + /* Add more info ...*/ + dcbpnt = dcbpnt->pNextDCB; + } + + restore_flags(flags); + *start = buffer + offset; + + if (pos - buffer < offset) + return 0; + else if (pos - buffer - offset < length) + return pos - buffer - offset; + else + return length; +} +#endif /* VERSION_ELF_1_2_13 */ + + +#ifdef MODULE + +/*********************************************************************** + * Function : static int DC390_shutdown (struct Scsi_Host *host) + * + * Purpose : does a clean (we hope) shutdown of the SCSI chip. + * Use prior to dumping core, unloading the driver, etc. + * + * Returns : 0 on success + ***********************************************************************/ +static int +DC390_shutdown (struct Scsi_Host *host) +{ + UCHAR bval; + USHORT ioport; + unsigned long flags; + PACB pACB = (PACB)(host->hostdata); + + ioport = (unsigned int) pACB->IOPortBase; + + save_flags (flags); + cli(); + +/* pACB->soft_reset(host); */ + +#ifdef DC390_DEBUG0 + printk("DC390: shutdown,"); +#endif + + bval = inb(ioport+CtrlReg1); + bval |= DIS_INT_ON_SCSI_RST; + outb(bval,ioport+CtrlReg1); /* disable interrupt */ + DC390_ResetSCSIBus( pACB ); + + restore_flags (flags); + return( 0 ); +} + + +int DC390_release(struct Scsi_Host *host) +{ + int irq_count; + struct Scsi_Host *tmp; + + DC390_shutdown (host); + + if (host->irq != IRQ_NONE) + { + for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next) + { + if ( tmp->irq == host->irq ) + ++irq_count; + } + if (irq_count == 1) + { +#ifdef DC390_DEBUG0 + printk("DC390: Free IRQ %i.",host->irq); +#endif +#ifndef VERSION_ELF_1_2_13 + free_irq(host->irq,NULL); +#else + free_irq(host->irq); +#endif + } + } + + release_region(host->io_port,host->n_io_port); + + return( 1 ); +} + +Scsi_Host_Template driver_template = DC390_T; +#include "scsi_module.c" +#endif /* def MODULE */ + diff --git a/drivers/scsi/tmscsim.h b/drivers/scsi/tmscsim.h new file mode 100644 index 000000000000..361c488acb58 --- /dev/null +++ b/drivers/scsi/tmscsim.h @@ -0,0 +1,680 @@ +/*********************************************************************** +;* File Name : TMSCSIM.H * +;* TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter * +;* Device Driver * +;***********************************************************************/ + +#ifndef TMSCSIM_H +#define TMSCSIM_H + +#define IRQ_NONE 255 + +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef unsigned long ULONG; +typedef unsigned int UINT; + +typedef UCHAR *PUCHAR; +typedef USHORT *PUSHORT; +typedef ULONG *PULONG; +typedef Scsi_Host_Template *PSHT; +typedef struct Scsi_Host *PSH; +typedef Scsi_Device *PSCSIDEV; +typedef Scsi_Cmnd *PSCSICMD; +typedef void *PVOID; +typedef struct scatterlist *PSGL, SGL; + + +/*;-----------------------------------------------------------------------*/ +typedef struct _SyncMsg +{ +UCHAR ExtendMsg; +UCHAR ExtMsgLen; +UCHAR SyncXferReq; +UCHAR Period; +UCHAR ReqOffset; +} SyncMsg; +/*;-----------------------------------------------------------------------*/ +typedef struct _Capacity +{ +ULONG BlockCount; +ULONG BlockLength; +} Capacity; +/*;-----------------------------------------------------------------------*/ +typedef struct _SGentry +{ +ULONG SGXferDataPtr; +ULONG SGXferDataLen; +} SGentry; + +typedef struct _SGentry1 +{ +ULONG SGXLen; +ULONG SGXPtr; +} SGentry1, *PSGE; + + +#define MAX_ADAPTER_NUM 4 +#define MAX_DEVICES 10 +#define MAX_SG_LIST_BUF 16 +#define MAX_CMD_QUEUE 20 +#define MAX_CMD_PER_LUN 8 +#define MAX_SCSI_ID 8 +#define MAX_SRB_CNT MAX_CMD_QUEUE+4 +#define END_SCAN 2 + +#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */ + +/* +;----------------------------------------------------------------------- +; SCSI Request Block +;----------------------------------------------------------------------- +*/ +struct _SRB +{ +UCHAR CmdBlock[12]; + +struct _SRB *pNextSRB; +struct _DCB *pSRBDCB; +PSCSICMD pcmd; +PSGL pSegmentList; + +ULONG PhysSRB; +ULONG TotalXferredLen; +ULONG SGPhysAddr; /*;a segment starting address */ +ULONG SGToBeXferLen; /*; to be xfer length */ + +SGL Segmentx; /* make a one entry of S/G list table */ + +PUCHAR pMsgPtr; +USHORT SRBState; +USHORT Revxx2; /* ??? */ + +UCHAR MsgInBuf[6]; +UCHAR MsgOutBuf[6]; + +UCHAR AdaptStatus; +UCHAR TargetStatus; +UCHAR MsgCnt; +UCHAR EndMessage; +UCHAR TagNumber; +UCHAR SGcount; +UCHAR SGIndex; +UCHAR IORBFlag; /*;81h-Reset, 2-retry */ + +UCHAR SRBStatus; +UCHAR RetryCnt; +UCHAR SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */ + /*; b4-settimeout,b5-Residual valid */ +UCHAR ScsiCmdLen; +UCHAR ScsiPhase; +UCHAR Reserved3[3]; /*;for dword alignment */ +ULONG Segment0[2]; +ULONG Segment1[2]; +}; + +typedef struct _SRB DC390_SRB, *PSRB; + +/* +;----------------------------------------------------------------------- +; Device Control Block +;----------------------------------------------------------------------- +*/ +struct _DCB +{ +struct _DCB *pNextDCB; +struct _ACB *pDCBACB; + +PSCSICMD pQIORBhead; +PSCSICMD pQIORBtail; +PSCSICMD AboIORBhead; +PSCSICMD AboIORBtail; +USHORT QIORBCnt; +USHORT AboIORBcnt; + +PSRB pWaitingSRB; +PSRB pWaitLast; +PSRB pGoingSRB; +PSRB pGoingLast; +PSRB pActiveSRB; +USHORT GoingSRBCnt; +USHORT WaitSRBCnt; /* ??? */ + +ULONG TagMask; + +USHORT MaxCommand; +USHORT AdaptIndex; /*; UnitInfo struc start */ +USHORT UnitIndex; /*; nth Unit on this card */ +UCHAR UnitSCSIID; /*; SCSI Target ID (SCSI Only) */ +UCHAR UnitSCSILUN; /*; SCSI Log. Unit (SCSI Only) */ + +UCHAR IdentifyMsg; +UCHAR CtrlR1; +UCHAR CtrlR3; +UCHAR CtrlR4; + +UCHAR InqDataBuf[8]; +UCHAR CapacityBuf[8]; +UCHAR DevMode; +UCHAR AdpMode; +UCHAR SyncMode; /*; 0:async mode */ +UCHAR NegoPeriod; /*;for nego. */ +UCHAR SyncPeriod; /*;for reg. */ +UCHAR SyncOffset; /*;for reg. and nego.(low nibble) */ +UCHAR UnitCtrlFlag; +UCHAR DCBFlag; +UCHAR DevType; +UCHAR Reserved2[3]; /*;for dword alignment */ +}; + +typedef struct _DCB DC390_DCB, *PDCB; +/* +;----------------------------------------------------------------------- +; Adapter Control Block +;----------------------------------------------------------------------- +*/ +struct _ACB +{ +ULONG PhysACB; +PSH pScsiHost; +struct _ACB *pNextACB; +USHORT IOPortBase; +USHORT Revxx1; /* ??? */ + +PDCB pLinkDCB; +PDCB pDCBRunRobin; +PDCB pActiveDCB; +PDCB pDCB_free; +PSRB pFreeSRB; +PSRB pTmpSRB; +USHORT SRBCount; +USHORT AdapterIndex; /*; nth Adapter this driver */ +USHORT max_id; +USHORT max_lun; + +UCHAR msgin123[4]; +UCHAR status; +UCHAR AdaptSCSIID; /*; Adapter SCSI Target ID */ +UCHAR AdaptSCSILUN; /*; Adapter SCSI LUN */ +UCHAR DeviceCnt; +UCHAR IRQLevel; +UCHAR TagMaxNum; +UCHAR ACBFlag; +UCHAR Gmode2; +UCHAR LUNchk; +UCHAR scan_devices; +UCHAR HostID_Bit; +UCHAR Reserved1[1]; /*;for dword alignment */ +UCHAR DCBmap[MAX_SCSI_ID]; +DC390_DCB DCB_array[MAX_DEVICES]; /* +74h, Len=3E8 */ +DC390_SRB SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */ +DC390_SRB TmpSRB; +}; + +typedef struct _ACB DC390_ACB, *PACB; + +/*;-----------------------------------------------------------------------*/ + + +#define BIT31 0x80000000 +#define BIT30 0x40000000 +#define BIT29 0x20000000 +#define BIT28 0x10000000 +#define BIT27 0x08000000 +#define BIT26 0x04000000 +#define BIT25 0x02000000 +#define BIT24 0x01000000 +#define BIT23 0x00800000 +#define BIT22 0x00400000 +#define BIT21 0x00200000 +#define BIT20 0x00100000 +#define BIT19 0x00080000 +#define BIT18 0x00040000 +#define BIT17 0x00020000 +#define BIT16 0x00010000 +#define BIT15 0x00008000 +#define BIT14 0x00004000 +#define BIT13 0x00002000 +#define BIT12 0x00001000 +#define BIT11 0x00000800 +#define BIT10 0x00000400 +#define BIT9 0x00000200 +#define BIT8 0x00000100 +#define BIT7 0x00000080 +#define BIT6 0x00000040 +#define BIT5 0x00000020 +#define BIT4 0x00000010 +#define BIT3 0x00000008 +#define BIT2 0x00000004 +#define BIT1 0x00000002 +#define BIT0 0x00000001 + +/*;---UnitCtrlFlag */ +#define UNIT_ALLOCATED BIT0 +#define UNIT_INFO_CHANGED BIT1 +#define FORMATING_MEDIA BIT2 +#define UNIT_RETRY BIT3 + +/*;---UnitFlags */ +#define DASD_SUPPORT BIT0 +#define SCSI_SUPPORT BIT1 +#define ASPI_SUPPORT BIT2 + +/*;----SRBState machine definition */ +#define SRB_FREE 0 +#define SRB_WAIT BIT0 +#define SRB_READY BIT1 +#define SRB_MSGOUT BIT2 /*;arbitration+msg_out 1st byte*/ +#define SRB_MSGIN BIT3 +#define SRB_MSGIN_MULTI BIT4 +#define SRB_COMMAND BIT5 +#define SRB_START_ BIT6 /*;arbitration+msg_out+command_out*/ +#define SRB_DISCONNECT BIT7 +#define SRB_DATA_XFER BIT8 +#define SRB_XFERPAD BIT9 +#define SRB_STATUS BIT10 +#define SRB_COMPLETED BIT11 +#define SRB_ABORT_SENT BIT12 +#define DO_SYNC_NEGO BIT13 +#define SRB_UNEXPECT_RESEL BIT14 + +/*;---ACBFlag */ +#define RESET_DEV BIT0 +#define RESET_DETECT BIT1 +#define RESET_DONE BIT2 + +/*;---DCBFlag */ +#define ABORT_DEV_ BIT0 + +/*;---SRBstatus */ +#define SRB_OK BIT0 +#define ABORTION BIT1 +#define OVER_RUN BIT2 +#define UNDER_RUN BIT3 +#define PARITY_ERROR BIT4 +#define SRB_ERROR BIT5 + +/*;---SRBFlag */ +#define DATAOUT BIT7 +#define DATAIN BIT6 +#define RESIDUAL_VALID BIT5 +#define ENABLE_TIMER BIT4 +#define RESET_DEV0 BIT2 +#define ABORT_DEV BIT1 +#define AUTO_REQSENSE BIT0 + +/*;---Adapter status */ +#define H_STATUS_GOOD 0 +#define H_SEL_TIMEOUT 0x11 +#define H_OVER_UNDER_RUN 0x12 +#define H_UNEXP_BUS_FREE 0x13 +#define H_TARGET_PHASE_F 0x14 +#define H_INVALID_CCB_OP 0x16 +#define H_LINK_CCB_BAD 0x17 +#define H_BAD_TARGET_DIR 0x18 +#define H_DUPLICATE_CCB 0x19 +#define H_BAD_CCB_OR_SG 0x1A +#define H_ABORT 0x0FF + +/*; SCSI Status byte codes*/ +#define SCSI_STAT_GOOD 0x0 /*; Good status */ +#define SCSI_STAT_CHECKCOND 0x02 /*; SCSI Check Condition */ +#define SCSI_STAT_CONDMET 0x04 /*; Condition Met */ +#define SCSI_STAT_BUSY 0x08 /*; Target busy status */ +#define SCSI_STAT_INTER 0x10 /*; Intermediate status */ +#define SCSI_STAT_INTERCONDMET 0x14 /*; Intermediate condition met */ +#define SCSI_STAT_RESCONFLICT 0x18 /*; Reservation conflict */ +#define SCSI_STAT_CMDTERM 0x22 /*; Command Terminated */ +#define SCSI_STAT_QUEUEFULL 0x28 /*; Queue Full */ + +#define SCSI_STAT_UNEXP_BUS_F 0xFD /*; Unexpect Bus Free */ +#define SCSI_STAT_BUS_RST_DETECT 0xFE /*; Scsi Bus Reset detected */ +#define SCSI_STAT_SEL_TIMEOUT 0xFF /*; Selection Time out */ + +/*;---Sync_Mode */ +#define SYNC_DISABLE 0 +#define SYNC_ENABLE BIT0 +#define SYNC_NEGO_DONE BIT1 +#define WIDE_ENABLE BIT2 +#define WIDE_NEGO_DONE BIT3 +#define EN_TAG_QUEUING BIT4 +#define EN_ATN_STOP BIT5 + +#define SYNC_NEGO_OFFSET 15 + +/*;---SCSI bus phase*/ +#define SCSI_DATA_OUT 0 +#define SCSI_DATA_IN 1 +#define SCSI_COMMAND 2 +#define SCSI_STATUS_ 3 +#define SCSI_NOP0 4 +#define SCSI_NOP1 5 +#define SCSI_MSG_OUT 6 +#define SCSI_MSG_IN 7 + +/*;----SCSI MSG BYTE*/ +#define MSG_COMPLETE 0x00 +#define MSG_EXTENDED 0x01 +#define MSG_SAVE_PTR 0x02 +#define MSG_RESTORE_PTR 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_INITIATOR_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT_ 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_LINK_CMD_COMPL 0x0A +#define MSG_LINK_CMD_COMPL_FLG 0x0B +#define MSG_BUS_RESET 0x0C +#define MSG_ABORT_TAG 0x0D +#define MSG_SIMPLE_QTAG 0x20 +#define MSG_HEAD_QTAG 0x21 +#define MSG_ORDER_QTAG 0x22 +#define MSG_IDENTIFY 0x80 +#define MSG_HOST_ID 0x0C0 + +/*;----SCSI STATUS BYTE*/ +#define STATUS_GOOD 0x00 +#define CHECK_CONDITION_ 0x02 +#define STATUS_BUSY 0x08 +#define STATUS_INTERMEDIATE 0x10 +#define RESERVE_CONFLICT 0x18 + +/* cmd->result */ +#define STATUS_MASK_ 0xFF +#define MSG_MASK 0xFF00 +#define RETURN_MASK 0xFF0000 + +/* +** Inquiry Data format +*/ + +typedef struct _SCSIInqData { /* INQ */ + + UCHAR DevType; /* Periph Qualifier & Periph Dev Type*/ + UCHAR RMB_TypeMod; /* rem media bit & Dev Type Modifier */ + UCHAR Vers; /* ISO, ECMA, & ANSI versions */ + UCHAR RDF; /* AEN, TRMIOP, & response data format*/ + UCHAR AddLen; /* length of additional data */ + UCHAR Res1; /* reserved */ + UCHAR Res2; /* reserved */ + UCHAR Flags; /* RelADr,Wbus32,Wbus16,Sync,etc. */ + UCHAR VendorID[8]; /* Vendor Identification */ + UCHAR ProductID[16]; /* Product Identification */ + UCHAR ProductRev[4]; /* Product Revision */ + + +} SCSI_INQDATA, *PSCSI_INQDATA; + + +/* Inquiry byte 0 masks */ + + +#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */ +#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */ + + +/* Inquiry byte 1 mask */ + +#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */ + + +/* Peripheral Device Type definitions */ + +#define SCSI_DASD 0x00 /* Direct-access Device */ +#define SCSI_SEQACESS 0x01 /* Sequential-access device */ +#define SCSI_PRINTER 0x02 /* Printer device */ +#define SCSI_PROCESSOR 0x03 /* Processor device */ +#define SCSI_WRITEONCE 0x04 /* Write-once device */ +#define SCSI_CDROM 0x05 /* CD-ROM device */ +#define SCSI_SCANNER 0x06 /* Scanner device */ +#define SCSI_OPTICAL 0x07 /* Optical memory device */ +#define SCSI_MEDCHGR 0x08 /* Medium changer device */ +#define SCSI_COMM 0x09 /* Communications device */ +#define SCSI_NODEV 0x1F /* Unknown or no device type */ + +/* +** Inquiry flag definitions (Inq data byte 7) +*/ + +#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing*/ +#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */ +#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */ +#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */ +#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */ +#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */ +#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */ + + +/* +;========================================================== +; EEPROM byte offset +;========================================================== +*/ +typedef struct _EEprom +{ +UCHAR EE_MODE1; +UCHAR EE_SPEED; +UCHAR xx1; +UCHAR xx2; +} EEprom, *PEEprom; + +#define EE_ADAPT_SCSI_ID 64 +#define EE_MODE2 65 +#define EE_DELAY 66 +#define EE_TAG_CMD_NUM 67 + +/*; EE_MODE1 bits definition*/ +#define PARITY_CHK_ BIT0 +#define SYNC_NEGO_ BIT1 +#define EN_DISCONNECT_ BIT2 +#define SEND_START_ BIT3 +#define TAG_QUEUING_ BIT4 + +/*; EE_MODE2 bits definition*/ +#define MORE2_DRV BIT0 +#define GREATER_1G BIT1 +#define RST_SCSI_BUS BIT2 +#define ACTIVE_NEGATION BIT3 +#define NO_SEEK BIT4 +#define LUN_CHECK BIT5 + +#define ENABLE_CE 1 +#define DISABLE_CE 0 +#define EEPROM_READ 0x80 + +/* +;========================================================== +; AMD 53C974 Registers bit Definition +;========================================================== +*/ +/* +;==================== +; SCSI Register +;==================== +*/ + +/*; Command Reg.(+0CH) */ +#define DMA_COMMAND BIT7 +#define NOP_CMD 0 +#define CLEAR_FIFO_CMD 1 +#define RST_DEVICE_CMD 2 +#define RST_SCSI_BUS_CMD 3 +#define INFO_XFER_CMD 0x10 +#define INITIATOR_CMD_CMPLTE 0x11 +#define MSG_ACCEPTED_CMD 0x12 +#define XFER_PAD_BYTE 0x18 +#define SET_ATN_CMD 0x1A +#define RESET_ATN_CMD 0x1B +#define SELECT_W_ATN 0x42 +#define SEL_W_ATN_STOP 0x43 +#define EN_SEL_RESEL 0x44 +#define SEL_W_ATN2 0x46 +#define DATA_XFER_CMD INFO_XFER_CMD + + +/*; SCSI Status Reg.(+10H) */ +#define INTERRUPT BIT7 +#define ILLEGAL_OP_ERR BIT6 +#define PARITY_ERR BIT5 +#define COUNT_2_ZERO BIT4 +#define GROUP_CODE_VALID BIT3 +#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0) + +/*; Interrupt Status Reg.(+14H) */ +#define SCSI_RESET BIT7 +#define INVALID_CMD BIT6 +#define DISCONNECTED BIT5 +#define SERVICE_REQUEST BIT4 +#define SUCCESSFUL_OP BIT3 +#define RESELECTED BIT2 +#define SEL_ATTENTION BIT1 +#define SELECTED BIT0 + +/*; Internal State Reg.(+18H) */ +#define SYNC_OFFSET_FLAG BIT3 +#define INTRN_STATE_MASK (BIT2+BIT1+BIT0) + +/*; Clock Factor Reg.(+24H) */ +#define CLK_FREQ_40MHZ 0 +#define CLK_FREQ_35MHZ (BIT2+BIT1+BIT0) +#define CLK_FREQ_30MHZ (BIT2+BIT1) +#define CLK_FREQ_25MHZ (BIT2+BIT0) +#define CLK_FREQ_20MHZ BIT2 +#define CLK_FREQ_15MHZ (BIT1+BIT0) +#define CLK_FREQ_10MHZ BIT1 + +/*; Control Reg. 1(+20H) */ +#define EXTENDED_TIMING BIT7 +#define DIS_INT_ON_SCSI_RST BIT6 +#define PARITY_ERR_REPO BIT4 +#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0) + +/*; Control Reg. 2(+2CH) */ +#define EN_FEATURE BIT6 +#define EN_SCSI2_CMD BIT3 + +/*; Control Reg. 3(+30H) */ +#define ID_MSG_CHECK BIT7 +#define EN_QTAG_MSG BIT6 +#define EN_GRP2_CMD BIT5 +#define FAST_SCSI BIT4 /* ;10MB/SEC */ +#define FAST_CLK BIT3 /* ;25 - 40 MHZ */ + +/*; Control Reg. 4(+34H) */ +#define EATER_12NS 0 +#define EATER_25NS BIT7 +#define EATER_35NS BIT6 +#define EATER_0NS (BIT7+BIT6) +#define NEGATE_REQACKDATA BIT2 +#define NEGATE_REQACK BIT3 +/* +;==================== +; DMA Register +;==================== +*/ +/*; DMA Command Reg.(+40H) */ +#define READ_DIRECTION BIT7 +#define WRITE_DIRECTION 0 +#define EN_DMA_INT BIT6 +#define MAP_TO_MDL BIT5 +#define DIAGNOSTIC BIT4 +#define DMA_IDLE_CMD 0 +#define DMA_BLAST_CMD BIT0 +#define DMA_ABORT_CMD BIT1 +#define DMA_START_CMD (BIT1+BIT0) + +/*; DMA Status Reg.(+54H) */ +#define PCI_MS_ABORT BIT6 +#define BLAST_COMPLETE BIT5 +#define SCSI_INTERRUPT BIT4 +#define DMA_XFER_DONE BIT3 +#define DMA_XFER_ABORT BIT2 +#define DMA_XFER_ERROR BIT1 +#define POWER_DOWN BIT0 + +/* +; DMA SCSI Bus and Ctrl.(+70H) +;EN_INT_ON_PCI_ABORT +*/ + +/* +;========================================================== +; SCSI Chip register address offset +;========================================================== +*/ +#define CtcReg_Low 0x00 +#define CtcReg_Mid 0x04 +#define ScsiFifo 0x08 +#define ScsiCmd 0x0C +#define Scsi_Status 0x10 +#define INT_Status 0x14 +#define Sync_Period 0x18 +#define Sync_Offset 0x1C +#define CtrlReg1 0x20 +#define Clk_Factor 0x24 +#define CtrlReg2 0x2C +#define CtrlReg3 0x30 +#define CtrlReg4 0x34 +#define CtcReg_High 0x38 +#define DMA_Cmd 0x40 +#define DMA_XferCnt 0x44 +#define DMA_XferAddr 0x48 +#define DMA_Wk_ByteCntr 0x4C +#define DMA_Wk_AddrCntr 0x50 +#define DMA_Status 0x54 +#define DMA_MDL_Addr 0x58 +#define DMA_Wk_MDL_Cntr 0x5C +#define DMA_ScsiBusCtrl 0x70 + +#define StcReg_Low CtcReg_Low +#define StcReg_Mid CtcReg_Mid +#define Scsi_Dest_ID Scsi_Status +#define Scsi_TimeOut INT_Status +#define Intern_State Sync_Period +#define Current_Fifo Sync_Offset +#define StcReg_High CtcReg_High + +#define am_target Scsi_Status +#define am_timeout INT_Status +#define am_seq_step Sync_Period +#define am_fifo_count Sync_Offset + + +#define DC390_read8(address) \ + inb(DC390_ioport + (address))) + +#define DC390_read16(address) \ + inw(DC390_ioport + (address))) + +#define DC390_read32(address) \ + inl(DC390_ioport + (address))) + +#define DC390_write8(address,value) \ + outb((value), DC390_ioport + (address))) + +#define DC390_write16(address,value) \ + outw((value), DC390_ioport + (address))) + +#define DC390_write32(address,value) \ + outl((value), DC390_ioport + (address))) + + +/* Configuration method #1 */ +#define PCI_CFG1_ADDRESS_REG 0xcf8 +#define PCI_CFG1_DATA_REG 0xcfc +#define PCI_CFG1_ENABLE 0x80000000 +#define PCI_CFG1_TUPPLE(bus, device, function, register) \ + (PCI_CFG1_ENABLE | (((bus) << 16) & 0xff0000) | \ + (((device) << 11) & 0xf800) | (((function) << 8) & 0x700)| \ + (((register) << 2) & 0xfc)) + +/* Configuration method #2 */ +#define PCI_CFG2_ENABLE_REG 0xcf8 +#define PCI_CFG2_FORWARD_REG 0xcfa +#define PCI_CFG2_ENABLE 0x0f0 +#define PCI_CFG2_TUPPLE(function) \ + (PCI_CFG2_ENABLE | (((function) << 1) & 0xe)) + + +#endif /* TMSCSIM_H */ diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index fb8a76753ce6..f7567a4962e1 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -1,15 +1,277 @@ -# -# Sound driver configuration -# -#-------- -# There is another confic script which is compatible with rest of -# the kernel. It can be activated by running 'make mkscript' in this -# directory. Please note that this is an _experimental_ feature which -# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui). -#-------- -# -$MAKE -C drivers/sound config || exit 1 +bool 'ProAudioSpectrum 16 support' CONFIG_PAS +bool '_TRUE_ Sound Blaster (SB, SBPro, SB16/32/64, ESS, Jazz16) support' CONFIG_SB +bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB +bool 'Gravis Ultrasound support' CONFIG_GUS +bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401 +bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS +bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 +bool 'GUS MAX support' CONFIG_GUSMAX +bool 'Microsoft Sound System support' CONFIG_MSS +bool 'Ensoniq SoundScape support' CONFIG_SSCAPE +bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX +bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16 +bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232 +bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI +bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 + +if [ "$CONFIG_AEDSP16" = "y" ]; then +hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'I/O base for SB Check from manual of the card' SBC_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330 +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'Enter -1 to the following question if you have something else such as SB16/32.' +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS DMA 0, 1 or 3' GUS16_DMA 3 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9 +fi + + +if [ "$CONFIG_MAUI" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with Maui.' +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330 +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +int 'UART6850 IRQ (Unknown)' U6850_IRQ -1 +fi + + +if [ "$CONFIG_PSS" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with PSS cards.' +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS I/O base 220 or 240' PSS_BASE 220 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3 +fi +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11 +fi + + +if [ "$CONFIG_TRIX" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with AudioTrix.' +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9 +fi +# +$MAKE -C drivers/sound kernelconfig || exit 1 bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then diff --git a/include/asm-alpha/io.h b/include/asm-alpha/io.h index 3084c9f26aa7..a81f5289e1df 100644 --- a/include/asm-alpha/io.h +++ b/include/asm-alpha/io.h @@ -39,7 +39,7 @@ extern struct hae { extern inline void set_hae(unsigned long new_hae) { unsigned long ipl; - swpipl(ipl,7); + ipl = swpipl(7); hae.cache = new_hae; *hae.reg = new_hae; mb(); diff --git a/include/asm-alpha/spinlock.h b/include/asm-alpha/spinlock.h index 7b6a218cf4f8..4b7e0242da75 100644 --- a/include/asm-alpha/spinlock.h +++ b/include/asm-alpha/spinlock.h @@ -15,7 +15,7 @@ typedef struct { int dummy; } spinlock_t; #define spin_lock_irq(lock) setipl(7) #define spin_unlock_irq(lock) setipl(0) -#define spin_lock_irqsave(lock, flags) swpipl(flags,7) +#define spin_lock_irqsave(lock, flags) do { (flags) = swpipl(7); } while (0) #define spin_unlock_irqrestore(lock, flags) setipl(flags) /* @@ -40,9 +40,9 @@ typedef struct { int dummy; } rwlock_t; #define write_lock_irq(lock) cli() #define write_unlock_irq(lock) sti() -#define read_lock_irqsave(lock, flags) swpipl(flags,7) +#define read_lock_irqsave(lock, flags) do { (flags) = swpipl(7); } while (0) #define read_unlock_irqrestore(lock, flags) setipl(flags) -#define write_lock_irqsave(lock, flags) swpipl(flags,7) +#define write_lock_irqsave(lock, flags) do { (flags) = swpipl(7); } while (0) #define write_unlock_irqrestore(lock, flags) setipl(flags) #else @@ -117,7 +117,7 @@ l1: do { spin_unlock(lock); __sti(); } while (0) #define spin_lock_irqsave(lock, flags) \ - do { swpipl(flags,7); spin_lock(lock); } while (0) + do { flags = swpipl(7); spin_lock(lock); } while (0) #define spin_unlock_irqrestore(lock, flags) \ do { spin_unlock(lock); setipl(flags); } while (0) diff --git a/include/asm-alpha/system.h b/include/asm-alpha/system.h index eeb9f58608c3..cde066b66aa8 100644 --- a/include/asm-alpha/system.h +++ b/include/asm-alpha/system.h @@ -62,57 +62,89 @@ extern void wrmces (unsigned long); extern void alpha_switch_to(unsigned long pctxp); -extern void imb(void); - #define mb() \ __asm__ __volatile__("mb": : :"memory") +#define imb() \ +__asm__ __volatile__ ("call_pal %0" : : "i" (PAL_imb) : "memory") + #define draina() \ __asm__ __volatile__ ("call_pal %0" : : "i" (PAL_draina) : "memory") -#define getipl(__old_ipl) \ -__asm__ __volatile__( \ - "call_pal 54\n\t" \ - "bis $0,$0,%0" \ - : "=r" (__old_ipl) \ - : : "$0", "$1", "$16", "$22", "$23", "$24", "$25") - -#define setipl(__new_ipl) \ -__asm__ __volatile__( \ - "bis %0,%0,$16\n\t" \ - "call_pal 53" \ - : : "r" (__new_ipl) \ - : "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory") - -#define swpipl(__old_ipl,__new_ipl) \ -__asm__ __volatile__( \ - "bis %1,%1,$16\n\t" \ - "call_pal 53\n\t" \ - "bis $0,$0,%0" \ - : "=r" (__old_ipl) \ - : "r" (__new_ipl) \ - : "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory") +#define call_pal1(palno,arg) \ +({ \ + register unsigned long __r0 __asm__("$0"); \ + register unsigned long __r16 __asm__("$16"); __r16 = arg; \ + __asm__ __volatile__( \ + "call_pal %3" \ + :"=r" (__r0),"=r" (__r16) \ + :"1" (__r16),"i" (palno) \ + :"$1", "$22", "$23", "$24", "$25", "memory"); \ + __r0; \ +}) + +#define getipl() \ +({ \ + register unsigned long r0 __asm__("$0"); \ + __asm__ __volatile__( \ + "call_pal %1" \ + :"=r" (r0) \ + :"i" (PAL_rdps) \ + :"$1", "$16", "$22", "$23", "$24", "$25", "memory"); \ + r0; \ +}) + +#define setipl(ipl) \ +do { \ + register unsigned long __r16 __asm__("$16") = (ipl); \ + __asm__ __volatile__( \ + "call_pal %2" \ + :"=r" (__r16) \ + :"0" (__r16),"i" (PAL_swpipl) \ + :"$0", "$1", "$22", "$23", "$24", "$25", "memory"); \ +} while (0) + +#define swpipl(ipl) \ +({ \ + register unsigned long __r0 __asm__("$0"); \ + register unsigned long __r16 __asm__("$16") = (ipl); \ + __asm__ __volatile__( \ + "call_pal %3" \ + :"=r" (__r0),"=r" (__r16) \ + :"1" (__r16),"i" (PAL_swpipl) \ + :"$1", "$22", "$23", "$24", "$25", "memory"); \ + __r0; \ +}) #define __cli() setipl(7) #define __sti() setipl(0) -#define __save_flags(flags) getipl(flags) +#define __save_flags(flags) do { (flags) = getipl(); } while (0) #define __restore_flags(flags) setipl(flags) #define cli() setipl(7) #define sti() setipl(0) -#define save_flags(flags) getipl(flags) +#define save_flags(flags) do { (flags) = getipl(); } while (0) #define restore_flags(flags) setipl(flags) /* * TB routines.. */ -extern void tbi(long type, ...); +#define __tbi(nr,arg,arg1...) do { \ + register unsigned long __r16 __asm__("$16") = (nr); \ + register unsigned long __r17 __asm__("$17"); arg; \ + __asm__ __volatile__( \ + "call_pal %3" \ + :"=r" (__r16),"=r" (__r17) \ + :"0" (__r16),"i" (PAL_tbi) ,##arg1 \ + :"$0", "$1", "$22", "$23", "$24", "$25"); \ +} while (0) -#define tbisi(x) tbi(1,(x)) -#define tbisd(x) tbi(2,(x)) -#define tbis(x) tbi(3,(x)) -#define tbiap() tbi(-1) -#define tbia() tbi(-2) +#define tbi(x,y) __tbi(x,__r17=(y),"1" (__r17)) +#define tbisi(x) __tbi(1,__r17=(x),"1" (__r17)) +#define tbisd(x) __tbi(2,__r17=(x),"1" (__r17)) +#define tbis(x) __tbi(3,__r17=(x),"1" (__r17)) +#define tbiap() __tbi(-1, /* no second argument */) +#define tbia() __tbi(-2, /* no second argument */) /* * Give prototypes to shut up gcc. diff --git a/include/asm-i386/spinlock.h b/include/asm-i386/spinlock.h index f3d2159d4daf..63d70e1f2a0e 100644 --- a/include/asm-i386/spinlock.h +++ b/include/asm-i386/spinlock.h @@ -80,7 +80,7 @@ __asm__ __volatile__( \ "\n1:\t" \ "lock ; btsl $0,%0\n\t" \ "jc 2f\n" \ - ".section .text.lock\n" \ + ".section .text.lock,\"ax\"\n" \ "2:\t" \ "testb $1,%0\n\t" \ "jne 2b\n\t" \ @@ -134,7 +134,7 @@ typedef struct { asm volatile("\n1:\t" \ "lock ; incl %0\n\t" \ "js 2f\n" \ - ".section .text.lock\n" \ + ".section .text.lock,\"ax\"\n" \ "2:\tlock ; decl %0\n" \ "3:\tcmpl $0,%0\n\t" \ "js 3b\n\t" \ @@ -153,7 +153,7 @@ typedef struct { "testl $0x7fffffff,%0\n\t" \ "jne 4f\n" \ "2:\n" \ - ".section .text.lock\n" \ + ".section .text.lock,\"ax\"\n" \ "3:\ttestl $-1,%0\n\t" \ "js 3b\n\t" \ "lock ; btsl $31,%0\n\t" \ diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index efed32b4db7e..9386c17f0bea 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -1,4 +1,4 @@ -/* +/* $Revision: 1.7 $$Date: 1997/03/26 10:30:00 $ * linux/include/linux/cyclades.h * * This file is maintained by Marcio Saito and @@ -6,9 +6,18 @@ * * This file contains the general definitions for the cyclades.c driver *$Log: cyclades.h,v $ - * Revision 1.5 1995/11/13 21:13:31 bentson - * changes suggested by Michael Chastain - * to support use of this file in non-kernel applications + *Revision 1.7 1997/03/26 10:30:00 daniel + *new entries at the end of cyclades_port struct to reallocate + *variables illegally allocated within card memory. + * + *Revision 1.6 1996/09/09 18:35:30 bentson + *fold in changes for Cyclom-Z -- including structures for + *communicating with board as well modest changes to original + *structures to support new features. + * + *Revision 1.5 1995/11/13 21:13:31 bentson + *changes suggested by Michael Chastain + *to support use of this file in non-kernel applications * * */ @@ -35,14 +44,359 @@ struct cyclades_monitor { #define CYGETDEFTIMEOUT 0x435908 #define CYSETDEFTIMEOUT 0x435909 +/*************** CYCLOM-Z ADDITIONS ***************/ + +#define CZIOC ('M' << 8) +#define CZ_NBOARDS (CZIOC|0xfa) +#define CZ_BOOT_START (CZIOC|0xfb) +#define CZ_BOOT_DATA (CZIOC|0xfc) +#define CZ_BOOT_END (CZIOC|0xfd) +#define CZ_TEST (CZIOC|0xfe) + +#define MAX_BOARD 4 /* Max number of boards */ +#define MAX_PORT 128 /* Max number of ports per board */ +#define MAX_DEV 256 /* Max number of ports total */ + +#define CYZ_BOOT_NWORDS 0x100 +struct CYZ_BOOT_CTRL { + unsigned short nboard; + int status[MAX_BOARD]; + int nchannel[MAX_BOARD]; + int fw_rev[MAX_BOARD]; + unsigned long offset; + unsigned long data[CYZ_BOOT_NWORDS]; +}; + + +#ifndef DP_WINDOW_SIZE +/* #include "cyclomz.h" */ +/****************** ****************** *******************/ +/* + * The data types defined below are used in all ZFIRM interface + * data structures. They accomodate differences between HW + * architectures and compilers. + */ + +typedef unsigned long uclong; /* 32 bits, unsigned */ +typedef unsigned short ucshort; /* 16 bits, unsigned */ +typedef unsigned char ucchar; /* 8 bits, unsigned */ + +/* + * Memory Window Sizes + */ + +#define DP_WINDOW_SIZE (0x00080000) /* window size 512 Kb */ +#define CTRL_WINDOW_SIZE (0x00000100) /* runtime regs 256 bytes */ + +/* + * CUSTOM_REG - Cyclom-Z/PCI Custom Registers Set. The driver + * normally will access only interested on the fpga_id, fpga_version, + * start_cpu and stop_cpu. + */ + +struct CUSTOM_REG { + uclong fpga_id; /* FPGA Identification Register */ + uclong fpga_version; /* FPGA Version Number Register */ + uclong cpu_start; /* CPU start Register (write) */ + uclong cpu_stop; /* CPU stop Register (write) */ + uclong misc_reg; /* Miscelaneous Register */ + uclong idt_mode; /* IDT mode Register */ + uclong uart_irq_status; /* UART IRQ status Register */ + uclong clear_timer0_irq; /* Clear timer interrupt Register */ + uclong clear_timer1_irq; /* Clear timer interrupt Register */ + uclong clear_timer2_irq; /* Clear timer interrupt Register */ + uclong test_register; /* Test Register */ + uclong test_count; /* Test Count Register */ + uclong timer_select; /* Timer select register */ + uclong pr_uart_irq_status; /* Prioritized UART IRQ stat Reg */ + uclong ram_wait_state; /* RAM wait-state Register */ + uclong uart_wait_state; /* UART wait-state Register */ + uclong timer_wait_state; /* timer wait-state Register */ + uclong ack_wait_state; /* ACK wait State Register */ +}; + +/* + * RUNTIME_9060 - PLX PCI9060ES local configuration and shared runtime + * registers. This structure can be used to access the 9060 registers + * (memory mapped). + */ + +struct RUNTIME_9060 { + uclong loc_addr_range; /* 00h - Local Address Range */ + uclong loc_addr_base; /* 04h - Local Address Base */ + uclong loc_arbitr; /* 08h - Local Arbitration */ + uclong endian_descr; /* 0Ch - Big/Little Endian Descriptor */ + uclong loc_rom_range; /* 10h - Local ROM Range */ + uclong loc_rom_base; /* 14h - Local ROM Base */ + uclong loc_bus_descr; /* 18h - Local Bus descriptor */ + uclong loc_range_mst; /* 1Ch - Local Range for Master to PCI */ + uclong loc_base_mst; /* 20h - Local Base for Master PCI */ + uclong loc_range_io; /* 24h - Local Range for Master IO */ + uclong pci_base_mst; /* 28h - PCI Base for Master PCI */ + uclong pci_conf_io; /* 2Ch - PCI configuration for Master IO */ + uclong filler1; /* 30h */ + uclong filler2; /* 34h */ + uclong filler3; /* 38h */ + uclong filler4; /* 3Ch */ + uclong mail_box_0; /* 40h - Mail Box 0 */ + uclong mail_box_1; /* 44h - Mail Box 1 */ + uclong mail_box_2; /* 48h - Mail Box 2 */ + uclong mail_box_3; /* 4Ch - Mail Box 3 */ + uclong filler5; /* 50h */ + uclong filler6; /* 54h */ + uclong filler7; /* 58h */ + uclong filler8; /* 5Ch */ + uclong pci_doorbell; /* 60h - PCI to Local Doorbell */ + uclong loc_doorbell; /* 64h - Local to PCI Doorbell */ + uclong intr_ctrl_stat; /* 68h - Interrupt Control/Status */ + uclong init_ctrl; /* 6Ch - EEPROM control, Init Control, etc */ +}; + +/* Values for the Local Base Address re-map register */ + +#define WIN_RAM 0x00000001L /* set the sliding window to RAM */ +#define WIN_CREG 0x14000001L /* set the window to custom Registers */ + +/* Values timer select registers */ + +#define TIMER_BY_1M 0x00 /* clock divided by 1M */ +#define TIMER_BY_256K 0x01 /* clock divided by 256k */ +#define TIMER_BY_128K 0x02 /* clock divided by 128k */ +#define TIMER_BY_32K 0x03 /* clock divided by 32k */ + +/****************** ****************** *******************/ +#endif + +#ifndef ZFIRM_ID +/* #include "zfwint.h" */ +/****************** ****************** *******************/ +/* + * This file contains the definitions for interfacing with the + * Cyclom-Z ZFIRM Firmware. + */ + +/* General Constant definitions */ + +#define MAX_CHAN 64 /* max number of channels per board */ + +/* firmware id structure (set after boot) */ + +#define ID_ADDRESS 0x00000180L /* signature/pointer address */ +#define ZFIRM_ID 0x5557465AL /* ZFIRM/U signature */ +struct FIRM_ID { + uclong signature; /* ZFIRM/U signature */ + uclong zfwctrl_addr; /* pointer to ZFW_CTRL structure */ +}; + +/* Op. System id */ + +#define C_OS_LINUX 0x00000030 /* generic Linux system */ + +/* channel op_mode */ + +#define C_CH_DISABLE 0x00000000 /* channel is disabled */ +#define C_CH_TXENABLE 0x00000001 /* channel Tx enabled */ +#define C_CH_RXENABLE 0x00000002 /* channel Rx enabled */ +#define C_CH_ENABLE 0x00000003 /* channel Tx/Rx enabled */ +#define C_CH_LOOPBACK 0x00000004 /* Loopback mode */ + +/* comm_parity - parity */ + +#define C_PR_NONE 0x00000000 /* None */ +#define C_PR_ODD 0x00000001 /* Odd */ +#define C_PR_EVEN 0x00000002 /* Even */ +#define C_PR_MARK 0x00000004 /* Mark */ +#define C_PR_SPACE 0x00000008 /* Space */ +#define C_PR_PARITY 0x000000ff + +#define C_PR_DISCARD 0x00000100 /* discard char with frame/par error */ +#define C_PR_IGNORE 0x00000200 /* ignore frame/par error */ + +/* comm_data_l - data length and stop bits */ + +#define C_DL_CS5 0x00000001 +#define C_DL_CS6 0x00000002 +#define C_DL_CS7 0x00000004 +#define C_DL_CS8 0x00000008 +#define C_DL_CS 0x0000000f +#define C_DL_1STOP 0x00000010 +#define C_DL_15STOP 0x00000020 +#define C_DL_2STOP 0x00000040 +#define C_DL_STOP 0x000000f0 + +/* interrupt enabling/status */ + +#define C_IN_DISABLE 0x00000000 /* zero, disable interrupts */ +#define C_IN_TXBEMPTY 0x00000001 /* tx buffer empty */ +#define C_IN_TXLOWWM 0x00000002 /* tx buffer below LWM */ +#define C_IN_RXHIWM 0x00000010 /* rx buffer above HWM */ +#define C_IN_RXNNDT 0x00000020 /* rx no new data timeout */ +#define C_IN_MDCD 0x00000100 /* modem DCD change */ +#define C_IN_MDSR 0x00000200 /* modem DSR change */ +#define C_IN_MRI 0x00000400 /* modem RI change */ +#define C_IN_MCTS 0x00000800 /* modem CTS change */ +#define C_IN_RXBRK 0x00001000 /* Break received */ +#define C_IN_PR_ERROR 0x00002000 /* parity error */ +#define C_IN_FR_ERROR 0x00004000 /* frame error */ + +/* flow control */ + +#define C_FL_OXX 0x00000001 /* output Xon/Xoff flow control */ +#define C_FL_IXX 0x00000002 /* output Xon/Xoff flow control */ +#define C_FL_OIXANY 0x00000004 /* output Xon/Xoff (any xon) */ +#define C_FL_SWFLOW 0x0000000f + +/* flow status */ + +#define C_FS_TXIDLE 0x00000000 /* no Tx data in the buffer or UART */ +#define C_FS_SENDING 0x00000001 /* UART is sending data */ +#define C_FS_SWFLOW 0x00000002 /* Tx is stopped by received Xoff */ + +/* rs_control/rs_status RS-232 signals */ + +#define C_RS_DCD 0x00000100 /* CD */ +#define C_RS_DSR 0x00000200 /* DSR */ +#define C_RS_RI 0x00000400 /* RI */ +#define C_RS_CTS 0x00000800 /* CTS */ +#define C_RS_RTS 0x00000001 /* RTS */ +#define C_RS_DTR 0x00000004 /* DTR */ + +/* commands Host <-> Board */ + +#define C_CM_RESET 0x01 /* reset/flush buffers */ +#define C_CM_IOCTL 0x02 /* re-read CH_CTRL */ +#define C_CM_IOCTLW 0x03 /* re-read CH_CTRL, intr when done */ +#define C_CM_IOCTLM 0x04 /* RS-232 outputs change */ +#define C_CM_SENDXOFF 0x10 /* send Xoff */ +#define C_CM_SENDXON 0x11 /* send Xon */ +#define C_CM_CLFLOW 0x12 /* Clear flow control (resume) */ +#define C_CM_SENDBRK 0x41 /* send break */ +#define C_CM_INTBACK 0x42 /* Interrupt back */ +#define C_CM_SET_BREAK 0x43 /* Tx break on */ +#define C_CM_CLR_BREAK 0x44 /* Tx break off */ +#define C_CM_CMD_DONE 0x45 /* Previous command done */ +#define C_CM_TINACT 0x51 /* set inactivity detection */ +#define C_CM_IRQ_ENBL 0x52 /* enable generation of interrupts */ +#define C_CM_IRQ_DSBL 0x53 /* disable generation of interrupts */ +#define C_CM_ACK_ENBL 0x54 /* enable acknolowdged interrupt mode */ +#define C_CM_ACK_DSBL 0x55 /* disable acknolowdged intr mode */ +#define C_CM_FLUSH_RX 0x56 /* flushes Rx buffer */ +#define C_CM_FLUSH_TX 0x57 /* flushes Tx buffer */ + +#define C_CM_TXBEMPTY 0x60 /* Tx buffer is empty */ +#define C_CM_TXLOWWM 0x61 /* Tx buffer low water mark */ +#define C_CM_RXHIWM 0x62 /* Rx buffer high water mark */ +#define C_CM_RXNNDT 0x63 /* rx no new data timeout */ +#define C_CM_MDCD 0x70 /* modem DCD change */ +#define C_CM_MDSR 0x71 /* modem DSR change */ +#define C_CM_MRI 0x72 /* modem RI change */ +#define C_CM_MCTS 0x73 /* modem CTS change */ +#define C_CM_RXBRK 0x84 /* Break received */ +#define C_CM_PR_ERROR 0x85 /* Parity error */ +#define C_CM_FR_ERROR 0x86 /* Frame error */ +#define C_CM_CMDERROR 0x90 /* command error */ +#define C_CM_FATAL 0x91 /* fatal error */ +#define C_CM_HW_RESET 0x92 /* reset board */ + +/* + * CH_CTRL - This per port structure contains all parameters + * that control an specific port. It can be seen as the + * configuration registers of a "super-serial-controller". + */ + +struct CH_CTRL { + uclong op_mode; /* operation mode */ + uclong intr_enable; /* interrupt masking */ + uclong sw_flow; /* SW flow control */ + uclong flow_status; /* output flow status */ + uclong comm_baud; /* baud rate - numerically specified */ + uclong comm_parity; /* parity */ + uclong comm_data_l; /* data length/stop */ + uclong comm_flags; /* other flags */ + uclong hw_flow; /* HW flow control */ + uclong rs_control; /* RS-232 outputs */ + uclong rs_status; /* RS-232 inputs */ + uclong flow_xon; /* xon char */ + uclong flow_xoff; /* xoff char */ + uclong filler[3]; /* filler to align structures */ +}; + + +/* + * BUF_CTRL - This per channel structure contains + * all Tx and Rx buffer control for a given channel. + */ + +struct BUF_CTRL { + uclong flag_dma; /* buffers are in Host memory */ + uclong tx_bufaddr; /* address of the tx buffer */ + uclong tx_bufsize; /* tx buffer size */ + uclong tx_threshold; /* tx low water mark */ + uclong tx_get; /* tail index tx buf */ + uclong tx_put; /* head index tx buf */ + uclong rx_bufaddr; /* address of the rx buffer */ + uclong rx_bufsize; /* rx buffer size */ + uclong rx_threshold; /* rx high water mark */ + uclong rx_get; /* tail index rx buf */ + uclong rx_put; /* head index rx buf */ + uclong filler[5]; /* filler to align structures */ +}; + +/* + * BOARD_CTRL - This per board structure contains all global + * control fields related to the board. + */ + +struct BOARD_CTRL { + + /* static info provided by the on-board CPU */ + uclong n_channel; /* number of channels */ + uclong fw_version; /* firmware version */ + + /* static info provided by the driver */ + uclong op_system; /* op_system id */ + uclong dr_version; /* driver version */ + + /* board control area */ + uclong inactivity; /* inactivity control */ + + /* host to FW commands */ + uclong hcmd_channel; /* channel number */ + uclong *hcmd_param; /* pointer to parameters */ + + /* FW to Host commands */ + uclong fwcmd_channel; /* channel number */ + uclong *fwcmd_param; /* pointer to parameters */ + + /* filler so the structures are aligned */ + uclong filler[7]; +}; + +/* + * ZFW_CTRL - This is the data structure that includes all other + * data structures used by the Firmware. + */ + +struct ZFW_CTRL { + struct BOARD_CTRL board_ctrl; + struct CH_CTRL ch_ctrl[MAX_CHAN]; + struct BUF_CTRL buf_ctrl[MAX_CHAN]; +}; + +/****************** ****************** *******************/ +#endif + + + #ifdef __KERNEL__ /* Per card data structure */ struct cyclades_card { - int base_addr; + long base_addr; + long ctl_addr; int irq; - int num_chips; /* 0 if card is absent */ + int num_chips; /* 0 if card absent, 1 if Z/PCI, else Y */ int first_line; /* minor number of first channel on card */ int bus_index; /* address shift - 0 for ISA, 1 for PCI */ }; @@ -72,6 +426,7 @@ struct cyclades_port { int xmit_fifo_size; int cor1,cor2,cor3,cor4,cor5; int tbpr,tco,rbpr,rco; + int baud; int ignore_status_mask; int close_delay; int IER; /* Interrupt Enable Register */ @@ -95,6 +450,8 @@ struct cyclades_port { struct wait_queue *open_wait; struct wait_queue *close_wait; struct cyclades_monitor mon; + unsigned long jiffies[3]; + unsigned long rflush_count; }; /* @@ -111,6 +468,10 @@ struct cyclades_port { #define CyMaxChipsPerCard 8 +#define CyPCI_Ywin 0x4000 +#define CyPCI_Zctl 0x100 +#define CyPCI_Zwin 0x80000 + /**** CD1400 registers ****/ #define CyRegSize 0x0400 @@ -282,3 +643,4 @@ struct cyclades_port { #endif /* __KERNEL__ */ #endif /* _LINUX_CYCLADES_H */ + diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index b3dabb0aaacf..df8a8f5e8501 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -162,6 +162,7 @@ enum scsi_directory_inos { PROC_SCSI_7000FASST, PROC_SCSI_IBMMCA, PROC_SCSI_EATA2X, + PROC_SCSI_DC390T, PROC_SCSI_AM53C974, PROC_SCSI_SSC, PROC_SCSI_NCR53C406A, diff --git a/include/linux/serial.h b/include/linux/serial.h index 34ce3e42b606..5081a0640e84 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -46,7 +46,8 @@ struct serial_struct { #define PORT_16650 6 #define PORT_16650V2 7 #define PORT_16750 8 -#define PORT_MAX 8 +#define PORT_STARTECH 9 +#define PORT_MAX 9 struct serial_uart_config { char *name; diff --git a/include/linux/socket.h b/include/linux/socket.h index a75919bf9997..200d05441c03 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -3,8 +3,8 @@ #include /* arch-dependent defines */ #include /* the SIOCxxx I/O controls */ -#include /* iovec support */ #include /* pid_t */ +#include /* iovec support */ typedef unsigned short sa_family_t; diff --git a/init/main.c b/init/main.c index 87be33d42b6e..2ad3b39b70b3 100644 --- a/init/main.c +++ b/init/main.c @@ -195,6 +195,9 @@ extern void atari_scsi_setup (char *str, int *ints); extern void wd33c93_setup (char *str, int *ints); extern void gvp11_setup (char *str, int *ints); +#ifdef CONFIG_CYCLADES +extern void cy_setup(char *str, int *ints); +#endif #ifdef CONFIG_DIGI extern void pcxx_setup(char *str, int *ints); #endif @@ -482,6 +485,9 @@ struct { #if defined(CONFIG_GVP11_SCSI) { "gvp11=", gvp11_setup }, #endif +#ifdef CONFIG_CYCLADES + { "cyclades=", cy_setup }, +#endif #ifdef CONFIG_DIGI { "digi=", pcxx_setup }, #endif diff --git a/kernel/sched.c b/kernel/sched.c index a6b0ab58c9bf..dccba9e7fae6 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -252,6 +252,135 @@ static inline int goodness(struct task_struct * p, struct task_struct * prev, in return weight; } +/* + * Event timer code + */ +#define TVN_BITS 6 +#define TVR_BITS 8 +#define TVN_SIZE (1 << TVN_BITS) +#define TVR_SIZE (1 << TVR_BITS) +#define TVN_MASK (TVN_SIZE - 1) +#define TVR_MASK (TVR_SIZE - 1) + +#define SLOW_BUT_DEBUGGING_TIMERS 0 + +struct timer_vec { + int index; + struct timer_list *vec[TVN_SIZE]; +}; + +struct timer_vec_root { + int index; + struct timer_list *vec[TVR_SIZE]; +}; + +static struct timer_vec tv5 = { 0 }; +static struct timer_vec tv4 = { 0 }; +static struct timer_vec tv3 = { 0 }; +static struct timer_vec tv2 = { 0 }; +static struct timer_vec_root tv1 = { 0 }; + +static struct timer_vec * const tvecs[] = { + (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5 +}; + +#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0])) + +static unsigned long timer_jiffies = 0; + +static inline void insert_timer(struct timer_list *timer, + struct timer_list **vec, int idx) +{ + if ((timer->next = vec[idx])) + vec[idx]->prev = timer; + vec[idx] = timer; + timer->prev = (struct timer_list *)&vec[idx]; +} + +static inline void internal_add_timer(struct timer_list *timer) +{ + /* + * must be cli-ed when calling this + */ + unsigned long expires = timer->expires; + unsigned long idx = expires - timer_jiffies; + + if (idx < TVR_SIZE) { + int i = expires & TVR_MASK; + insert_timer(timer, tv1.vec, i); + } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { + int i = (expires >> TVR_BITS) & TVN_MASK; + insert_timer(timer, tv2.vec, i); + } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { + int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; + insert_timer(timer, tv3.vec, i); + } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { + int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; + insert_timer(timer, tv4.vec, i); + } else if (expires < timer_jiffies) { + /* can happen if you add a timer with expires == jiffies, + * or you set a timer to go off in the past + */ + insert_timer(timer, tv1.vec, tv1.index); + } else if (idx < 0xffffffffUL) { + int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; + insert_timer(timer, tv5.vec, i); + } else { + /* Can only get here on architectures with 64-bit jiffies */ + timer->next = timer->prev = timer; + } +} + +static spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED; + +void add_timer(struct timer_list *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&timerlist_lock, flags); +#if SLOW_BUT_DEBUGGING_TIMERS + if (timer->next || timer->prev) { + printk("add_timer() called with non-zero list from %p\n", + __builtin_return_address(0)); + goto out; + } +#endif + internal_add_timer(timer); +#if SLOW_BUT_DEBUGGING_TIMERS +out: +#endif + spin_unlock_irqrestore(&timerlist_lock, flags); +} + +static inline int detach_timer(struct timer_list *timer) +{ + int ret = 0; + struct timer_list *next, *prev; + next = timer->next; + prev = timer->prev; + if (next) { + next->prev = prev; + } + if (prev) { + ret = 1; + prev->next = next; + } + return ret; +} + + +int del_timer(struct timer_list * timer) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&timerlist_lock, flags); + ret = detach_timer(timer); + timer->next = timer->prev = 0; + spin_unlock_irqrestore(&timerlist_lock, flags); + return ret; +} + #ifdef __SMP__ #define idle_task (task[cpu_number_map[this_cpu]]) @@ -284,10 +413,8 @@ asmlinkage void schedule(void) need_resched = 0; prev = current; this_cpu = smp_processor_id(); - if (local_irq_count[this_cpu]) { - printk("Scheduling in interrupt\n"); - *(char *)0 = 0; - } + if (local_irq_count[this_cpu]) + goto scheduling_in_interrupt; release_kernel_lock(prev, this_cpu, lock_depth); if (bh_active & bh_mask) do_bottom_half(); @@ -297,16 +424,8 @@ asmlinkage void schedule(void) /* move an exhausted RR process to be last.. */ if (!prev->counter && prev->policy == SCHED_RR) { - if (prev->pid) { - prev->counter = prev->priority; - move_last_runqueue(prev); - } else { - static int count = 5; - if (count) { - count--; - printk("Moving pid 0 last\n"); - } - } + prev->counter = prev->priority; + move_last_runqueue(prev); } timeout = 0; switch (prev->state) { @@ -338,7 +457,9 @@ asmlinkage void schedule(void) * the scheduler lock */ spin_unlock_irq(&runqueue_lock); +#ifdef __SMP__ prev->has_cpu = 0; +#endif /* * Note! there may appear new tasks on the run-queue during this, as @@ -369,8 +490,10 @@ asmlinkage void schedule(void) } } +#ifdef __SMP__ next->has_cpu = 1; next->processor = this_cpu; +#endif if (prev != next) { struct timer_list timer; @@ -392,6 +515,11 @@ asmlinkage void schedule(void) spin_unlock(&scheduler_lock); reacquire_kernel_lock(prev, smp_processor_id(), lock_depth); + return; + +scheduling_in_interrupt: + printk("Scheduling in interrupt\n"); + *(int *)0 = 0; } #ifndef __alpha__ @@ -613,133 +741,6 @@ void sleep_on(struct wait_queue **p) __sleep_on(p,TASK_UNINTERRUPTIBLE); } - -#define TVN_BITS 6 -#define TVR_BITS 8 -#define TVN_SIZE (1 << TVN_BITS) -#define TVR_SIZE (1 << TVR_BITS) -#define TVN_MASK (TVN_SIZE - 1) -#define TVR_MASK (TVR_SIZE - 1) - -#define SLOW_BUT_DEBUGGING_TIMERS 0 - -struct timer_vec { - int index; - struct timer_list *vec[TVN_SIZE]; -}; - -struct timer_vec_root { - int index; - struct timer_list *vec[TVR_SIZE]; -}; - -static struct timer_vec tv5 = { 0 }; -static struct timer_vec tv4 = { 0 }; -static struct timer_vec tv3 = { 0 }; -static struct timer_vec tv2 = { 0 }; -static struct timer_vec_root tv1 = { 0 }; - -static struct timer_vec * const tvecs[] = { - (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5 -}; - -#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0])) - -static unsigned long timer_jiffies = 0; - -static inline void insert_timer(struct timer_list *timer, - struct timer_list **vec, int idx) -{ - if ((timer->next = vec[idx])) - vec[idx]->prev = timer; - vec[idx] = timer; - timer->prev = (struct timer_list *)&vec[idx]; -} - -static inline void internal_add_timer(struct timer_list *timer) -{ - /* - * must be cli-ed when calling this - */ - unsigned long expires = timer->expires; - unsigned long idx = expires - timer_jiffies; - - if (idx < TVR_SIZE) { - int i = expires & TVR_MASK; - insert_timer(timer, tv1.vec, i); - } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { - int i = (expires >> TVR_BITS) & TVN_MASK; - insert_timer(timer, tv2.vec, i); - } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { - int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; - insert_timer(timer, tv3.vec, i); - } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { - int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; - insert_timer(timer, tv4.vec, i); - } else if (expires < timer_jiffies) { - /* can happen if you add a timer with expires == jiffies, - * or you set a timer to go off in the past - */ - insert_timer(timer, tv1.vec, tv1.index); - } else if (idx < 0xffffffffUL) { - int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; - insert_timer(timer, tv5.vec, i); - } else { - /* Can only get here on architectures with 64-bit jiffies */ - timer->next = timer->prev = timer; - } -} - -static spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED; - -void add_timer(struct timer_list *timer) -{ - unsigned long flags; - - spin_lock_irqsave(&timerlist_lock, flags); -#if SLOW_BUT_DEBUGGING_TIMERS - if (timer->next || timer->prev) { - printk("add_timer() called with non-zero list from %p\n", - __builtin_return_address(0)); - goto out; - } -#endif - internal_add_timer(timer); -#if SLOW_BUT_DEBUGGING_TIMERS -out: -#endif - spin_unlock_irqrestore(&timerlist_lock, flags); -} - -static inline int detach_timer(struct timer_list *timer) -{ - int ret = 0; - struct timer_list *next, *prev; - next = timer->next; - prev = timer->prev; - if (next) { - next->prev = prev; - } - if (prev) { - ret = 1; - prev->next = next; - } - return ret; -} - - -int del_timer(struct timer_list * timer) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&timerlist_lock, flags); - ret = detach_timer(timer); - timer->next = timer->prev = 0; - spin_unlock_irqrestore(&timerlist_lock, flags); - return ret; -} - static inline void cascade_timers(struct timer_vec *tv) { /* cascade all the timers from tv up one level */ @@ -831,17 +832,18 @@ unsigned long avenrun[3] = { 0,0,0 }; */ static unsigned long count_active_tasks(void) { - struct task_struct **p; + struct task_struct *p; unsigned long nr = 0; - for(p = &LAST_TASK; p > &FIRST_TASK; --p) - if (*p && ((*p)->state == TASK_RUNNING || - (*p)->state == TASK_UNINTERRUPTIBLE || - (*p)->state == TASK_SWAPPING)) + read_lock(&tasklist_lock); + for_each_task(p) { + if (p->pid && + (p->state == TASK_RUNNING || + p->state == TASK_UNINTERRUPTIBLE || + p->state == TASK_SWAPPING)) nr += FIXED_1; -#ifdef __SMP__ - nr-=(smp_num_cpus-1)*FIXED_1; -#endif + } + read_unlock(&tasklist_lock); return nr; } diff --git a/scripts/mkdep.c b/scripts/mkdep.c index d8ecde8813ae..50c04920c300 100644 --- a/scripts/mkdep.c +++ b/scripts/mkdep.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include