aboutsummaryrefslogtreecommitdiff
path: root/drivers/char/cyclades.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/cyclades.c')
-rw-r--r--drivers/char/cyclades.c4200
1 files changed, 0 insertions, 4200 deletions
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
deleted file mode 100644
index c99728f0cd9..00000000000
--- a/drivers/char/cyclades.c
+++ /dev/null
@@ -1,4200 +0,0 @@
-#undef BLOCKMOVE
-#define Z_WAKE
-#undef Z_EXT_CHARS_IN_BUFFER
-
-/*
- * linux/drivers/char/cyclades.c
- *
- * This file contains the driver for the Cyclades async multiport
- * serial boards.
- *
- * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>.
- * Modified and maintained by Marcio Saito <marcio@cyclades.com>.
- *
- * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com>
- *
- * Much of the design and some of the code came from serial.c
- * which was copyright (C) 1991, 1992 Linus Torvalds. It was
- * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
- * and then fixed as suggested by Michael K. Johnson 12/12/92.
- * Converted to pci probing and cleaned up by Jiri Slaby.
- *
- */
-
-#define CY_VERSION "2.6"
-
-/* 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 256
-
-#define ZO_V1 0
-#define ZO_V2 1
-#define ZE_V1 2
-
-#define SERIAL_PARANOIA_CHECK
-#undef CY_DEBUG_OPEN
-#undef CY_DEBUG_THROTTLE
-#undef CY_DEBUG_OTHER
-#undef CY_DEBUG_IO
-#undef CY_DEBUG_COUNT
-#undef CY_DEBUG_DTR
-#undef CY_DEBUG_WAIT_UNTIL_SENT
-#undef CY_DEBUG_INTERRUPTS
-#undef CY_16Y_HACK
-#undef CY_ENABLE_MONITORING
-#undef CY_PCI_DEBUG
-
-/*
- * Include section
- */
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/cyclades.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/bitops.h>
-#include <linux/firmware.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-
-#include <linux/io.h>
-#include <linux/uaccess.h>
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-
-static void cy_send_xchar(struct tty_struct *tty, char ch);
-
-#ifndef SERIAL_XMIT_SIZE
-#define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096))
-#endif
-
-#define STD_COM_FLAGS (0)
-
-/* firmware stuff */
-#define ZL_MAX_BLOCKS 16
-#define DRIVER_VERSION 0x02010203
-#define RAM_SIZE 0x80000
-
-enum zblock_type {
- ZBLOCK_PRG = 0,
- ZBLOCK_FPGA = 1
-};
-
-struct zfile_header {
- char name[64];
- char date[32];
- char aux[32];
- u32 n_config;
- u32 config_offset;
- u32 n_blocks;
- u32 block_offset;
- u32 reserved[9];
-} __attribute__ ((packed));
-
-struct zfile_config {
- char name[64];
- u32 mailbox;
- u32 function;
- u32 n_blocks;
- u32 block_list[ZL_MAX_BLOCKS];
-} __attribute__ ((packed));
-
-struct zfile_block {
- u32 type;
- u32 file_offset;
- u32 ram_offset;
- u32 size;
-} __attribute__ ((packed));
-
-static struct tty_driver *cy_serial_driver;
-
-#ifdef CONFIG_ISA
-/* 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.
-*/
-
-static unsigned int cy_isa_addresses[] = {
- 0xD0000,
- 0xD2000,
- 0xD4000,
- 0xD6000,
- 0xD8000,
- 0xDA000,
- 0xDC000,
- 0xDE000,
- 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses)
-
-static long maddr[NR_CARDS];
-static int irq[NR_CARDS];
-
-module_param_array(maddr, long, NULL, 0);
-module_param_array(irq, int, NULL, 0);
-
-#endif /* CONFIG_ISA */
-
-/* This is the per-card data structure containing address, irq, number of
- channels, etc. This driver supports a maximum of NR_CARDS cards.
-*/
-static struct cyclades_card cy_card[NR_CARDS];
-
-static int cy_next_channel; /* next minor available */
-
-/*
- * 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->port.flags.
- * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
- * HI VHI
- * 20
- */
-static const int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
- 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
- 230400, 0
-};
-
-static const char baud_co_25[] = { /* 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
-};
-
-static const char baud_bpr_25[] = { /* 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
-};
-
-static const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */
- /* value => 00 01 02 03 04 */
- /* divide by 8 32 128 512 2048 */
- 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03,
- 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00
-};
-
-static const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */
- 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62,
- 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32,
- 0x21
-};
-
-static const 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,
- 0x07
-};
-
-/*
- * The Cyclades driver implements HW flow control as any serial driver.
- * The cyclades_port structure member rflow and the vector rflow_thr
- * allows us to take advantage of a special feature in the CD1400 to avoid
- * data loss even when the system interrupt latency is too high. These flags
- * are to be used only with very special applications. Setting these flags
- * requires the use of a special cable (DTR and RTS reversed). In the new
- * CD1400-based boards (rev. 6.00 or later), there is no need for special
- * cables.
- */
-
-static const char rflow_thr[] = { /* rflow threshold */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a
-};
-
-/* The Cyclom-Ye has placed the sequential chips in non-sequential
- * address order. This look-up table overcomes that problem.
- */
-static const unsigned int cy_chip_offset[] = { 0x0000,
- 0x0400,
- 0x0800,
- 0x0C00,
- 0x0200,
- 0x0600,
- 0x0A00,
- 0x0E00
-};
-
-/* PCI related definitions */
-
-#ifdef CONFIG_PCI
-static const struct pci_device_id cy_pci_dev_id[] = {
- /* PCI < 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) },
- /* PCI > 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) },
- /* 4Y PCI < 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) },
- /* 4Y PCI > 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) },
- /* 8Y PCI < 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) },
- /* 8Y PCI > 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) },
- /* Z PCI < 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) },
- /* Z PCI > 1Mb */
- { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) },
- { } /* end of table */
-};
-MODULE_DEVICE_TABLE(pci, cy_pci_dev_id);
-#endif
-
-static void cy_start(struct tty_struct *);
-static void cy_set_line_char(struct cyclades_port *, struct tty_struct *);
-static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32);
-#ifdef CONFIG_ISA
-static unsigned detect_isa_irq(void __iomem *);
-#endif /* CONFIG_ISA */
-
-#ifndef CONFIG_CYZ_INTR
-static void cyz_poll(unsigned long);
-
-/* The Cyclades-Z polling cycle is defined by this variable */
-static long cyz_polling_cycle = CZ_DEF_POLL;
-
-static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0);
-
-#else /* CONFIG_CYZ_INTR */
-static void cyz_rx_restart(unsigned long);
-static struct timer_list cyz_rx_full_timer[NR_PORTS];
-#endif /* CONFIG_CYZ_INTR */
-
-static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val)
-{
- struct cyclades_card *card = port->card;
-
- cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val);
-}
-
-static inline u8 cyy_readb(struct cyclades_port *port, u32 reg)
-{
- struct cyclades_card *card = port->card;
-
- return readb(port->u.cyy.base_addr + (reg << card->bus_index));
-}
-
-static inline bool cy_is_Z(struct cyclades_card *card)
-{
- return card->num_chips == (unsigned int)-1;
-}
-
-static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr)
-{
- return readl(&ctl_addr->init_ctrl) & (1 << 17);
-}
-
-static inline bool cyz_fpga_loaded(struct cyclades_card *card)
-{
- return __cyz_fpga_loaded(card->ctl_addr.p9060);
-}
-
-static inline bool cyz_is_loaded(struct cyclades_card *card)
-{
- struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;
-
- return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) &&
- readl(&fw_id->signature) == ZFIRM_ID;
-}
-
-static inline int serial_paranoia_check(struct cyclades_port *info,
- const char *name, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
- if (!info) {
- printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) "
- "in %s\n", name, routine);
- return 1;
- }
-
- if (info->magic != CYCLADES_MAGIC) {
- printk(KERN_WARNING "cyc Warning: bad magic number for serial "
- "struct (%s) in %s\n", name, routine);
- return 1;
- }
-#endif
- return 0;
-}
-
-/***********************************************************/
-/********* 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.
-
- This function is only called from inside spinlock-protected code.
- */
-static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index)
-{
- void __iomem *ccr = base_addr + (CyCCR << index);
- unsigned int i;
-
- /* Check to see that the previous command has completed */
- for (i = 0; i < 100; i++) {
- if (readb(ccr) == 0)
- break;
- udelay(10L);
- }
- /* if the CCR never cleared, the previous command
- didn't finish within the "reasonable time" */
- if (i == 100)
- return -1;
-
- /* Issue the new command */
- cy_writeb(ccr, cmd);
-
- return 0;
-}
-
-static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd)
-{
- return __cyy_issue_cmd(port->u.cyy.base_addr, cmd,
- port->card->bus_index);
-}
-
-#ifdef CONFIG_ISA
-/* ISA interrupt detection code */
-static unsigned detect_isa_irq(void __iomem *address)
-{
- int irq;
- unsigned long irqs, flags;
- int save_xir, save_car;
- int index = 0; /* IRQ probing is only for ISA */
-
- /* forget possible initially masked and pending IRQ */
- irq = probe_irq_off(probe_irq_on());
-
- /* Clear interrupts on the board first */
- cy_writeb(address + (Cy_ClrIntr << index), 0);
- /* Cy_ClrIntr is 0x1800 */
-
- irqs = probe_irq_on();
- /* Wait ... */
- msleep(5);
-
- /* Enable the Tx interrupts on the CD1400 */
- local_irq_save(flags);
- cy_writeb(address + (CyCAR << index), 0);
- __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index);
-
- cy_writeb(address + (CyCAR << index), 0);
- cy_writeb(address + (CySRER << index),
- readb(address + (CySRER << index)) | CyTxRdy);
- local_irq_restore(flags);
-
- /* Wait ... */
- msleep(5);
-
- /* Check which interrupt is in use */
- irq = probe_irq_off(irqs);
-
- /* Clean up */
- save_xir = (u_char) readb(address + (CyTIR << index));
- save_car = readb(address + (CyCAR << index));
- cy_writeb(address + (CyCAR << index), (save_xir & 0x3));
- cy_writeb(address + (CySRER << index),
- readb(address + (CySRER << index)) & ~CyTxRdy);
- cy_writeb(address + (CyTIR << index), (save_xir & 0x3f));
- cy_writeb(address + (CyCAR << index), (save_car));
- cy_writeb(address + (Cy_ClrIntr << index), 0);
- /* Cy_ClrIntr is 0x1800 */
-
- return (irq > 0) ? irq : 0;
-}
-#endif /* CONFIG_ISA */
-
-static void cyy_chip_rx(struct cyclades_card *cinfo, int chip,
- void __iomem *base_addr)
-{
- struct cyclades_port *info;
- struct tty_struct *tty;
- int len, index = cinfo->bus_index;
- u8 ivr, save_xir, channel, save_car, data, char_count;
-
-#ifdef CY_DEBUG_INTERRUPTS
- printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip);
-#endif
- /* determine the channel & change to that context */
- save_xir = readb(base_addr + (CyRIR << index));
- channel = save_xir & CyIRChannel;
- info = &cinfo->ports[channel + chip * 4];
- save_car = cyy_readb(info, CyCAR);
- cyy_writeb(info, CyCAR, save_xir);
- ivr = cyy_readb(info, CyRIVR) & CyIVRMask;
-
- tty = tty_port_tty_get(&info->port);
- /* if there is nowhere to put the data, discard it */
- if (tty == NULL) {
- if (ivr == CyIVRRxEx) { /* exception */
- data = cyy_readb(info, CyRDSR);
- } else { /* normal character reception */
- char_count = cyy_readb(info, CyRDCR);
- while (char_count--)
- data = cyy_readb(info, CyRDSR);
- }
- goto end;
- }
- /* there is an open port for this data */
- if (ivr == CyIVRRxEx) { /* exception */
- data = cyy_readb(info, CyRDSR);
-
- /* For statistics only */
- if (data & CyBREAK)
- info->icount.brk++;
- else if (data & CyFRAME)
- info->icount.frame++;
- else if (data & CyPARITY)
- info->icount.parity++;
- else if (data & CyOVERRUN)
- info->icount.overrun++;
-
- if (data & info->ignore_status_mask) {
- info->icount.rx++;
- tty_kref_put(tty);
- return;
- }
- if (tty_buffer_request_room(tty, 1)) {
- if (data & info->read_status_mask) {
- if (data & CyBREAK) {
- tty_insert_flip_char(tty,
- cyy_readb(info, CyRDSR),
- TTY_BREAK);
- info->icount.rx++;
- if (info->port.flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (data & CyFRAME) {
- tty_insert_flip_char(tty,
- cyy_readb(info, CyRDSR),
- TTY_FRAME);
- info->icount.rx++;
- info->idle_stats.frame_errs++;
- } else if (data & CyPARITY) {
- /* Pieces of seven... */
- tty_insert_flip_char(tty,
- cyy_readb(info, CyRDSR),
- TTY_PARITY);
- info->icount.rx++;
- info->idle_stats.parity_errs++;
- } else if (data & CyOVERRUN) {
- tty_insert_flip_char(tty, 0,
- TTY_OVERRUN);
- info->icount.rx++;
- /* If the flip buffer itself is
- overflowing, we still lose
- the next incoming character.
- */
- tty_insert_flip_char(tty,
- cyy_readb(info, CyRDSR),
- TTY_FRAME);
- info->icount.rx++;
- info->idle_stats.overruns++;
- /* These two conditions may imply */
- /* a normal read should be done. */
- /* } else if(data & CyTIMEOUT) { */
- /* } else if(data & CySPECHAR) { */
- } else {
- tty_insert_flip_char(tty, 0,
- TTY_NORMAL);
- info->icount.rx++;
- }
- } else {
- tty_insert_flip_char(tty, 0, TTY_NORMAL);
- info->icount.rx++;
- }
- } else {
- /* there was a software buffer overrun and nothing
- * could be done about it!!! */
- info->icount.buf_overrun++;
- info->idle_stats.overruns++;
- }
- } else { /* normal character reception */
- /* load # chars available from the chip */
- char_count = cyy_readb(info, CyRDCR);
-
-#ifdef CY_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
- len = tty_buffer_request_room(tty, char_count);
- while (len--) {
- data = cyy_readb(info, CyRDSR);
- tty_insert_flip_char(tty, data, TTY_NORMAL);
- info->idle_stats.recv_bytes++;
- info->icount.rx++;
-#ifdef CY_16Y_HACK
- udelay(10L);
-#endif
- }
- info->idle_stats.recv_idle = jiffies;
- }
- tty_schedule_flip(tty);
- tty_kref_put(tty);
-end:
- /* end of service */
- cyy_writeb(info, CyRIR, save_xir & 0x3f);
- cyy_writeb(info, CyCAR, save_car);
-}
-
-static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip,
- void __iomem *base_addr)
-{
- struct cyclades_port *info;
- struct tty_struct *tty;
- int char_count, index = cinfo->bus_index;
- u8 save_xir, channel, save_car, outch;
-
- /* Since we only get here when the transmit buffer
- is empty, we know we can always stuff a dozen
- characters. */
-#ifdef CY_DEBUG_INTERRUPTS
- printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip);
-#endif
-
- /* determine the channel & change to that context */
- save_xir = readb(base_addr + (CyTIR << index));
- channel = save_xir & CyIRChannel;
- save_car = readb(base_addr + (CyCAR << index));
- cy_writeb(base_addr + (CyCAR << index), save_xir);
-
- info = &cinfo->ports[channel + chip * 4];
- tty = tty_port_tty_get(&info->port);
- if (tty == NULL) {
- cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
- goto end;
- }
-
- /* load the on-chip space for outbound data */
- char_count = info->xmit_fifo_size;
-
- if (info->x_char) { /* send special char */
- outch = info->x_char;
- cyy_writeb(info, CyTDR, outch);
- char_count--;
- info->icount.tx++;
- info->x_char = 0;
- }
-
- if (info->breakon || info->breakoff) {
- if (info->breakon) {
- cyy_writeb(info, CyTDR, 0);
- cyy_writeb(info, CyTDR, 0x81);
- info->breakon = 0;
- char_count -= 2;
- }
- if (info->breakoff) {
- cyy_writeb(info, CyTDR, 0);
- cyy_writeb(info, CyTDR, 0x83);
- info->breakoff = 0;
- char_count -= 2;
- }
- }
-
- while (char_count-- > 0) {
- if (!info->xmit_cnt) {
- if (cyy_readb(info, CySRER) & CyTxMpty) {
- cyy_writeb(info, CySRER,
- cyy_readb(info, CySRER) & ~CyTxMpty);
- } else {
- cyy_writeb(info, CySRER, CyTxMpty |
- (cyy_readb(info, CySRER) & ~CyTxRdy));
- }
- goto done;
- }
- if (info->port.xmit_buf == NULL) {
- cyy_writeb(info, CySRER,
- cyy_readb(info, CySRER) & ~CyTxRdy);
- goto done;
- }
- if (tty->stopped || tty->hw_stopped) {
- cyy_writeb(info, CySRER,
- cyy_readb(info, CySRER) & ~CyTxRdy);
- goto done;
- }
- /* 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->port.xmit_buf[info->xmit_tail];
- if (outch) {
- info->xmit_cnt--;
- info->xmit_tail = (info->xmit_tail + 1) &
- (SERIAL_XMIT_SIZE - 1);
- cyy_writeb(info, CyTDR, outch);
- info->icount.tx++;
- } else {
- if (char_count > 1) {
- info->xmit_cnt--;
- info->xmit_tail = (info->xmit_tail + 1) &
- (SERIAL_XMIT_SIZE - 1);
- cyy_writeb(info, CyTDR, outch);
- cyy_writeb(info, CyTDR, 0);
- info->icount.tx++;
- char_count--;
- }
- }
- }
-
-done:
- tty_wakeup(tty);
- tty_kref_put(tty);
-end:
- /* end of service */
- cyy_writeb(info, CyTIR, save_xir & 0x3f);
- cyy_writeb(info, CyCAR, save_car);
-}
-
-static void cyy_chip_modem(struct cyclades_card *cinfo, int chip,
- void __iomem *base_addr)
-{
- struct cyclades_port *info;
- struct tty_struct *tty;
- int index = cinfo->bus_index;
- u8 save_xir, channel, save_car, mdm_change, mdm_status;
-
- /* determine the channel & change to that context */
- save_xir = readb(base_addr + (CyMIR << index));
- channel = save_xir & CyIRChannel;
- info = &cinfo->ports[channel + chip * 4];
- save_car = cyy_readb(info, CyCAR);
- cyy_writeb(info, CyCAR, save_xir);
-
- mdm_change = cyy_readb(info, CyMISR);
- mdm_status = cyy_readb(info, CyMSVR1);
-
- tty = tty_port_tty_get(&info->port);
- if (!tty)
- goto end;
-
- if (mdm_change & CyANY_DELTA) {
- /* For statistics only */
- if (mdm_change & CyDCD)
- info->icount.dcd++;
- if (mdm_change & CyCTS)
- info->icount.cts++;
- if (mdm_change & CyDSR)
- info->icount.dsr++;
- if (mdm_change & CyRI)
- info->icount.rng++;
-
- wake_up_interruptible(&info->port.delta_msr_wait);
- }
-
- if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) {
- if (mdm_status & CyDCD)
- wake_up_interruptible(&info->port.open_wait);
- else
- tty_hangup(tty);
- }
- if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) {
- if (tty->hw_stopped) {
- if (mdm_status & CyCTS) {
- /* cy_start isn't used
- because... !!! */
- tty->hw_stopped = 0;
- cyy_writeb(info, CySRER,
- cyy_readb(info, CySRER) | CyTxRdy);
- tty_wakeup(tty);
- }
- } else {
- if (!(mdm_status & CyCTS)) {
- /* cy_stop isn't used
- because ... !!! */
- tty->hw_stopped = 1;
- cyy_writeb(info, CySRER,
- cyy_readb(info, CySRER) & ~CyTxRdy);
- }
- }
- }
-/* if (mdm_change & CyDSR) {
- }
- if (mdm_change & CyRI) {
- }*/
- tty_kref_put(tty);
-end:
- /* end of service */
- cyy_writeb(info, CyMIR, save_xir & 0x3f);
- cyy_writeb(info, CyCAR, save_car);
-}
-
-/* The real interrupt service routine is called
- whenever the card wants its hand held--chars
- received, out buffer empty, modem change, etc.
- */
-static irqreturn_t cyy_interrupt(int irq, void *dev_id)
-{
- int status;
- struct cyclades_card *cinfo = dev_id;
- void __iomem *base_addr, *card_base_addr;
- unsigned int chip, too_many, had_work;
- int index;
-
- if (unlikely(cinfo == NULL)) {
-#ifdef CY_DEBUG_INTERRUPTS
- printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",
- irq);
-#endif
- return IRQ_NONE; /* spurious interrupt */
- }
-
- card_base_addr = cinfo->base_addr;
- index = cinfo->bus_index;
-
- /* card was not initialized yet (e.g. DEBUG_SHIRQ) */
- if (unlikely(card_base_addr == NULL))
- return IRQ_HANDLED;
-
- /* This loop checks all chips in the card. Make a note whenever
- _any_ chip had some work to do, as this is considered an
- indication that there will be more to do. Only when no chip
- has any work does this outermost loop exit.
- */
- do {
- had_work = 0;
- for (chip = 0; chip < cinfo->num_chips; chip++) {
- base_addr = cinfo->base_addr +
- (cy_chip_offset[chip] << index);
- too_many = 0;
- while ((status = readb(base_addr +
- (CySVRR << index))) != 0x00) {
- had_work++;
- /* The purpose of the following test is to ensure that
- 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;
- spin_lock(&cinfo->card_lock);
- if (status & CySRReceive) /* rx intr */
- cyy_chip_rx(cinfo, chip, base_addr);
- if (status & CySRTransmit) /* tx intr */
- cyy_chip_tx(cinfo, chip, base_addr);
- if (status & CySRModem) /* modem intr */
- cyy_chip_modem(cinfo, chip, base_addr);
- spin_unlock(&cinfo->card_lock);
- }
- }
- } while (had_work);
-
- /* clear interrupts */
- spin_lock(&cinfo->card_lock);
- cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0);
- /* Cy_ClrIntr is 0x1800 */
- spin_unlock(&cinfo->card_lock);
- return IRQ_HANDLED;
-} /* cyy_interrupt */
-
-static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set,
- unsigned int clear)
-{
- struct cyclades_card *card = info->card;
- int channel = info->line - card->first_line;
- u32 rts, dtr, msvrr, msvrd;
-
- channel &= 0x03;
-
- if (info->rtsdtr_inv) {
- msvrr = CyMSVR2;
- msvrd = CyMSVR1;
- rts = CyDTR;
- dtr = CyRTS;
- } else {
- msvrr = CyMSVR1;
- msvrd = CyMSVR2;
- rts = CyRTS;
- dtr = CyDTR;
- }
- if (set & TIOCM_RTS) {
- cyy_writeb(info, CyCAR, channel);
- cyy_writeb(info, msvrr, rts);
- }
- if (clear & TIOCM_RTS) {
- cyy_writeb(info, CyCAR, channel);
- cyy_writeb(info, msvrr, ~rts);
- }
- if (set & TIOCM_DTR) {
- cyy_writeb(info, CyCAR, channel);
- cyy_writeb(info, msvrd, dtr);
-#ifdef CY_DEBUG_DTR
- printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n");
- printk(KERN_DEBUG " status: 0x%x, 0x%x\n",
- cyy_readb(info, CyMSVR1),
- cyy_readb(info, CyMSVR2));
-#endif
- }
- if (clear & TIOCM_DTR) {
- cyy_writeb(info, CyCAR, channel);
- cyy_writeb(info, msvrd, ~dtr);
-#ifdef CY_DEBUG_DTR
- printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n");
- printk(KERN_DEBUG " status: 0x%x, 0x%x\n",
- cyy_readb(info, CyMSVR1),
- cyy_readb(info, CyMSVR2));
-#endif
- }
-}
-
-/***********************************************************/
-/********* End of block of Cyclom-Y specific code **********/
-/******** Start of block of Cyclades-Z specific code *******/
-/***********************************************************/
-
-static int
-cyz_fetch_msg(struct cyclades_card *cinfo,
- __u32 *channel, __u8 *cmd, __u32 *param)
-{
- struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
- unsigned long loc_doorbell;
-
- loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell);
- if (loc_doorbell) {
- *cmd = (char)(0xff & loc_doorbell);
- *channel = readl(&board_ctrl->fwcmd_channel);
- *param = (__u32) readl(&board_ctrl->fwcmd_param);
- cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff);
- return 1;
- }
- return 0;
-} /* cyz_fetch_msg */
-
-static int
-cyz_issue_cmd(struct cyclades_card *cinfo,
- __u32 channel, __u8 cmd, __u32 param)
-{
- struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
- __u32 __iomem *pci_doorbell;
- unsigned int index;
-
- if (!cyz_is_loaded(cinfo))
- return -1;
-
- index = 0;
- pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell;
- while ((readl(pci_doorbell) & 0xff) != 0) {
- if (index++ == 1000)
- return (int)(readl(pci_doorbell) & 0xff);
- udelay(50L);
- }
- cy_writel(&board_ctrl->hcmd_channel, channel);
- cy_writel(&board_ctrl->hcmd_param, param);
- cy_writel(pci_doorbell, (long)cmd);
-
- return 0;
-} /* cyz_issue_cmd */
-
-static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty)
-{
- struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
- struct cyclades_card *cinfo = info->card;
- unsigned int char_count;
- int len;
-#ifdef BLOCKMOVE
- unsigned char *buf;
-#else
- char data;
-#endif
- __u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr;
-
- rx_get = new_rx_get = readl(&buf_ctrl->rx_get);
- rx_put = readl(&buf_ctrl->rx_put);
- rx_bufsize = readl(&buf_ctrl->rx_bufsize);
- rx_bufaddr = readl(&buf_ctrl->rx_bufaddr);
- if (rx_put >= rx_get)
- char_count = rx_put - rx_get;
- else
- char_count = rx_put - rx_get + rx_bufsize;
-
- if (char_count) {
-#ifdef CY_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 == NULL) {
- /* flush received characters */
- new_rx_get = (new_rx_get + char_count) &
- (rx_bufsize - 1);
- 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 (1) {
- len = tty_prepare_flip_string(tty, &buf,
- char_count);
- if (!len)
- break;
-
- len = min_t(unsigned int, min(len, char_count),
- rx_bufsize - new_rx_get);
-
- memcpy_fromio(buf, cinfo->base_addr +
- rx_bufaddr + new_rx_get, len);
-
- new_rx_get = (new_rx_get + len) &
- (rx_bufsize - 1);
- char_count -= len;
- info->icount.rx += len;
- info->idle_stats.recv_bytes += len;
- }
-#else
- len = tty_buffer_request_room(tty, char_count);
- while (len--) {
- data = readb(cinfo->base_addr + rx_bufaddr +
- new_rx_get);
- new_rx_get = (new_rx_get + 1) &
- (rx_bufsize - 1);
- tty_insert_flip_char(tty, data, TTY_NORMAL);
- info->idle_stats.recv_bytes++;
- info->icount.rx++;
- }
-#endif
-#ifdef CONFIG_CYZ_INTR
- /* Recalculate the number of chars in the RX buffer and issue
- a cmd in case it's higher than the RX high water mark */
- rx_put = readl(&buf_ctrl->rx_put);
- if (rx_put >= rx_get)
- char_count = rx_put - rx_get;
- else
- char_count = rx_put - rx_get + rx_bufsize;
- if (char_count >= readl(&buf_ctrl->rx_threshold) &&
- !timer_pending(&cyz_rx_full_timer[
- info->line]))
- mod_timer(&cyz_rx_full_timer[info->line],
- jiffies + 1);
-#endif
- info->idle_stats.recv_idle = jiffies;
- tty_schedule_flip(tty);
- }
- /* Update rx_get */
- cy_writel(&buf_ctrl->rx_get, new_rx_get);
- }
-}
-
-static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty)
-{
- struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
- struct cyclades_card *cinfo = info->card;
- u8 data;
- unsigned int char_count;
-#ifdef BLOCKMOVE
- int small_count;
-#endif
- __u32 tx_put, tx_get, tx_bufsize, tx_bufaddr;
-
- if (info->xmit_cnt <= 0) /* Nothing to transmit */
- return;
-
- tx_get = readl(&buf_ctrl->tx_get);
- tx_put = readl(&buf_ctrl->tx_put);
- tx_bufsize = readl(&buf_ctrl->tx_bufsize);
- tx_bufaddr = readl(&buf_ctrl->tx_bufaddr);
- if (tx_put >= tx_get)
- char_count = tx_get - tx_put - 1 + tx_bufsize;
- else
- char_count = tx_get - tx_put - 1;
-
- if (char_count) {
-
- if (tty == NULL)
- goto ztxdone;
-
- if (info->x_char) { /* send special char */
- data = info->x_char;
-
- cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
- tx_put = (tx_put + 1) & (tx_bufsize - 1);
- info->x_char = 0;
- char_count--;
- info->icount.tx++;
- }
-#ifdef BLOCKMOVE
- while (0 < (small_count = min_t(unsigned int,
- tx_bufsize - tx_put, min_t(unsigned int,
- (SERIAL_XMIT_SIZE - info->xmit_tail),
- min_t(unsigned int, info->xmit_cnt,
- char_count))))) {
-
- memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr +
- tx_put),
- &info->port.xmit_buf[info->xmit_tail],
- small_count);
-
- tx_put = (tx_put + small_count) & (tx_bufsize - 1);
- char_count -= small_count;
- info->icount.tx += small_count;
- info->xmit_cnt -= small_count;
- info->xmit_tail = (info->xmit_tail + small_count) &
- (SERIAL_XMIT_SIZE - 1);
- }
-#else
- while (info->xmit_cnt && char_count) {
- data = info->port.xmit_buf[info->xmit_tail];
- info->xmit_cnt--;
- info->xmit_tail = (info->xmit_tail + 1) &
- (SERIAL_XMIT_SIZE - 1);
-
- cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
- tx_put = (tx_put + 1) & (tx_bufsize - 1);
- char_count--;
- info->icount.tx++;
- }
-#endif
- tty_wakeup(tty);
-ztxdone:
- /* Update tx_put */
- cy_writel(&buf_ctrl->tx_put, tx_put);
- }
-}
-
-static void cyz_handle_cmd(struct cyclades_card *cinfo)
-{
- struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
- struct tty_struct *tty;
- struct cyclades_port *info;
- __u32 channel, param, fw_ver;
- __u8 cmd;
- int special_count;
- int delta_count;
-
- fw_ver = readl(&board_ctrl->fw_version);
-
- while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
- special_count = 0;
- delta_count = 0;
- info = &cinfo->ports[channel];
- tty = tty_port_tty_get(&info->port);
- if (tty == NULL)
- continue;
-
- switch (cmd) {
- case C_CM_PR_ERROR:
- tty_insert_flip_char(tty, 0, TTY_PARITY);
- info->icount.rx++;
- special_count++;
- break;
- case C_CM_FR_ERROR:
- tty_insert_flip_char(tty, 0, TTY_FRAME);
- info->icount.rx++;
- special_count++;
- break;
- case C_CM_RXBRK:
- tty_insert_flip_char(tty, 0, TTY_BREAK);
- info->icount.rx++;
- special_count++;
- break;
- case C_CM_MDCD:
- info->icount.dcd++;
- delta_count++;
- if (info->port.flags & ASYNC_CHECK_CD) {
- u32 dcd = fw_ver > 241 ? param :
- readl(&info->u.cyz.ch_ctrl->rs_status);
- if (dcd & C_RS_DCD)
- wake_up_interruptible(&info->port.open_wait);
- else
- tty_hangup(tty);
- }
- break;
- case C_CM_MCTS:
- info->icount.cts++;
- delta_count++;
- break;
- case C_CM_MRI:
- info->icount.rng++;
- delta_count++;
- break;
- case C_CM_MDSR:
- info->icount.dsr++;
- delta_count++;
- break;
-#ifdef Z_WAKE
- case C_CM_IOCTLW:
- complete(&info->shutdown_wait);
- break;
-#endif
-#ifdef CONFIG_CYZ_INTR
- case C_CM_RXHIWM:
- case C_CM_RXNNDT: