diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-06-01 03:16:17 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-06-01 03:16:17 +0000 |
commit | d8d9b8f76f22b7a16a83e261e64f89ee611f49df (patch) | |
tree | 3067bc130b80d52808e6390c9fc7fc087ec1e33c /drivers/char/cyclades.c | |
parent | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (diff) |
Initial revision
Diffstat (limited to 'drivers/char/cyclades.c')
-rw-r--r-- | drivers/char/cyclades.c | 5029 |
1 files changed, 3232 insertions, 1797 deletions
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 0a1825ee8..efa895a69 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 <jlewis@INORGANIC5.FDT.NET>) + * 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 <stevep@fa.tdktca.com> 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 <linux/module.h> #include <linux/errno.h> @@ -280,7 +445,7 @@ static char rcsid[] = #include <asm/system.h> #include <asm/io.h> -#include <asm/uaccess.h> +#include <asm/segment.h> #include <asm/bitops.h> #include <linux/config.h> @@ -290,24 +455,34 @@ static char rcsid[] = #include <linux/pci.h> #include <linux/init.h> -#define small_delay(x) for(j=0;j<x;j++)k++; +#include <linux/version.h> +#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 <asm/uaccess.h> + +#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,128 +694,278 @@ 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 */ +static void CP4(int data) + { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */ +static void CP8(int data) + { CP4((data>>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 */ + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver + * (also known as the "bottom half"). This can be called any + * number of times for any channel without harm. + */ +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(&info->tqueue, &tq_cyclades); /* it belongs to */ + mark_bh(CYCLADES_BH); /* then trigger event */ +} /* cy_sched_event */ + + +/* + * 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 +do_cyclades_bh(void) +{ + run_task_queue(&tq_cyclades); +} /* do_cyclades_bh */ + + +static void +do_softint(void *private_) +{ + struct cyclades_port *info = (struct cyclades_port *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_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 (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (test_and_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 */ + + +/***********************************************************/ +/********* Start of block of Cyclom-Y specific code ********/ /* 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) +static int +cyy_issue_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<<index] == 0){ - break; - } - udelay(10L); - } - /* if the CCR never cleared, the previous command - didn't finish within the "reasonable time" */ - if ( i == 100 ) { - restore_flags(flags); - return (-1); - } + /* Check to see that the previous command has completed */ + for(i = 0 ; i < 100 ; i++){ + if (base_addr[CyCCR<<index] == 0){ + break; + } + udelay(10L); + } + /* if the CCR never cleared, the previous command + didn't finish within the "reasonable time" */ + if ( i == 100 ) { + restore_flags(flags); + return (-1); + } - /* Issue the new command */ - base_addr[CyCCR<<index] = cmd; + /* Issue the new command */ + base_addr[CyCCR<<index] = cmd; restore_flags(flags); return(0); -} /* write_cy_cmd */ +} /* cyy_issue_cmd */ +static int probe_ready; -/* cy_start and cy_stop provide software output flow control as a - function of XON/XOFF, software CTS, and other such stuff. */ +/* + * 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; + } + } + return irq_lines; +} /* grab_all_interrupts */ +/* + * Release all interrupts grabbed by grab_all_interrupts + */ static void -cy_stop(struct tty_struct *tty) +free_all_interrupts(int irq_lines) { - 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; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_stop ttyC%d\n", info->line); /* */ -#endif + int i; + + for (i = 0; i < 16; i++) { + if (irq_lines & (1 << i)) + free_irq(i,NULL); + } +} /* free_all_interrupts */ - 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]<<index)); +/* + * This routine returns a bitfield of "wild interrupts". Basically, + * any unclaimed interrupts which is flapping around. + */ +static int +check_wild_interrupts(void) +{ + 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(); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); /* index channel */ - base_addr[CySRER<<index] &= ~CyTxMpty; + 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; + } + } + free_all_interrupts(irq_lines); restore_flags(flags); + return wild_interrupts; +} /* check_wild_interrupts */ - return; -} /* cy_stop */ - -static void -cy_start(struct tty_struct *tty) +/* + * 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) { - struct cyclades_card *cinfo; - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long timeout; 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]<<index)); + int index; - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); + 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<<index] = 0; + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index); base_addr[CySRER<<index] |= CyTxMpty; - restore_flags(flags); - - return; -} /* cy_start */ - + probe_ready = 1; + sti(); + + timeout = jiffies+2; + while (timeout >= jiffies) { + if (cy_irq_triggered) + break; + } + probe_ready = 0; + return(cy_irq_triggered); +} /* get_auto_irq */ /* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver - * (also known as the "bottom half"). This can be called any - * number of times for any channel without harm. + * Calls get_auto_irq() multiple times, to make sure we don't get + * faked out by random interrupts */ -static inline void -cy_sched_event(struct cyclades_port *info, int event) +static int +do_auto_irq(unsigned char *address) { - info->event |= 1 << event; /* remember what kind of event and who */ - queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */ - mark_bh(CYCLADES_BH); /* then trigger event */ -} /* cy_sched_event */ + 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 */ -static int probe_ready; /* * This interrupt routine is used @@ -638,30 +975,31 @@ 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 */ + int index = 0; /* probing interrupts is only for ISA */ if (!probe_ready) { - *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; + *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; return; } cy_irq_triggered = irq; cy_triggered |= 1 << irq; - if(intr_base_addr[CySVRR<<index] != 0) { - save_xir = (u_char) intr_base_addr[CyTIR<<index]; - save_car = intr_base_addr[CyCAR<<index]; - if ((save_xir & 0x3) != 0){ - SP("channel "); - CP2(save_xir); - SP(" requesting unexpected interrupt\n"); - } - intr_base_addr[CyCAR<<index] = (save_xir & 0x3); - intr_base_addr[CySRER<<index] &= ~CyTxMpty; - intr_base_addr[CyTIR<<index] = (save_xir & 0x3f); - intr_base_addr[CyCAR<<index] = (save_car); - } - *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */ + if(intr_base_addr[CySVRR<<index] != 0) { + save_xir = (u_char) intr_base_addr[CyTIR<<index]; + save_car = intr_base_addr[CyCAR<<index]; + if ((save_xir & 0x3) != 0){ + SP("channel "); + CP8(save_xir); + SP(" requesting unexpected interrupt\n"); + } + intr_base_addr[CyCAR<<index] = (save_xir & 0x3); + intr_base_addr[CySRER<<index] &= ~CyTxMpty; + intr_base_addr[CyTIR<<index] = (save_xir & 0x3f); + intr_base_addr[CyCAR<<index] = (save_car); + } + *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; + /* Cy_ClrIntr is 0x1800 */ return; } /* cy_probe */ @@ -670,7 +1008,7 @@ cy_probe(int irq, void *dev_id, struct pt_regs *regs) received, out buffer empty, modem change, etc. */ static void -cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) +cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct tty_struct *tty; int status; @@ -703,8 +1041,8 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) do{ had_work = 0; for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) { - base_addr = (unsigned char *) - (cinfo->base_addr + (cy_chip_offset[chip]<<index)); + base_addr = (unsigned char *) + (cinfo->base_addr + (cy_chip_offset[chip]<<index)); too_many = 0; while ( (status = base_addr[CySVRR<<index]) != 0x00) { had_work++; @@ -712,12 +1050,12 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) no chip can monopolize the driver. This forces the chips to be checked in a round-robin fashion (after draining each of a bunch (1000) of characters). - */ + */ if(1000<too_many++){ break; } - if (status & CySRReceive) { /* reception interrupt */ - /* determine the channel and change to that context */ + if (status & CySRReceive) { /* reception interrupt */ + /* determine the channel & change to that context */ save_xir = (u_char) base_addr[CyRIR<<index]; channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->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<<index]; - if (info->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<<index]; - }else if(data & CyPARITY){ - *tty->flip.flag_buf_ptr++ = - TTY_PARITY; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<<index]; - }else if(data & CyOVERRUN){ - *tty->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<<index]; - } - /* These two conditions may imply */ - /* a normal read should be done. */ - /* }else if(data & CyTIMEOUT){ */ - /* }else if(data & CySPECHAR){ */ - }else{ - *tty->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<<index]; + if (info->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<<index]; + }else if(data & CyPARITY){ + *tty->flip.flag_buf_ptr++ = + TTY_PARITY; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<<index]; + }else if(data & CyOVERRUN){ + *tty->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<<index]; + } + /* These two conditions may imply */ + /* a normal read should be done. */ + /* }else if(data & CyTIMEOUT){ */ + /* }else if(data & CySPECHAR){ */ + }else{ + *tty->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<<index]; #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; + ++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<<index]; - *tty->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<<index] = (save_xir & 0x3f); @@ -829,18 +1169,19 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) } - if (status & CySRTransmit) { /* transmission interrupt */ - /* Since we only get here when the transmit buffer is empty, - we know we can always stuff a dozen characters. */ + if (status & CySRTransmit) { /* transmission interrupt */ + /* Since we only get here when the transmit buffer + is empty, we know we can always stuff a dozen + characters. */ - /* determine the channel and change to that context */ + /* determine the channel & change to that context */ save_xir = (u_char) base_addr[CyTIR<<index]; channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->first_line; save_car = base_addr[CyCAR<<index]; base_addr[CyCAR<<index] = save_xir; - /* validate the port number (as configured and open) */ + /* validate the port# (as configured and open) */ if( (i < 0) || (NR_PORTS <= i) ){ base_addr[CySRER<<index] &= ~CyTxMpty; goto txend; @@ -852,7 +1193,7 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) goto txdone; } - /* load the on-chip space available for outbound data */ + /* load the on-chip space for outbound data */ char_count = info->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<<index] = 0; /* start break */ - base_addr[CyTDR<<index] = 0x81; - base_addr[CyTDR<<index] = 0; /* delay a bit */ - base_addr[CyTDR<<index] = 0x82; - base_addr[CyTDR<<index] = info->x_break*200/HZ; - base_addr[CyTDR<<index] = 0; /* terminate break */ - base_addr[CyTDR<<index] = 0x83; - char_count -= 7; - info->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<<index] = 0; /* start break */ + base_addr[CyTDR<<index] = 0x81; + base_addr[CyTDR<<index] = 0; /* delay a bit */ + base_addr[CyTDR<<index] = 0x82; + base_addr[CyTDR<<index] = info->x_break*200/HZ; + base_addr[CyTDR<<index] = 0; /* finish break */ + base_addr[CyTDR<<index] = 0x83; + char_count -= 7; + info->x_break = 0; + } while (char_count-- > 0){ if (!info->xmit_cnt){ - base_addr[CySRER<<index] &= ~CyTxMpty; - goto txdone; + base_addr[CySRER<<index] &= ~CyTxMpty; + goto txdone; } - if (info->xmit_buf == 0){ - base_addr[CySRER<<index] &= ~CyTxMpty; - goto txdone; - } - if (info->tty->stopped || info->tty->hw_stopped){ - base_addr[CySRER<<index] &= ~CyTxMpty; - goto txdone; - } - /* Because the Embedded Transmit Commands have been - enabled, we must check to see if the escape - character, NULL, is being sent. If it is, we - must ensure that there is room for it to be - doubled in the output stream. Therefore we - no longer advance the pointer when the character - is fetched, but rather wait until after the check - for a NULL output character. (This is necessary - because there may not be room for the two chars - needed to send a NULL. - */ + if (info->xmit_buf == 0){ + base_addr[CySRER<<index] &= ~CyTxMpty; + goto txdone; + } + if (info->tty->stopped || info->tty->hw_stopped){ + base_addr[CySRER<<index] &= ~CyTxMpty; + goto txdone; + } + /* Because the Embedded Transmit Commands have + been enabled, we must check to see if the + escape character, NULL, is being sent. If it + is, we must ensure that there is room for it + to be doubled in the output stream. Therefore + we no longer advance the pointer when the + character is fetched, but rather wait until + after the check for a NULL output character. + This is necessary because there may not be + room for the two chars needed to send a NULL.) + */ outch = info->xmit_buf[info->xmit_tail]; if( outch ){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<<index] = outch; + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR<<index] = outch; }else{ if(char_count > 1){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<<index] = outch; - base_addr[CyTDR<<index] = 0; - char_count--; + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR<<index] = outch; + base_addr[CyTDR<<index] = 0; + char_count--; }else{ } } @@ -939,10 +1281,11 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (status & CySRModem) { /* modem interrupt */ - /* determine the channel and change to that context */ + /* determine the channel & change to that context */ save_xir = (u_char) base_addr[CyMIR<<index]; channel = (u_short ) (save_xir & CyIRChannel); - info = &cy_port[channel + chip * 4 + cinfo->first_line]; + info = &cy_port[channel + chip * 4 + + cinfo->first_line]; info->last_active = jiffies; save_car = base_addr[CyCAR<<index]; base_addr[CyCAR<<index] = save_xir; @@ -950,32 +1293,40 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) mdm_change = base_addr[CyMISR<<index]; mdm_status = base_addr[CyMSVR1<<index]; - if(info->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<<index] |= CyTxMpty; - cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + base_addr[CySRER<<index] |= CyTxMpty; + cy_sched_event(info, + Cy_EVENT_WRITE_WAKEUP); } }else{ if(!(mdm_status & CyCTS)){ - /* !!! cy_stop isn't used because... */ + /* cy_stop isn't used + because ... !!! */ info->tty->hw_stopped = 1; - base_addr[CySRER<<index] &= ~CyTxMpty; + base_addr[CySRER<<index] &= + ~CyTxMpty; } } } @@ -993,217 +1344,373 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) } while(had_work); /* clear interrupts */ - *(card_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */ + *(card_base_addr + (Cy_ClrIntr<<index)) = 0; + /* Cy_ClrIntr is 0x1800 */ -} /* cy_interrupt */ +} /* cyy_interrupt */ -/* - * 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 -do_cyclades_bh(void) -{ - run_task_queue(&tq_cyclades); -} /* do_cyclades_bh */ +/***********************************************************/ +/********* End of block of Cyclom-Y specific code **********/ +/******** Start of block of Cyclom-Z specific code *********/ +/***********************************************************/ -static void -do_softint(void *private_) + +static int +cyz_fetch_msg( struct cyclades_card *cinfo, + u_long *channel, u_char *cmd, u_long **param) { - struct cyclades_port *info = (struct cyclades_port *) private_; - struct tty_struct *tty; + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + unsigned long loc_doorbell; + + firm_id = (struct FIRM_ID *)(cinfo->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; - } - } - free_all_interrupts(irq_lines); - restore_flags(flags); - return wild_interrupts; -} /* check_wild_interrupts */ + cyz_timerlist.expires = jiffies + 100; -/* - * 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; + for (card = 0 ; card < NR_CARDS ; card++){ + cinfo = &cy_card[card]; + if (!IS_CYC_Z(*cinfo)) continue; - 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<<index] = 0; - write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index); - base_addr[CySRER<<index] |= CyTxMpty; - probe_ready = 1; - sti(); - - timeout = jiffies+2; - while (timeout >= jiffies) { - if (cy_irq_triggered) - break; - } - probe_ready = 0; - return(cy_irq_triggered); -} /* get_auto_irq */ + firm_id = (struct FIRM_ID *) + (cinfo->base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + continue; + } -/* - * 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; + 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); + } + } - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + 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]); - probe_ready = 0; + if ((char_count = CHARS_IN_BUF(buf_ctrl))){ + info->last_active = jiffies; + info->jiffies[1] = jiffies; - cy_wild_int_mask = check_wild_interrupts(); +#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); + } + } - 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 ((char_count = SPACE_IN_BUF(buf_ctrl))){ + if( tty == 0){ + goto ztxdone; + } + + 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]<<index)); #ifdef SERIAL_DEBUG_OPEN - printk("startup card %d, chip %d, channel %d, base_addr %lx", - card, chip, channel, (long)base_addr);/**/ + printk("cyc startup card %d, chip %d, channel %d, base_addr %lx\n", + card, chip, channel, (long)base_addr);/**/ #endif - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyRTPR<<index] = (info->default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + base_addr[CyRTPR<<index] = (info->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<<index] = (u_char)channel; /* !!! Is this needed? */ - base_addr[CyMSVR1<<index] = CyRTS; - base_addr[CyMSVR2<<index] = CyDTR; + base_addr[CyCAR<<index] = + (u_char)channel; /* !!! Is this needed? */ + base_addr[CyMSVR1<<index] = CyRTS; + base_addr[CyMSVR2<<index] = CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:startup raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - base_addr[CySRER<<index] |= CyRxData; - info->flags |= ASYNC_INITIALIZED; + base_addr[CySRER<<index] |= CyRxData; + 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); + } 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]<<index)); + 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]<<index)); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = channel; - base_addr[CySRER<<index] |= CyTxMpty; - restore_flags(flags); + save_flags(flags); cli(); + base_addr[CyCAR<<index] = channel; + base_addr[CySRER<<index] |= CyTxMpty; + restore_flags(flags); + } else { + /* Don't have to do anything at this time */ + } } /* start_xmit */ + /* * This routine shuts down a serial port; interrupts are disabled, * and DTR is dropped if the hangup on close termio flag is on. @@ -1315,326 +1886,535 @@ shutdown(struct cyclades_port * info) int card,chip,channel,index; if (!(info->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]<<index)); + 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]<<index)); #ifdef SERIAL_DEBUG_OPEN - printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n", - card, chip, channel, (long)base_addr); + printk("cyc shutdown Y card %d, chip %d, channel %d, base_addr %lx\n", + card, chip, channel, (long)base_addr); #endif - /* !!! REALLY MUST 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); - } + /* 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<<index] = (u_char)channel; - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1<<index] = ~CyRTS; - base_addr[CyMSVR2<<index] = ~CyDTR; + base_addr[CyCAR<<index] = (u_char)channel; + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + base_addr[CyMSVR1<<index] = ~CyRTS; + base_addr[CyMSVR2<<index] = ~CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc shutdown dropping DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - } - write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index); - /* it may be appropriate to clear _XMIT at - some later date (after testing)!!! */ + } + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index); + /* it may be appropriate to clear _XMIT at + some later date (after testing)!!! */ - if (info->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]<<index)); - - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - - /* tx and rx baud rate */ - - base_addr[CyTCOR<<index] = info->tco; - base_addr[CyTBPR<<index] = info->tbpr; - base_addr[CyRCOR<<index] = info->rco; - base_addr[CyRBPR<<index] = info->rbpr; - - /* set line characteristics according configuration */ - - base_addr[CySCHR1<<index] = START_CHAR(info->tty); - base_addr[CySCHR2<<index] = STOP_CHAR(info->tty); - base_addr[CyCOR1<<index] = info->cor1; - base_addr[CyCOR2<<index] = info->cor2; - base_addr[CyCOR3<<index] = info->cor3; - base_addr[CyCOR4<<index] = info->cor4; - base_addr[CyCOR5<<index] = info->cor5; - - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); - - base_addr[CyCAR<<index] = (u_char)channel; /* !!! Is this needed? */ + /* + * 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; + } - base_addr[CyRTPR<<index] = (info->default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + /* + * 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 (C_CLOCAL(info->tty)) { - base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */ - /* act on 1->0 modem transitions */ - base_addr[CyMCOR1<<index] = CyCTS; - /* act on 0->1 modem transitions */ - base_addr[CyMCOR2<<index] = CyCTS; - } else { - base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */ - /* act on 1->0 modem transitions */ - base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD; - /* act on 0->1 modem transitions */ - base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD; - } + /* + * 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("cyc block_til_ready before block: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + info->count--; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc block_til_ready: (%d): decrementing count to %d\n", + current->pid, info->count); +#endif + info->blocked_open++; - if(i == 0){ /* baud rate is zero, turn off line */ - base_addr[CyMSVR2<<index] = ~CyDTR; + 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]<<index)); + + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + base_addr[CyMSVR2<<index] = CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:block_til_ready raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - }else{ - base_addr[CyMSVR2<<index] = CyDTR; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + } + restore_flags(flags); + 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; + } + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (base_addr[CyMSVR1<<index] & CyDCD))) { + restore_flags(flags); + break; + } + restore_flags(flags); + 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(); } - - 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]<<index)); + /* If the TTY is being hung up, nothing to do */ + if (tty_hung_up_p(filp)) { + 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("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<<index] = channel; - base_addr[CySRER<<index] |= CyTxMpty; +#ifdef SERIAL_DEBUG_OTHER + printk(" cyc:cy_close done\n"); +#endif + + MOD_DEC_USE_COUNT; restore_flags(flags); -} /* cy_flush_chars */ + return; +} /* cy_close */ /* This routine gets called when tty_write has put something into - the write_queue. If the port is not already transmitting stuff, - start it off by enabling interrupts. The interrupt service - routine will then ensure that the characters are sent. If the - port is already active, there is no need to kick it. + * the write_queue. The characters may come from user space or + * kernel space. + * + * This routine will return the number of characters actually + * accepted for writing. + * + * If the port is not already transmitting stuff, start it off by + * enabling interrupts. The interrupt service routine will then + * ensure that the characters are sent. + * If the port is already active, there is no need to kick it. + * */ static int cy_write(struct tty_struct * tty, int from_user, @@ -1645,42 +2425,49 @@ cy_write(struct tty_struct * tty, int from_user, int c, total = 0; #ifdef SERIAL_DEBUG_IO - printk("cy_write ttyC%d\n", info->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]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = channel; + base_addr[CySRER<<index] |= CyTxMpty; + restore_flags(flags); + } else { + /* Since polling is already in place, + nothing further need be done. */ + } +} /* cy_flush_chars */ + + +/* + * This routine returns the numbers of characters the tty driver + * will accept for queuing to be written. This number is subject + * to change as output buffers get emptied, or if the output flow + * control is activated. + */ static int cy_write_room(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->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]<<index)); + channel = (info->line) - (cy_card[card].first_line); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = ~CyRTS; - restore_flags(flags); + 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; + } - return; -} /* cy_throttle */ + 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]<<index)); + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; -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; + /* tx and rx baud rate */ + + base_addr[CyTCOR<<index] = info->tco; + base_addr[CyTBPR<<index] = info->tbpr; + base_addr[CyRCOR<<index] = info->rco; + base_addr[CyRBPR<<index] = info->rbpr; + + /* set line characteristics according configuration */ + + base_addr[CySCHR1<<index] = START_CHAR(info->tty); + base_addr[CySCHR2<<index] = STOP_CHAR(info->tty); + base_addr[CyCOR1<<index] = info->cor1; + base_addr[CyCOR2<<index] = info->cor2; + base_addr[CyCOR3<<index] = info->cor3; + base_addr[CyCOR4<<index] = info->cor4; + base_addr[CyCOR5<<index] = info->cor5; + + cyy_issue_cmd(base_addr, + CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); + + base_addr[CyCAR<<index] = + (u_char)channel; /* !!! Is this needed? */ + + base_addr[CyRTPR<<index] = (info->default_timeout + ? info->default_timeout + : 0x02); /* 10ms rx timeout */ + + if (C_CLOCAL(info->tty)) { + base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */ + /* act on 1->0 modem transitions */ + base_addr[CyMCOR1<<index] = CyCTS; + /* act on 0->1 modem transitions */ + base_addr[CyMCOR2<<index] = CyCTS; + } else { + base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */ + /* act on 1->0 modem transitions */ + base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD; + /* act on 0->1 modem transitions */ + base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD; + } -#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); + if(i == 0){ /* baud rate is zero, turn off line */ + base_addr[CyMSVR2<<index] = ~CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char dropping DTR\n"); + printk(" status: 0x%x, + 0x%x\n", base_addr[CyMSVR1<<index], + base_addr[CyMSVR2<<index]); +#endif + }else{ + base_addr[CyMSVR2<<index] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], + base_addr[CyMSVR2<<index]); #endif + } - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + if (info->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; - } + } - if (I_IXOFF(tty)) { - info->x_char = START_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ - } + 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]; - 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]<<index)); + /* baud rate */ + switch(i = cflag & CBAUD){ + /* + case B0: ch_ctrl->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; + } - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - restore_flags(flags); + /* 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); + } + + 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); + } + + /* 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 + } + + 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 (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + } + +} /* set_line_char */ - return; -} /* cy_unthrottle */ static int get_serial_info(struct cyclades_port * info, @@ -1851,14 +2952,15 @@ get_serial_info(struct cyclades_port * info, tmp.port = info->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]<<index)); + 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]<<index)); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - status = base_addr[CyMSVR1<<index]; - status |= base_addr[CyMSVR2<<index]; - restore_flags(flags); + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + status = base_addr[CyMSVR1<<index]; + status |= base_addr[CyMSVR2<<index]; + restore_flags(flags); + + 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); + } else { + base_addr = (unsigned char*) (cy_card[card].base_addr); + + if (cy_card[card].num_chips != 1){ + return -EINVAL; + } - 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); + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (firm_id->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; + } + + } + 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]<<index)); - - switch (cmd) { - case TIOCMBIS: - if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + 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]<<index)); + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - restore_flags(flags); - } - if (arg & TIOCM_DTR){ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = CyDTR; + base_addr[CyMSVR2<<index] = CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - restore_flags(flags); - } - break; - case TIOCMBIC: - if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + restore_flags(flags); + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = ~CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = ~CyRTS; - restore_flags(flags); - } - if (arg & TIOCM_DTR){ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = ~CyDTR; + base_addr[CyMSVR2<<index] = ~CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info dropping DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - restore_flags(flags); - } - break; - case TIOCMSET: - if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + restore_flags(flags); + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + restore_flags(flags); + }else{ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = ~CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - restore_flags(flags); - }else{ - save_flags(flags); cli(); + base_addr[CyMSVR2<<index] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_modem_info raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); +#endif + restore_flags(flags); + }else{ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = ~CyRTS; - restore_flags(flags); + base_addr[CyMSVR2<<index] = ~CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_modem_info dropping DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); +#endif + restore_flags(flags); + } + break; + default: + return -EINVAL; } - if (arg & TIOCM_DTR){ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = CyDTR; + } else { + base_addr = (unsigned char*) (cy_card[card].base_addr); + + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (firm_id->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<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info raising Z DTR\n"); #endif - restore_flags(flags); - }else{ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = ~CyDTR; + } + break; + case TIOCMBIC: + 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: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info clearing Z DTR\n"); #endif - restore_flags(flags); - } - break; - default: + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + ch_ctrl[channel].rs_control |= C_RS_RTS; + }else{ + 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:set_modem_info raising Z DTR\n"); +#endif + }else{ + ch_ctrl[channel].rs_control &= ~C_RS_DTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_modem_info clearing Z DTR\n"); +#endif + } + break; + default: return -EINVAL; - } + } + }else{ + return -ENODEV; + } + retval = cyz_issue_cmd(&cy_card[info->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]<<index)); + 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]<<index)); + + info->cor3 &= ~CyREC_FIFO; + info->cor3 |= value & CyREC_FIFO; + base_addr[CyCOR3<<index] = info->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<<index] = info->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]<<index)); + 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]<<index)); + + tmp = base_addr[CyCOR3<<index] & CyREC_FIFO; + put_fs_long(tmp,value); + } else { + // Nothing to do! + } + return 0; +}/* get_threshold */ - tmp = base_addr[CyCOR3<<index] & CyREC_FIFO; - put_user(tmp,value); - return 0; -} static int set_default_threshold(struct cyclades_port * info, unsigned long value) { - info->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]<<index)); + 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]<<index)); + + base_addr[CyRTPR<<index] = value & 0xff; + } else { + // Nothing to do! + } + return 0; +}/* set_timeout */ - base_addr[CyRTPR<<index] = value & 0xff; - return 0; -} static int get_timeout(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]<<index)); + 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]<<index)); + + tmp = base_addr[CyRTPR<<index]; + put_fs_long(tmp,value); + } else { + // Nothing to do! + } + return 0; +}/* get_timeout */ - tmp = base_addr[CyRTPR<<index]; - put_user(tmp,value); - return 0; -} static int set_default_timeout(struct cyclades_port * info, unsigned long value) { - info->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]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = ~CyRTS; 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, 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_throttle */ + /* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + * 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. */ -void -cy_hangup(struct tty_struct *tty) +static void +cy_unthrottle(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 + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; - 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); +#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 - info->tty = 0; - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); - wake_up_interruptible(&info->open_wait); -} /* cy_hangup */ + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; + } + if (I_IXOFF(tty)) { + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } -/* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ - */ + 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]<<index)); -static int -block_til_ready(struct tty_struct *tty, struct file * filp, - struct cyclades_port *info) + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + restore_flags(flags); + }else{ + // Nothing to do! + } + + 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 wait_queue wait = { current, NULL }; 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; - int chip, channel,index; - int retval; - char *base_addr; - /* - * 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_OTHER + printk("cyc:cy_stop ttyC%d\n", 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_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]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = + (u_char)(channel & 0x0003); /* index channel */ + base_addr[CySRER<<index] &= ~CyTxMpty; + restore_flags(flags); + } else { + // Nothing to do! } - /* - * 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; - } + return; +} /* cy_stop */ - /* - * 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); + +static void +cy_start(struct tty_struct *tty) +{ + 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; + +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_start ttyC%d\n", info->line); /* */ #endif - info->blocked_open++; + if (serial_paranoia_check(info, tty->device, "cy_start")) + return; + 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]<<index)); - - while (1) { - save_flags(flags); cli(); - if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - base_addr[CyMSVR2<<index] = CyDTR; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); -#endif - } - restore_flags(flags); - 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; - } - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - if (!(info->flags & ASYNC_CALLOUT_ACTIVE) - && !(info->flags & ASYNC_CLOSING) - && (C_CLOCAL(tty) - || (base_addr[CyMSVR1<<index] & CyDCD))) { - restore_flags(flags); - break; - } - restore_flags(flags); - if (current->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 + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); + base_addr[CySRER<<index] |= CyTxMpty; + restore_flags(flags); + } 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 */ + + return; +} /* cy_start */ + /* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. */ -int -cy_open(struct tty_struct *tty, struct file * filp) +static void +cy_hangup(struct tty_struct *tty) { - struct cyclades_port *info; - int retval, 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; - } + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + #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);/**/ + printk("cyc:cy_hangup ttyC%d\n", info->line); /* */ #endif - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); -#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 ((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; - } - - 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); + 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 - return retval; - } + info->tty = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ - info->session = current->session; - info->pgrp = current->pgrp; -#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<CyMaxChipsPerCard; chip_number++){ - base_addr = true_base_addr + (cy_chip_offset[chip_number]<<index); + base_addr = true_base_addr + + (cy_chip_offset[chip_number]<<index); udelay(1000L); if(base_addr[CyCCR<<index] != 0x00){ /************* @@ -2736,39 +3903,396 @@ cy_init_card(unsigned char *true_base_addr,int index)) and this must be a Cyclom-16Y, not a Cyclom-32Ye. */ if (chip_number == 4 - && *(true_base_addr + (cy_chip_offset[0]<<index) + (CyGFRCR<<index)) == 0){ - return chip_number; + && *(true_base_addr + + (cy_chip_offset[0]<<index) + + (CyGFRCR<<index)) == 0){ + return chip_number; } base_addr[CyCCR<<index] = CyCHIP_RESET; udelay(1000L); if(base_addr[CyGFRCR<<index] == 0x00){ - /* - printk(" chip #%d at %#6lx is not responding (GFRCR stayed 0)\n", + /* + printk(" chip #%d at %#6lx is not responding ", chip_number, (unsigned long)base_addr); - */ + printk("(GFRCR stayed 0)\n", + */ return chip_number; } if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){ - /* + /* printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n", - chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]); - */ + chip_number, (unsigned long)base_addr, + base_addr[CyGFRCR<<index]); + */ return chip_number; } base_addr[CyGCR<<index] = CyCH0_SERIAL; - base_addr[CyPPR<<index] = 244; /* better value than CyCLOCK_25_1MS * 5 - to run clock at 200 Hz */ + base_addr[CyPPR<<index] = 244; + /* better value than CyCLOCK_25_1MS * 5 + to run clock at 200 Hz */ - /* + /* printk(" chip #%d at %#6lx is rev 0x%2x\n", - chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]); - */ + chip_number, (unsigned long)base_addr, + base_addr[CyGFRCR<<index]); + */ } return chip_number; -} /* cy_init_card */ +} /* cyy_init_card */ + + +/* + * --------------------------------------------------------------------- + * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. + * sets global variables and return the number of ISA boards found. + * --------------------------------------------------------------------- + */ +__initfunc(static int +cy_detect_isa(void)) +{ + 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_ADDRS ; i++) { + cy_isa_address = cy_isa_addresses[i]; + if (cy_isa_address == 0x0000) { + return(nboard); + } + + /* probe for CD1400... */ +#if LINUX_VERSION_CODE >= 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]<<index)); + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); /* Global Registers */ - printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]); - printk(" CyCAR %x\n", base_addr[CyCAR<<index]); - printk(" CyGCR %x\n", base_addr[CyGCR<<index]); - printk(" CySVRR %x\n", base_addr[CySVRR<<index]); - printk(" CyRICR %x\n", base_addr[CyRICR<<index]); - printk(" CyTICR %x\n", base_addr[CyTICR<<index]); - printk(" CyMICR %x\n", base_addr[CyMICR<<index]); - printk(" CyRIR %x\n", base_addr[CyRIR<<index]); - printk(" CyTIR %x\n", base_addr[CyTIR<<index]); - printk(" CyMIR %x\n", base_addr[CyMIR<<index]); - printk(" CyPPR %x\n", base_addr[CyPPR<<index]); + printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]); + printk(" CyCAR %x\n", base_addr[CyCAR<<index]); + printk(" CyGCR %x\n", base_addr[CyGCR<<index]); + printk(" CySVRR %x\n", base_addr[CySVRR<<index]); + printk(" CyRICR %x\n", base_addr[CyRICR<<index]); + printk(" CyTICR %x\n", base_addr[CyTICR<<index]); + printk(" CyMICR %x\n", base_addr[CyMICR<<index]); + printk(" CyRIR %x\n", base_addr[CyRIR<<index]); + printk(" CyTIR %x\n", base_addr[CyTIR<<index]); + printk(" CyMIR %x\n", base_addr[CyMIR<<index]); + printk(" CyPPR %x\n", base_addr[CyPPR<<index]); - base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyCAR<<index] = (u_char)channel; /* Virtual Registers */ - printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]); - printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]); - printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]); - printk(" CyMISR %x\n", base_addr[CyMISR<<index]); + printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]); + printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]); + printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]); + printk(" CyMISR %x\n", base_addr[CyMISR<<index]); /* Channel Registers */ - printk(" CyCCR %x\n", base_addr[CyCCR<<index]); - printk(" CySRER %x\n", base_addr[CySRER<<index]); - printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]); - printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]); - printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]); - printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]); - printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]); - printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]); - printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]); - printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]); - printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]); - printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]); - printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]); - printk(" CySCRL %x\n", base_addr[CySCRL<<index]); - printk(" CySCRH %x\n", base_addr[CySCRH<<index]); - printk(" CyLNC %x\n", base_addr[CyLNC<<index]); - printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]); - printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]); - printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]); - printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]); - printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]); - printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]); - printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]); - printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]); - printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]); + printk(" CyCCR %x\n", base_addr[CyCCR<<index]); + printk(" CySRER %x\n", base_addr[CySRER<<index]); + printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]); + printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]); + printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]); + printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]); + printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]); + printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]); + printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]); + printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]); + printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]); + printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]); + printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]); + printk(" CySCRL %x\n", base_addr[CySCRL<<index]); + printk(" CySCRH %x\n", base_addr[CySCRH<<index]); + printk(" CyLNC %x\n", base_addr[CyLNC<<index]); + printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]); + printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]); + printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]); + printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]); + printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]); + printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]); + printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]); + printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]); + printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]); restore_flags(flags); } /* show_status */ |