X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fchar%2Fcyclades.c;h=9e0adfe27c12d56cf8d36515f0c6061a47db4a8a;hb=0b887d037bf4b76eec1c960e5feecd6a5a806971;hp=9bec4ef876f249a34d0342719b0e5820ace06447;hpb=875b206b5f4971cc990a12e891f5519f0f6772a9;p=linux-2.6 diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 9bec4ef876..9e0adfe27c 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -10,15 +10,14 @@ * * Initially written by Randolph Bentson . * Modified and maintained by Marcio Saito . - * Currently maintained by Cyclades team . * - * For Technical support and installation problems, please send e-mail - * to support@cyclades.com. + * Copyright (C) 2007 Jiri Slaby * * 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. * * This version supports shared IRQ's (only for PCI boards). * @@ -591,7 +590,7 @@ * */ -#define CY_VERSION "2.4" +#define CY_VERSION "2.5" /* 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 @@ -647,22 +646,13 @@ #include #include #include +#include #include #include #include #include -#define CY_LOCK(info,flags) \ - do { \ - spin_lock_irqsave(&info->card->card_lock, flags); \ - } while (0) - -#define CY_UNLOCK(info,flags) \ - do { \ - spin_unlock_irqrestore(&info->card->card_lock, flags); \ - } while (0) - #include #include @@ -691,6 +681,44 @@ static void cy_send_xchar(struct tty_struct *tty, char ch); #define STD_COM_FLAGS (0) +/* firmware stuff */ +#define ZL_MAX_BLOCKS 16 +#define DRIVER_VERSION 0x02010203 +#define RAM_SIZE 0x80000 + +#define Z_FPGA_LOADED(X) ((readl(&(X)->init_ctrl) & (1<<17)) != 0) + +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 @@ -732,11 +760,6 @@ module_param_array(irq, int, NULL, 0); */ 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. -*/ -static struct cyclades_port cy_port[NR_PORTS]; - static int cy_next_channel; /* next minor available */ /* @@ -865,13 +888,6 @@ static inline int serial_paranoia_check(struct cyclades_port *info, return 1; } - if ((long)info < (long)(&cy_port[0]) || - (long)(&cy_port[NR_PORTS]) < (long)info) { - printk(KERN_WARNING "cyc Warning: cyclades_port out of range " - "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); @@ -932,22 +948,16 @@ do_softint(struct work_struct *work) if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) wake_up_interruptible(&info->open_wait); #ifdef CONFIG_CYZ_INTR - if (test_and_clear_bit(Cy_EVENT_Z_RX_FULL, &info->event)) { - if (cyz_rx_full_timer[info->line].function == NULL) { - cyz_rx_full_timer[info->line].expires = jiffies + 1; - cyz_rx_full_timer[info->line].function = cyz_rx_restart; - cyz_rx_full_timer[info->line].data = - (unsigned long)info; - add_timer(&cyz_rx_full_timer[info->line]); - } - } + if (test_and_clear_bit(Cy_EVENT_Z_RX_FULL, &info->event) && + !timer_pending(&cyz_rx_full_timer[info->line])) + mod_timer(&cyz_rx_full_timer[info->line], jiffies + 1); #endif if (test_and_clear_bit(Cy_EVENT_DELTA_WAKEUP, &info->event)) wake_up_interruptible(&info->delta_msr_wait); tty_wakeup(tty); #ifdef Z_WAKE if (test_and_clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) - wake_up_interruptible(&info->shutdown_wait); + complete(&info->shutdown_wait); #endif } /* do_softint */ @@ -1041,7 +1051,7 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip, struct cyclades_port *info; struct tty_struct *tty; int char_count; - int i, j, len, mdm_change, mdm_status, outch; + int j, len, mdm_change, mdm_status, outch; int save_xir, channel, save_car; char data; @@ -1053,14 +1063,12 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip, spin_lock(&cinfo->card_lock); save_xir = (u_char) readb(base_addr + (CyRIR << index)); channel = (u_short) (save_xir & CyIRChannel); - i = channel + chip * 4 + cinfo->first_line; - info = &cy_port[i]; - info->last_active = jiffies; + info = &cinfo->ports[channel + chip * 4]; save_car = readb(base_addr + (CyCAR << index)); cy_writeb(base_addr + (CyCAR << index), save_xir); /* if there is nowhere to put the data, discard it */ - if (info->tty == 0) { + if (info->tty == NULL) { j = (readb(base_addr + (CyRIVR << index)) & CyIVRMask); if (j == CyIVRRxEx) { /* exception */ @@ -1092,6 +1100,7 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip, if (data & info->ignore_status_mask) { info->icount.rx++; + spin_unlock(&cinfo->card_lock); return; } if (tty_buffer_request_room(tty, 1)) { @@ -1219,20 +1228,18 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip, spin_lock(&cinfo->card_lock); save_xir = (u_char) readb(base_addr + (CyTIR << index)); channel = (u_short) (save_xir & CyIRChannel); - i = channel + chip * 4 + cinfo->first_line; save_car = readb(base_addr + (CyCAR << index)); cy_writeb(base_addr + (CyCAR << index), save_xir); /* validate the port# (as configured and open) */ - if ((i < 0) || (NR_PORTS <= i)) { + if (channel + chip * 4 >= cinfo->nports) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); goto txend; } - info = &cy_port[i]; - info->last_active = jiffies; - if (info->tty == 0) { + info = &cinfo->ports[channel + chip * 4]; + if (info->tty == NULL) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1281,7 +1288,7 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip, } goto txdone; } - if (info->xmit_buf == 0) { + if (info->xmit_buf == NULL) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1322,7 +1329,6 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip, 0); info->icount.tx++; char_count--; - } else { } } } @@ -1344,17 +1350,14 @@ txend: spin_lock(&cinfo->card_lock); save_xir = (u_char) readb(base_addr + (CyMIR << index)); channel = (u_short) (save_xir & CyIRChannel); - info = &cy_port[channel + chip * 4 + cinfo->first_line]; - info->last_active = jiffies; + info = &cinfo->ports[channel + chip * 4]; save_car = readb(base_addr + (CyCAR << index)); cy_writeb(base_addr + (CyCAR << index), save_xir); mdm_change = readb(base_addr + (CyMISR << index)); mdm_status = readb(base_addr + (CyMSVR1 << index)); - if (info->tty == 0) { /* no place for data, ignore it */ - ; - } else { + if (info->tty) { if (mdm_change & CyANY_DELTA) { /* For statistics only */ if (mdm_change & CyDCD) @@ -1408,10 +1411,10 @@ txend: } } } - if (mdm_change & CyDSR) { +/* if (mdm_change & CyDSR) { } if (mdm_change & CyRI) { - } + }*/ } /* end of service */ cy_writeb(base_addr + (CyMIR << index), (save_xir & 0x3f)); @@ -1427,14 +1430,14 @@ txend: static irqreturn_t cyy_interrupt(int irq, void *dev_id) { int status; - struct cyclades_card *cinfo; + struct cyclades_card *cinfo = dev_id; void __iomem *base_addr, *card_base_addr; int chip; int index; int too_many; int had_work; - if ((cinfo = (struct cyclades_card *)dev_id) == 0) { + if (unlikely(cinfo == NULL)) { #ifdef CY_DEBUG_INTERRUPTS printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",irq); #endif @@ -1560,7 +1563,7 @@ cyz_handle_rx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, int char_count; int len; #ifdef BLOCKMOVE - int small_count; + unsigned char *buf; #else char data; #endif @@ -1576,9 +1579,6 @@ cyz_handle_rx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, char_count = rx_put - rx_get + rx_bufsize; if (char_count) { - info->last_active = jiffies; - info->jiffies[1] = jiffies; - #ifdef CY_ENABLE_MONITORING info->mon.int_count++; info->mon.char_count += char_count; @@ -1586,7 +1586,7 @@ cyz_handle_rx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, info->mon.char_max = char_count; info->mon.char_last = char_count; #endif - if (tty == 0) { + if (tty == NULL) { /* flush received characters */ new_rx_get = (new_rx_get + char_count) & (rx_bufsize - 1); @@ -1596,25 +1596,23 @@ cyz_handle_rx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, /* 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 = min_t(unsigned int, - rx_bufsize - new_rx_get, - min_t(unsigned int, TTY_FLIPBUF_SIZE - - tty->flip.count, char_count)))){ - memcpy_fromio(tty->flip.char_buf_ptr, - (char *)(cinfo->base_addr + rx_bufaddr + - new_rx_get), - small_count); + while (1) { + len = tty_prepare_flip_string(tty, &buf, + char_count); + if (!len) + break; - tty->flip.char_buf_ptr += small_count; - memset(tty->flip.flag_buf_ptr, TTY_NORMAL, - small_count); - tty->flip.flag_buf_ptr += small_count; - new_rx_get = (new_rx_get + small_count) & + 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 -= small_count; - info->icount.rx += small_count; - info->idle_stats.recv_bytes += small_count; - tty->flip.count += small_count; + char_count -= len; + info->icount.rx += len; + info->idle_stats.recv_bytes += len; } #else len = tty_buffer_request_room(tty, char_count); @@ -1674,9 +1672,8 @@ cyz_handle_tx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, if (char_count) { - if (tty == 0) { + if (tty == NULL) goto ztxdone; - } if (info->x_char) { /* send special char */ data = info->x_char; @@ -1686,8 +1683,6 @@ cyz_handle_tx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, info->x_char = 0; char_count--; info->icount.tx++; - info->last_active = jiffies; - info->jiffies[2] = jiffies; } #ifdef BLOCKMOVE while (0 < (small_count = min_t(unsigned int, @@ -1707,8 +1702,6 @@ cyz_handle_tx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, info->xmit_cnt -= small_count; info->xmit_tail = (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); - info->last_active = jiffies; - info->jiffies[2] = jiffies; } #else while (info->xmit_cnt && char_count) { @@ -1721,8 +1714,6 @@ cyz_handle_tx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl, tx_put = (tx_put + 1) & (tx_bufsize - 1); char_count--; info->icount.tx++; - info->last_active = jiffies; - info->jiffies[2] = jiffies; } #endif ztxdone: @@ -1760,10 +1751,10 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { special_count = 0; delta_count = 0; - info = &cy_port[channel + cinfo->first_line]; - if ((tty = info->tty) == 0) { + info = &cinfo->ports[channel]; + if ((tty = info->tty) == NULL) continue; - } + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); @@ -1852,16 +1843,16 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) #ifdef CONFIG_CYZ_INTR static irqreturn_t cyz_interrupt(int irq, void *dev_id) { - struct cyclades_card *cinfo; + struct cyclades_card *cinfo = dev_id; - if ((cinfo = (struct cyclades_card *)dev_id) == 0) { + if (unlikely(cinfo == NULL)) { #ifdef CY_DEBUG_INTERRUPTS printk(KERN_DEBUG "cyz_interrupt: spurious interrupt %d\n",irq); #endif return IRQ_NONE; /* spurious interrupt */ } - if (!ISZLOADED(*cinfo)) { + if (unlikely(!ISZLOADED(*cinfo))) { #ifdef CY_DEBUG_INTERRUPTS printk(KERN_DEBUG "cyz_interrupt: board not yet loaded " "(IRQ%d).\n", irq); @@ -1883,14 +1874,13 @@ static void cyz_rx_restart(unsigned long arg) __u32 channel = info->line - card->first_line; unsigned long flags; - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L); if (retval != 0) { printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n", info->line, retval); } - cyz_rx_full_timer[info->line].function = NULL; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } #else /* CONFIG_CYZ_INTR */ @@ -1900,11 +1890,11 @@ static void cyz_poll(unsigned long arg) struct cyclades_card *cinfo; struct cyclades_port *info; struct tty_struct *tty; - static struct FIRM_ID *firm_id; - static struct ZFW_CTRL *zfw_ctrl; - static struct BOARD_CTRL *board_ctrl; - static struct CH_CTRL *ch_ctrl; - static struct BUF_CTRL *buf_ctrl; + struct FIRM_ID __iomem *firm_id; + struct ZFW_CTRL __iomem *zfw_ctrl; + struct BOARD_CTRL __iomem *board_ctrl; + struct CH_CTRL __iomem *ch_ctrl; + struct BUF_CTRL __iomem *buf_ctrl; unsigned long expires = jiffies + HZ; int card, port; @@ -1931,7 +1921,7 @@ static void cyz_poll(unsigned long arg) cyz_handle_cmd(cinfo); for (port = 0; port < cinfo->nports; port++) { - info = &cy_port[port + cinfo->first_line]; + info = &cinfo->ports[port]; tty = info->tty; ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); @@ -1970,7 +1960,7 @@ static int startup(struct cyclades_port *info) if (!page) return -ENOMEM; - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); @@ -1990,7 +1980,7 @@ static int startup(struct cyclades_port *info) else info->xmit_buf = (unsigned char *)page; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); set_line_char(info); @@ -2005,7 +1995,7 @@ static int startup(struct cyclades_port *info) "base_addr %p\n", card, chip, channel, base_addr); #endif - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); @@ -2041,14 +2031,13 @@ static int startup(struct cyclades_port *info) info->idle_stats.recv_idle = info->idle_stats.xmit_idle = jiffies; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } else { struct FIRM_ID __iomem *firm_id; struct ZFW_CTRL __iomem *zfw_ctrl; struct BOARD_CTRL __iomem *board_ctrl; struct CH_CTRL __iomem *ch_ctrl; - int retval; base_addr = card->base_addr; @@ -2066,7 +2055,7 @@ static int startup(struct cyclades_port *info) printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " "base_addr %p\n", card, channel, base_addr); #endif - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); #ifdef Z_WAKE @@ -2106,8 +2095,7 @@ static int startup(struct cyclades_port *info) cy_writel(&ch_ctrl[channel].rs_control, readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR); - retval = cyz_issue_cmd(info->card, channel, - C_CM_IOCTLM, 0L); + retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); if (retval != 0) { printk(KERN_ERR "cyc:startup(3) retval on ttyC%d was " "%x\n", info->line, retval); @@ -2129,7 +2117,7 @@ static int startup(struct cyclades_port *info) info->idle_stats.recv_idle = info->idle_stats.xmit_idle = jiffies; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } #ifdef CY_DEBUG_OPEN @@ -2138,7 +2126,7 @@ static int startup(struct cyclades_port *info) return 0; errout: - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); return retval; } /* startup */ @@ -2157,22 +2145,22 @@ static void start_xmit(struct cyclades_port *info) index = card->bus_index; base_addr = card->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), channel); cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyTxRdy); - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } else { #ifdef CONFIG_CYZ_INTR int retval; - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L); if (retval != 0) { printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was " "%x\n", info->line, retval); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); #else /* CONFIG_CYZ_INTR */ /* Don't have to do anything at this time */ #endif /* CONFIG_CYZ_INTR */ @@ -2208,7 +2196,7 @@ static void shutdown(struct cyclades_port *info) card, chip, channel, base_addr); #endif - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); /* Clear delta_msr_wait queue to avoid mem leaks. */ wake_up_interruptible(&info->delta_msr_wait); @@ -2238,7 +2226,7 @@ static void shutdown(struct cyclades_port *info) set_bit(TTY_IO_ERROR, &info->tty->flags); } info->flags &= ~ASYNC_INITIALIZED; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } else { struct FIRM_ID __iomem *firm_id; struct ZFW_CTRL __iomem *zfw_ctrl; @@ -2262,7 +2250,7 @@ static void shutdown(struct cyclades_port *info) board_ctrl = &zfw_ctrl->board_ctrl; ch_ctrl = zfw_ctrl->ch_ctrl; - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); if (info->xmit_buf) { unsigned char *temp; @@ -2291,7 +2279,7 @@ static void shutdown(struct cyclades_port *info) } info->flags &= ~ASYNC_INITIALIZED; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } #ifdef CY_DEBUG_OPEN @@ -2324,9 +2312,8 @@ block_til_ready(struct tty_struct *tty, struct file *filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); - } + wait_event_interruptible(info->close_wait, + !(info->flags & ASYNC_CLOSING)); return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } @@ -2352,10 +2339,10 @@ block_til_ready(struct tty_struct *tty, struct file *filp, printk(KERN_DEBUG "cyc block_til_ready before block: ttyC%d, " "count = %d\n", info->line, info->count); #endif - CY_LOCK(info, flags); + spin_lock_irqsave(&cinfo->card_lock, flags); if (!tty_hung_up_p(filp)) info->count--; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&cinfo->card_lock, flags); #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc block_til_ready: (%d): decrementing count to " "%d\n", current->pid, info->count); @@ -2369,7 +2356,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); while (1) { - CY_LOCK(info, flags); + spin_lock_irqsave(&cinfo->card_lock, flags); if ((tty->termios->c_cflag & CBAUD)) { cy_writeb(base_addr + (CyCAR << index), (u_char) channel); @@ -2385,7 +2372,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, readb(base_addr + (CyMSVR2 << index))); #endif } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&cinfo->card_lock, flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || @@ -2395,16 +2382,16 @@ block_til_ready(struct tty_struct *tty, struct file *filp, break; } - CY_LOCK(info, flags); + spin_lock_irqsave(&cinfo->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); if (!(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || (readb(base_addr + (CyMSVR1 << index)) & CyDCD))) { - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&cinfo->card_lock, flags); break; } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&cinfo->card_lock, flags); if (signal_pending(current)) { retval = -ERESTARTSYS; @@ -2422,7 +2409,6 @@ block_til_ready(struct tty_struct *tty, struct file *filp, struct ZFW_CTRL __iomem *zfw_ctrl; struct BOARD_CTRL __iomem *board_ctrl; struct CH_CTRL __iomem *ch_ctrl; - int retval; base_addr = cinfo->base_addr; firm_id = base_addr + ID_ADDRESS; @@ -2441,7 +2427,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, cy_writel(&ch_ctrl[channel].rs_control, readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR); - retval = cyz_issue_cmd(info->card, + retval = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); if (retval != 0) { printk(KERN_ERR "cyc:block_til_ready " @@ -2505,13 +2491,20 @@ block_til_ready(struct tty_struct *tty, struct file *filp, static int cy_open(struct tty_struct *tty, struct file *filp) { struct cyclades_port *info; + unsigned int i; int retval, line; line = tty->index; if ((line < 0) || (NR_PORTS <= line)) { return -ENODEV; } - info = &cy_port[line]; + for (i = 0; i < NR_CARDS; i++) + if (line < cy_card[i].first_line + cy_card[i].nports && + line >= cy_card[i].first_line) + break; + if (i >= NR_CARDS) + return -ENODEV; + info = &cy_card[i].ports[line - cy_card[i].first_line]; if (info->line < 0) { return -ENODEV; } @@ -2597,8 +2590,8 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + wait_event_interruptible(info->close_wait, + !(info->flags & ASYNC_CLOSING)); return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } @@ -2694,8 +2687,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) timeout)) break; } - } else { - /* Nothing to do! */ } /* Run one more char cycle */ msleep_interruptible(jiffies_to_msecs(char_time * 5)); @@ -2710,6 +2701,7 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) static void cy_close(struct tty_struct *tty, struct file *filp) { struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; unsigned long flags; #ifdef CY_DEBUG_OTHER @@ -2720,10 +2712,12 @@ static void cy_close(struct tty_struct *tty, struct file *filp) return; } - CY_LOCK(info, flags); + card = info->card; + + spin_lock_irqsave(&card->card_lock, flags); /* If the TTY is being hung up, nothing to do */ if (tty_hung_up_p(filp)) { - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); return; } #ifdef CY_DEBUG_OPEN @@ -2753,7 +2747,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) info->count = 0; } if (info->count) { - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); return; } info->flags |= ASYNC_CLOSING; @@ -2763,16 +2757,16 @@ static void cy_close(struct tty_struct *tty, struct file *filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); if (info->closing_wait != CY_CLOSING_WAIT_NONE) { tty_wait_until_sent(tty, info->closing_wait); } - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); - if (!IS_CYC_Z(*info->card)) { - int channel = info->line - info->card->first_line; - int index = info->card->bus_index; - void __iomem *base_addr = info->card->base_addr + + if (!IS_CYC_Z(*card)) { + int channel = info->line - card->first_line; + int index = card->bus_index; + void __iomem *base_addr = card->base_addr + (cy_chip_offset[channel >> 2] << index); /* Stop accepting input */ channel &= 0x03; @@ -2782,53 +2776,52 @@ static void cy_close(struct tty_struct *tty, struct file *filp) if (info->flags & ASYNC_INITIALIZED) { /* Waiting for on-board buffers to be empty before closing the port */ - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); cy_wait_until_sent(tty, info->timeout); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); } } else { #ifdef Z_WAKE /* Waiting for on-board buffers to be empty before closing the port */ - void __iomem *base_addr = info->card->base_addr; + void __iomem *base_addr = card->base_addr; struct FIRM_ID __iomem *firm_id = base_addr + ID_ADDRESS; struct ZFW_CTRL __iomem *zfw_ctrl = base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); struct CH_CTRL __iomem *ch_ctrl = zfw_ctrl->ch_ctrl; - int channel = info->line - info->card->first_line; + int channel = info->line - card->first_line; int retval; if (readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) { - retval = cyz_issue_cmd(info->card, channel, - C_CM_IOCTLW, 0L); + retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); if (retval != 0) { printk(KERN_DEBUG "cyc:cy_close retval on " "ttyC%d was %x\n", info->line, retval); } - CY_UNLOCK(info, flags); - interruptible_sleep_on(&info->shutdown_wait); - CY_LOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); + wait_for_completion_interruptible(&info->shutdown_wait); + spin_lock_irqsave(&card->card_lock, flags); } #endif } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); tty->closing = 0; info->event = 0; info->tty = NULL; if (info->blocked_open) { - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); if (info->close_delay) { msleep_interruptible(jiffies_to_msecs (info->close_delay)); } wake_up_interruptible(&info->open_wait); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); } info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); @@ -2837,7 +2830,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) printk(KERN_DEBUG "cyc:cy_close done\n"); #endif - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } /* cy_close */ /* This routine gets called when tty_write has put something into @@ -2870,7 +2863,7 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) if (!info->xmit_buf) return 0; - CY_LOCK(info, flags); + spin_lock_irqsave(&info->card->card_lock, flags); while (1) { c = min(count, min((int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1), (int)(SERIAL_XMIT_SIZE - info->xmit_head))); @@ -2886,7 +2879,7 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) count -= c; ret += c; } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&info->card->card_lock, flags); info->idle_stats.xmit_bytes += ret; info->idle_stats.xmit_idle = jiffies; @@ -2919,9 +2912,9 @@ static void cy_put_char(struct tty_struct *tty, unsigned char ch) if (!info->xmit_buf) return; - CY_LOCK(info, flags); + spin_lock_irqsave(&info->card->card_lock, flags); if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) { - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&info->card->card_lock, flags); return; } @@ -2930,7 +2923,7 @@ static void cy_put_char(struct tty_struct *tty, unsigned char ch) info->xmit_cnt++; info->idle_stats.xmit_bytes++; info->idle_stats.xmit_idle = jiffies; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&info->card->card_lock, flags); } /* cy_put_char */ /* @@ -3224,7 +3217,7 @@ static void set_line_char(struct cyclades_port *info) channel &= 0x03; base_addr = card->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); /* tx and rx baud rate */ @@ -3319,7 +3312,7 @@ static void set_line_char(struct cyclades_port *info) if (info->tty) { clear_bit(TTY_IO_ERROR, &info->tty->flags); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } else { struct FIRM_ID __iomem *firm_id; @@ -3559,10 +3552,10 @@ static int get_lsr_info(struct cyclades_port *info, unsigned int __user * value) index = card->bus_index; base_addr = card->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); status = readb(base_addr + (CySRER << index)) & (CyTxRdy | CyTxMpty); - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); result = (status ? 0 : TIOCSER_TEMT); } else { /* Not supported yet */ @@ -3597,11 +3590,11 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) index = card->bus_index; base_addr = card->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); status = readb(base_addr + (CyMSVR1 << index)); status |= readb(base_addr + (CyMSVR2 << index)); - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); if (info->rtsdtr_inv) { result = ((status & CyRTS) ? TIOCM_DTR : 0) | @@ -3665,7 +3658,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, base_addr = card->base_addr + (cy_chip_offset[chip] << index); if (set & TIOCM_RTS) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); if (info->rtsdtr_inv) { @@ -3675,10 +3668,10 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } if (clear & TIOCM_RTS) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); if (info->rtsdtr_inv) { @@ -3688,10 +3681,10 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } if (set & TIOCM_DTR) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); if (info->rtsdtr_inv) { @@ -3707,10 +3700,10 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, readb(base_addr + (CyMSVR1 << index)), readb(base_addr + (CyMSVR2 << index))); #endif - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } if (clear & TIOCM_DTR) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); if (info->rtsdtr_inv) { @@ -3727,7 +3720,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, readb(base_addr + (CyMSVR1 << index)), readb(base_addr + (CyMSVR2 << index))); #endif - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } } else { base_addr = card->base_addr; @@ -3740,21 +3733,21 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, ch_ctrl = zfw_ctrl->ch_ctrl; if (set & TIOCM_RTS) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writel(&ch_ctrl[channel].rs_control, readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } if (clear & TIOCM_RTS) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writel(&ch_ctrl[channel].rs_control, readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } if (set & TIOCM_DTR) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writel(&ch_ctrl[channel].rs_control, readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); @@ -3762,10 +3755,10 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, printk(KERN_DEBUG "cyc:set_modem_info raising " "Z DTR\n"); #endif - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } if (clear & TIOCM_DTR) { - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writel(&ch_ctrl[channel].rs_control, readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); @@ -3773,19 +3766,18 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, printk(KERN_DEBUG "cyc:set_modem_info clearing " "Z DTR\n"); #endif - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } } else { return -ENODEV; } - CY_LOCK(info, flags); - retval = cyz_issue_cmd(info->card, - channel, C_CM_IOCTLM, 0L); + spin_lock_irqsave(&card->card_lock, flags); + retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); if (retval != 0) { printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " "was %x\n", info->line, retval); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } return 0; } /* cy_tiocmset */ @@ -3796,13 +3788,16 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, static void cy_break(struct tty_struct *tty, int break_state) { struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; unsigned long flags; if (serial_paranoia_check(info, tty->name, "cy_break")) return; - CY_LOCK(info, flags); - if (!IS_CYC_Z(*info->card)) { + card = info->card; + + spin_lock_irqsave(&card->card_lock, flags); + if (!IS_CYC_Z(*card)) { /* Let the transmit ISR take care of this (since it requires stuffing characters into the output stream). */ @@ -3810,18 +3805,18 @@ static void cy_break(struct tty_struct *tty, int break_state) if (!info->breakon) { info->breakon = 1; if (!info->xmit_cnt) { - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); start_xmit(info); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); } } } else { if (!info->breakoff) { info->breakoff = 1; if (!info->xmit_cnt) { - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); start_xmit(info); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); } } } @@ -3829,16 +3824,16 @@ static void cy_break(struct tty_struct *tty, int break_state) int retval; if (break_state == -1) { - retval = cyz_issue_cmd(info->card, - info->line - info->card->first_line, + retval = cyz_issue_cmd(card, + info->line - card->first_line, C_CM_SET_BREAK, 0L); if (retval != 0) { printk(KERN_ERR "cyc:cy_break (set) retval on " "ttyC%d was %x\n", info->line, retval); } } else { - retval = cyz_issue_cmd(info->card, - info->line - info->card->first_line, + retval = cyz_issue_cmd(card, + info->line - card->first_line, C_CM_CLR_BREAK, 0L); if (retval != 0) { printk(KERN_DEBUG "cyc:cy_break (clr) retval " @@ -3847,7 +3842,7 @@ static void cy_break(struct tty_struct *tty, int break_state) } } } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } /* cy_break */ static int @@ -3882,12 +3877,10 @@ static int set_threshold(struct cyclades_port *info, unsigned long value) info->cor3 &= ~CyREC_FIFO; info->cor3 |= value & CyREC_FIFO; - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCOR3 << index), info->cor3); cyy_issue_cmd(base_addr, CyCOR_CHANGE | CyCOR3ch, index); - CY_UNLOCK(info, flags); - } else { - /* Nothing to do! */ + spin_unlock_irqrestore(&card->card_lock, flags); } return 0; } /* set_threshold */ @@ -3910,10 +3903,8 @@ get_threshold(struct cyclades_port *info, unsigned long __user * value) tmp = readb(base_addr + (CyCOR3 << index)) & CyREC_FIFO; return put_user(tmp, value); - } else { - /* Nothing to do! */ - return 0; } + return 0; } /* get_threshold */ static int @@ -3944,11 +3935,9 @@ static int set_timeout(struct cyclades_port *info, unsigned long value) index = card->bus_index; base_addr = card->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyRTPR << index), value & 0xff); - CY_UNLOCK(info, flags); - } else { - /* Nothing to do! */ + spin_unlock_irqrestore(&card->card_lock, flags); } return 0; } /* set_timeout */ @@ -3970,10 +3959,8 @@ static int get_timeout(struct cyclades_port *info, unsigned long __user * value) tmp = readb(base_addr + (CyRTPR << index)); return put_user(tmp, value); - } else { - /* Nothing to do! */ - return 0; } + return 0; } /* get_timeout */ static int set_default_timeout(struct cyclades_port *info, unsigned long value) @@ -4089,34 +4076,22 @@ cy_ioctl(struct tty_struct *tty, struct file *file, * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: - CY_LOCK(info, flags); + spin_lock_irqsave(&info->card->card_lock, flags); /* note the counters on entry */ - cprev = info->icount; - CY_UNLOCK(info, flags); - while (1) { - interruptible_sleep_on(&info->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) { - return -ERESTARTSYS; - } - - CY_LOCK(info, flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->card->card_lock, flags); + ret_val = wait_event_interruptible(info->delta_msr_wait, ({ + cprev = cnow; + spin_lock_irqsave(&info->card->card_lock, flags); cnow = info->icount; /* atomic copy */ - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&info->card->card_lock, flags); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - return -EIO; /* no change => error */ - } - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ + ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)); + })); + break; /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) @@ -4125,9 +4100,9 @@ cy_ioctl(struct tty_struct *tty, struct file *file, * RI where only 0->1 is counted. */ case TIOCGICOUNT: - CY_LOCK(info, flags); + spin_lock_irqsave(&info->card->card_lock, flags); cnow = info->icount; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&info->card->card_lock, flags); p_cuser = argp; ret_val = put_user(cnow.cts, &p_cuser->cts); if (ret_val) @@ -4189,10 +4164,6 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line); #endif - if (tty->termios->c_cflag == old_termios->c_cflag && - (tty->termios->c_iflag & (IXON | IXANY)) == - (old_termios->c_iflag & (IXON | IXANY))) - return; set_line_char(info); if ((old_termios->c_cflag & CRTSCTS) && @@ -4282,7 +4253,7 @@ static void cy_throttle(struct tty_struct *tty) base_addr = card->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); if (info->rtsdtr_inv) { @@ -4292,7 +4263,7 @@ static void cy_throttle(struct tty_struct *tty) cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } else { info->throttle = 1; } @@ -4340,7 +4311,7 @@ static void cy_unthrottle(struct tty_struct *tty) base_addr = card->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); if (info->rtsdtr_inv) { @@ -4350,7 +4321,7 @@ static void cy_unthrottle(struct tty_struct *tty) cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } else { info->throttle = 0; } @@ -4381,17 +4352,14 @@ static void cy_stop(struct tty_struct *tty) index = cinfo->bus_index; chip = channel >> 2; channel &= 0x03; - base_addr = info->card->base_addr + - (cy_chip_offset[chip] << index); + base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&cinfo->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char)(channel & 0x0003)); /* index channel */ cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); - CY_UNLOCK(info, flags); - } else { - /* Nothing to do! */ + spin_unlock_irqrestore(&cinfo->card_lock, flags); } } /* cy_stop */ @@ -4416,16 +4384,13 @@ static void cy_start(struct tty_struct *tty) if (!IS_CYC_Z(*cinfo)) { chip = channel >> 2; channel &= 0x03; - base_addr = info->card->base_addr + - (cy_chip_offset[chip] << index); + base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - CY_LOCK(info, flags); + spin_lock_irqsave(&cinfo->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) (channel & 0x0003)); /* index channel */ cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyTxRdy); - CY_UNLOCK(info, flags); - } else { - /* Nothing to do! */ + spin_unlock_irqrestore(&cinfo->card_lock, flags); } } /* cy_start */ @@ -4446,19 +4411,19 @@ static void cy_flush_buffer(struct tty_struct *tty) card = info->card; channel = info->line - card->first_line; - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); if (IS_CYC_Z(*card)) { /* If it is a Z card, flush the on-board buffers as well */ - CY_LOCK(info, flags); + spin_lock_irqsave(&card->card_lock, flags); retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); if (retval != 0) { printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " "was %x\n", info->line, retval); } - CY_UNLOCK(info, flags); + spin_unlock_irqrestore(&card->card_lock, flags); } tty_wakeup(tty); } /* cy_flush_buffer */ @@ -4498,13 +4463,13 @@ static void cy_hangup(struct tty_struct *tty) * --------------------------------------------------------------------- */ -static void __devinit cy_init_card(struct cyclades_card *cinfo) +static int __devinit cy_init_card(struct cyclades_card *cinfo) { struct cyclades_port *info; - u32 mailbox; + u32 uninitialized_var(mailbox); unsigned int nports; unsigned short chip_number; - int index, port; + int uninitialized_var(index), port; spin_lock_init(&cinfo->card_lock); @@ -4520,10 +4485,16 @@ static void __devinit cy_init_card(struct cyclades_card *cinfo) nports = cinfo->nports = CyPORTS_PER_CHIP * cinfo->num_chips; } + cinfo->ports = kzalloc(sizeof(*cinfo->ports) * nports, GFP_KERNEL); + if (cinfo->ports == NULL) { + printk(KERN_ERR "Cyclades: cannot allocate ports\n"); + cinfo->nports = 0; + return -ENOMEM; + } + for (port = cinfo->first_line; port < cinfo->first_line + nports; port++) { - info = &cy_port[port]; - memset(info, 0, sizeof(*info)); + info = &cinfo->ports[port - cinfo->first_line]; info->magic = CYCLADES_MAGIC; info->card = cinfo; info->line = port; @@ -4534,7 +4505,7 @@ static void __devinit cy_init_card(struct cyclades_card *cinfo) INIT_WORK(&info->tqueue, do_softint); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->shutdown_wait); + init_completion(&info->shutdown_wait); init_waitqueue_head(&info->delta_msr_wait); if (IS_CYC_Z(*cinfo)) { @@ -4544,8 +4515,8 @@ static void __devinit cy_init_card(struct cyclades_card *cinfo) else info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; #ifdef CONFIG_CYZ_INTR - init_timer(&cyz_rx_full_timer[port]); - cyz_rx_full_timer[port].function = NULL; + setup_timer(&cyz_rx_full_timer[port], + cyz_rx_restart, (unsigned long)info); #endif } else { info->type = PORT_CIRRUS; @@ -4586,6 +4557,7 @@ static void __devinit cy_init_card(struct cyclades_card *cinfo) #endif } #endif + return 0; } /* initialize chips on Cyclom-Y card -- return number of valid @@ -4709,9 +4681,15 @@ static int __init cy_detect_isa(void) /* probe for CD1400... */ cy_isa_address = ioremap(isa_address, CyISA_Ywin); + if (cy_isa_address == NULL) { + printk(KERN_ERR "Cyclom-Y/ISA: can't remap base " + "address\n"); + continue; + } cy_isa_nchan = CyPORTS_PER_CHIP * cyy_init_card(cy_isa_address, 0); if (cy_isa_nchan == 0) { + iounmap(cy_isa_address); continue; } #ifdef MODULE @@ -4725,6 +4703,7 @@ static int __init cy_detect_isa(void) printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the " "IRQ could not be detected.\n", (unsigned long)cy_isa_address); + iounmap(cy_isa_address); continue; } @@ -4733,11 +4712,12 @@ static int __init cy_detect_isa(void) "more channels are available. Change NR_PORTS " "in cyclades.c and recompile kernel.\n", (unsigned long)cy_isa_address); + iounmap(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) + if (cy_card[j].base_addr == NULL) break; } if (j == NR_CARDS) { /* no more cy_cards available */ @@ -4745,6 +4725,7 @@ static int __init cy_detect_isa(void) "more cards can be used. Change NR_CARDS in " "cyclades.c and recompile kernel.\n", (unsigned long)cy_isa_address); + iounmap(cy_isa_address); return nboard; } @@ -4754,6 +4735,7 @@ static int __init cy_detect_isa(void) printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " "could not allocate IRQ#%d.\n", (unsigned long)cy_isa_address, cy_isa_irq); + iounmap(cy_isa_address); return nboard; } @@ -4764,7 +4746,12 @@ static int __init cy_detect_isa(void) cy_card[j].bus_index = 0; cy_card[j].first_line = cy_next_channel; cy_card[j].num_chips = cy_isa_nchan / 4; - cy_init_card(&cy_card[j]); + if (cy_init_card(&cy_card[j])) { + cy_card[j].base_addr = NULL; + free_irq(cy_isa_irq, &cy_card[j]); + iounmap(cy_isa_address); + continue; + } nboard++; printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: " @@ -4785,346 +4772,524 @@ static int __init cy_detect_isa(void) } /* cy_detect_isa */ #ifdef CONFIG_PCI -static void __devinit plx_init(void __iomem * addr, __u32 initctl) +static inline int __devinit cyc_isfwstr(const char *str, unsigned int size) +{ + unsigned int a; + + for (a = 0; a < size && *str; a++, str++) + if (*str & 0x80) + return -EINVAL; + + for (; a < size; a++, str++) + if (*str) + return -EINVAL; + + return 0; +} + +static inline void __devinit cyz_fpga_copy(void __iomem *fpga, u8 *data, + unsigned int size) +{ + for (; size > 0; size--) { + cy_writel(fpga, *data++); + udelay(10); + } +} + +static void __devinit plx_init(struct pci_dev *pdev, int irq, + struct RUNTIME_9060 __iomem *addr) { /* Reset PLX */ - cy_writel(addr + initctl, readl(addr + initctl) | 0x40000000); + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000); udelay(100L); - cy_writel(addr + initctl, readl(addr + initctl) & ~0x40000000); + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000); /* Reload Config. Registers from EEPROM */ - cy_writel(addr + initctl, readl(addr + initctl) | 0x20000000); + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000); udelay(100L); - cy_writel(addr + initctl, readl(addr + initctl) & ~0x20000000); + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000); + + /* For some yet unknown reason, once the PLX9060 reloads the EEPROM, + * the IRQ is lost and, thus, we have to re-write it to the PCI config. + * registers. This will remain here until we find a permanent fix. + */ + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); } -static int __devinit cy_init_Ze(struct RUNTIME_9060 __iomem *cy_pci_addr0, - int cy_pci_irq, struct pci_dev *pdev) +static int __devinit __cyz_load_fw(const struct firmware *fw, + const char *name, const u32 mailbox, void __iomem *base, + void __iomem *fpga) { - void __iomem *cy_pci_addr2; - unsigned int j; - unsigned short cy_pci_nchan; + void *ptr = fw->data; + struct zfile_header *h = ptr; + struct zfile_config *c, *cs; + struct zfile_block *b, *bs; + unsigned int a, tmp, len = fw->size; +#define BAD_FW KERN_ERR "Bad firmware: " + if (len < sizeof(*h)) { + printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h)); + return -EINVAL; + } - cy_pci_addr2 = pci_iomap(pdev, 2, CyPCI_Ze_win); + cs = ptr + h->config_offset; + bs = ptr + h->block_offset; - readl(&cy_pci_addr0->mail_box_0); - dev_dbg(&pdev->dev, "new Cyclades-Z board. FPGA not loaded\n"); + if ((void *)(cs + h->n_config) > ptr + len || + (void *)(bs + h->n_blocks) > ptr + len) { + printk(BAD_FW "too short"); + return -EINVAL; + } - /* This must be the new Cyclades-Ze/PCI. */ - cy_pci_nchan = ZE_V1_NPORTS; + if (cyc_isfwstr(h->name, sizeof(h->name)) || + cyc_isfwstr(h->date, sizeof(h->date))) { + printk(BAD_FW "bad formatted header string\n"); + return -EINVAL; + } - if ((cy_next_channel + cy_pci_nchan) > NR_PORTS) { - dev_err(&pdev->dev, "Cyclades-Ze/PCI found, but no channels " - "are available.\nChange NR_PORTS in cyclades.c " - "and recompile kernel.\n"); - return -EIO; + if (strncmp(name, h->name, sizeof(h->name))) { + printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name); + return -EINVAL; } - /* fill the next cy_card structure available */ - for (j = 0; j < NR_CARDS; j++) { - if (cy_card[j].base_addr == 0) + tmp = 0; + for (c = cs; c < cs + h->n_config; c++) { + for (a = 0; a < c->n_blocks; a++) + if (c->block_list[a] > h->n_blocks) { + printk(BAD_FW "bad block ref number in cfgs\n"); + return -EINVAL; + } + if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */ + tmp++; + } + if (!tmp) { + printk(BAD_FW "nothing appropriate\n"); + return -EINVAL; + } + + for (b = bs; b < bs + h->n_blocks; b++) + if (b->file_offset + b->size > len) { + printk(BAD_FW "bad block data offset\n"); + return -EINVAL; + } + + /* everything is OK, let's seek'n'load it */ + for (c = cs; c < cs + h->n_config; c++) + if (c->mailbox == mailbox && c->function == 0) break; + + for (a = 0; a < c->n_blocks; a++) { + b = &bs[c->block_list[a]]; + if (b->type == ZBLOCK_FPGA) { + if (fpga != NULL) + cyz_fpga_copy(fpga, ptr + b->file_offset, + b->size); + } else { + if (base != NULL) + memcpy_toio(base + b->ram_offset, + ptr + b->file_offset, b->size); + } } - if (j == NR_CARDS) { /* no more cy_cards available */ - dev_err(&pdev->dev, "Cyclades-Ze/PCI found, but no more " - "cards can be used.\nChange NR_CARDS in " - "cyclades.c and recompile kernel.\n"); - return -EIO; +#undef BAD_FW + return 0; +} + +static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, + struct RUNTIME_9060 __iomem *ctl_addr, int irq) +{ + const struct firmware *fw; + struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS; + struct CUSTOM_REG __iomem *cust = base_addr; + struct ZFW_CTRL __iomem *pt_zfwctrl; + void __iomem *tmp; + u32 mailbox, status; + unsigned int i; + int retval; + + retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev); + if (retval) { + dev_err(&pdev->dev, "can't get firmware\n"); + goto err; } -#ifdef CONFIG_CYZ_INTR - /* allocate IRQ only if board has an IRQ */ - if ((cy_pci_irq != 0) && (cy_pci_irq != 255)) { - if (request_irq(cy_pci_irq, cyz_interrupt, - IRQF_SHARED, "Cyclades-Z", - &cy_card[j])) { - dev_err(&pdev->dev, "could not allocate IRQ.\n"); - return -EIO; + + /* Check whether the firmware is already loaded and running. If + positive, skip this board */ + if (Z_FPGA_LOADED(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) { + u32 cntval = readl(base_addr + 0x190); + + udelay(100); + if (cntval != readl(base_addr + 0x190)) { + /* FW counter is working, FW is running */ + dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. " + "Skipping board.\n"); + retval = 0; + goto err_rel; } } -#endif /* CONFIG_CYZ_INTR */ - /* set cy_card */ - cy_card[j].base_addr = cy_pci_addr2; - cy_card[j].ctl_addr = cy_pci_addr0; - cy_card[j].irq = cy_pci_irq; - cy_card[j].bus_index = 1; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = -1; - cy_init_card(&cy_card[j]); - pci_set_drvdata(pdev, &cy_card[j]); - - dev_info(&pdev->dev, "Cyclades-Ze/PCI #%d found: %d channels starting " - "from port %d.\n", j + 1, cy_pci_nchan, cy_next_channel); - - for (j = cy_next_channel; j < cy_next_channel + cy_pci_nchan; j++) - tty_register_device(cy_serial_driver, j, &pdev->dev); - cy_next_channel += cy_pci_nchan; + /* start boot */ + cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) & + ~0x00030800UL); + + mailbox = readl(&ctl_addr->mail_box_0); + + if (mailbox == 0 || Z_FPGA_LOADED(ctl_addr)) { + /* stops CPU and set window to beginning of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + cy_writel(&cust->cpu_stop, 0); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + udelay(100); + } + + plx_init(pdev, irq, ctl_addr); + + if (mailbox != 0) { + /* load FPGA */ + retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL, + base_addr); + if (retval) + goto err_rel; + if (!Z_FPGA_LOADED(ctl_addr)) { + dev_err(&pdev->dev, "fw upload successful, but fw is " + "not loaded\n"); + goto err_rel; + } + } + + /* stops CPU and set window to beginning of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + cy_writel(&cust->cpu_stop, 0); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + udelay(100); + + /* clear memory */ + for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) + cy_writeb(tmp, 255); + if (mailbox != 0) { + /* set window to last 512K of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); + //sleep(1); + for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) + cy_writeb(tmp, 255); + /* set window to beginning of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + //sleep(1); + } + + retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); + release_firmware(fw); + if (retval) + goto err; + + /* finish boot and start boards */ + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + cy_writel(&cust->cpu_start, 0); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + i = 0; + while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40) + msleep(100); + if (status != ZFIRM_ID) { + if (status == ZFIRM_HLT) { + dev_err(&pdev->dev, "you need an external power supply " + "for this number of ports. Firmware halted and " + "board reset.\n"); + retval = -EIO; + goto err; + } + dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting " + "some more time\n", status); + while ((status = readl(&fid->signature)) != ZFIRM_ID && + i++ < 200) + msleep(100); + if (status != ZFIRM_ID) { + dev_err(&pdev->dev, "Board not started in 20 seconds! " + "Giving up. (fid->signature = 0x%x)\n", + status); + dev_info(&pdev->dev, "*** Warning ***: if you are " + "upgrading the FW, please power cycle the " + "system before loading the new FW to the " + "Cyclades-Z.\n"); + + if (Z_FPGA_LOADED(ctl_addr)) + plx_init(pdev, irq, ctl_addr); + + retval = -EIO; + goto err; + } + dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n", + i / 10); + } + pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr); + + dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n", + base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr), + base_addr + readl(&fid->zfwctrl_addr)); + + dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n", + readl(&pt_zfwctrl->board_ctrl.fw_version), + readl(&pt_zfwctrl->board_ctrl.n_channel)); + + if (readl(&pt_zfwctrl->board_ctrl.n_channel) == 0) { + dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please " + "check the connection between the Z host card and the " + "serial expanders.\n"); + + if (Z_FPGA_LOADED(ctl_addr)) + plx_init(pdev, irq, ctl_addr); + + dev_info(&pdev->dev, "Null number of ports detected. Board " + "reset.\n"); + retval = 0; + goto err; + } + + cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX); + cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION); + + /* + Early firmware failed to start looking for commands. + This enables firmware interrupts for those commands. + */ + cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | + (1 << 17)); + cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | + 0x00030800UL); + + plx_init(pdev, irq, ctl_addr); return 0; +err_rel: + release_firmware(fw); +err: + return retval; } static int __devinit cy_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - unsigned char cyy_rev_id; - int cy_pci_irq; - __u32 mailbox; - void __iomem *cy_pci_addr0, *cy_pci_addr2; - unsigned int device_id; - unsigned short j, cy_pci_nchan, plx_ver; - int retval; + void __iomem *addr0 = NULL, *addr2 = NULL; + char *card_name = NULL; + u32 mailbox; + unsigned int device_id, nchan = 0, card_no, i; + unsigned char plx_ver; + int retval, irq; retval = pci_enable_device(pdev); if (retval) { dev_err(&pdev->dev, "cannot enable device\n"); - return retval; + goto err; } /* read PCI configuration area */ - cy_pci_irq = pdev->irq; - pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id); - + irq = pdev->irq; device_id = pdev->device & ~PCI_DEVICE_ID_MASK; +#if defined(__alpha__) + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ + dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low " + "addresses on Alpha systems.\n"); + retval = -EIO; + goto err_dis; + } +#endif + if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) { + dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low " + "addresses\n"); + retval = -EIO; + goto err_dis; + } + + if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { + dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring " + "it...\n"); + pdev->resource[2].flags &= ~IORESOURCE_IO; + } + + retval = pci_request_regions(pdev, "cyclades"); + if (retval) { + dev_err(&pdev->dev, "failed to reserve resources\n"); + goto err_dis; + } + + retval = -EIO; if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { - dev_dbg(&pdev->dev, "Cyclom-Y/PCI found\n"); - - if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { - dev_warn(&pdev->dev, "PCI I/O bit incorrectly " - "set. Ignoring it...\n"); - pdev->resource[2].flags &= ~IORESOURCE_IO; - } + card_name = "Cyclom-Y"; - /* Although we don't use this I/O region, we should - request it from the kernel anyway, to avoid problems - with other drivers accessing it. */ - retval = pci_request_regions(pdev, "Cyclom-Y"); - if (retval) { - dev_err(&pdev->dev, "failed to reserve resources\n"); - return retval; + addr0 = pci_iomap(pdev, 0, CyPCI_Yctl); + if (addr0 == NULL) { + dev_err(&pdev->dev, "can't remap ctl region\n"); + goto err_reg; } -#if defined(__alpha__) - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ - dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for " - "low addresses on Alpha systems.\n"); - return -EIO; + addr2 = pci_iomap(pdev, 2, CyPCI_Ywin); + if (addr2 == NULL) { + dev_err(&pdev->dev, "can't remap base region\n"); + goto err_unmap; } -#endif - cy_pci_addr0 = pci_iomap(pdev, 0, CyPCI_Yctl); - cy_pci_addr2 = pci_iomap(pdev, 2, CyPCI_Ywin); - - dev_dbg(&pdev->dev, "Cyclom-Y/PCI: relocate winaddr=0x%p " - "ctladdr=0x%p\n", cy_pci_addr2, cy_pci_addr0); - cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP * - cyy_init_card(cy_pci_addr2, 1)); - if (cy_pci_nchan == 0) { + nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1); + if (nchan == 0) { dev_err(&pdev->dev, "Cyclom-Y PCI host card with no " "Serial-Modules\n"); return -EIO; } - if ((cy_next_channel + cy_pci_nchan) > NR_PORTS) { - dev_err(&pdev->dev, "Cyclom-Y/PCI found, but no " - "channels are available. Change NR_PORTS in " - "cyclades.c and recompile kernel.\n"); - return -EIO; - } - /* 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 */ - dev_err(&pdev->dev, "Cyclom-Y/PCI found, but no more " - "cards can be used. Change NR_CARDS in " - "cyclades.c and recompile kernel.\n"); - return -EIO; - } - - /* allocate IRQ */ - retval = request_irq(cy_pci_irq, cyy_interrupt, - IRQF_SHARED, "Cyclom-Y", &cy_card[j]); - if (retval) { - dev_err(&pdev->dev, "could not allocate IRQ\n"); - return retval; - } - - /* set cy_card */ - cy_card[j].base_addr = cy_pci_addr2; - cy_card[j].ctl_addr = cy_pci_addr0; - cy_card[j].irq = 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; - cy_init_card(&cy_card[j]); - pci_set_drvdata(pdev, &cy_card[j]); - - /* enable interrupts in the PCI interface */ - plx_ver = readb(cy_pci_addr2 + CyPLX_VER) & 0x0f; - switch (plx_ver) { - case PLX_9050: - - cy_writeb(cy_pci_addr0 + 0x4c, 0x43); - break; - - case PLX_9060: - case PLX_9080: - default: /* Old boards, use PLX_9060 */ + } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { + struct RUNTIME_9060 __iomem *ctl_addr; - plx_init(cy_pci_addr0, 0x6c); - /* For some yet unknown reason, once the PLX9060 reloads - the EEPROM, the IRQ is lost and, thus, we have to - re-write it to the PCI config. registers. - This will remain here until we find a permanent - fix. */ - pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, - cy_pci_irq); - - cy_writew(cy_pci_addr0 + 0x68, - readw(cy_pci_addr0 + 0x68) | 0x0900); - break; + ctl_addr = addr0 = pci_iomap(pdev, 0, CyPCI_Zctl); + if (addr0 == NULL) { + dev_err(&pdev->dev, "can't remap ctl region\n"); + goto err_reg; } - dev_info(&pdev->dev, "Cyclom-Y/PCI #%d found: %d channels " - "starting from port %d.\n", j + 1, cy_pci_nchan, - cy_next_channel); - - for (j = cy_next_channel; - j < cy_next_channel + cy_pci_nchan; j++) - tty_register_device(cy_serial_driver, j, &pdev->dev); - - cy_next_channel += cy_pci_nchan; - } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) { - dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for " - "low addresses\n"); - return -EIO; - } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { - dev_dbg(&pdev->dev, "Cyclades-Z/PCI found\n"); + /* Disable interrupts on the PLX before resetting it */ + cy_writew(addr0 + 0x68, readw(addr0 + 0x68) & ~0x0900); - cy_pci_addr0 = pci_iomap(pdev, 0, CyPCI_Zctl); + plx_init(pdev, irq, addr0); - /* Disable interrupts on the PLX before resetting it */ - cy_writew(cy_pci_addr0 + 0x68, - readw(cy_pci_addr0 + 0x68) & ~0x0900); - - plx_init(cy_pci_addr0, 0x6c); - /* For some yet unknown reason, once the PLX9060 reloads - the EEPROM, the IRQ is lost and, thus, we have to - re-write it to the PCI config. registers. - This will remain here until we find a permanent - fix. */ - pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, cy_pci_irq); - - mailbox = (__u32)readl(&((struct RUNTIME_9060 __iomem *) - cy_pci_addr0)->mail_box_0); - - if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { - dev_warn(&pdev->dev, "PCI I/O bit incorrectly " - "set. Ignoring it...\n"); - pdev->resource[2].flags &= ~IORESOURCE_IO; - } + mailbox = (u32)readl(&ctl_addr->mail_box_0); - /* Although we don't use this I/O region, we should - request it from the kernel anyway, to avoid problems - with other drivers accessing it. */ - retval = pci_request_regions(pdev, "Cyclades-Z"); - if (retval) { - dev_err(&pdev->dev, "failed to reserve resources\n"); - return retval; + addr2 = pci_iomap(pdev, 2, mailbox == ZE_V1 ? + CyPCI_Ze_win : CyPCI_Zwin); + if (addr2 == NULL) { + dev_err(&pdev->dev, "can't remap base region\n"); + goto err_unmap; } if (mailbox == ZE_V1) { - retval = cy_init_Ze(cy_pci_addr0, cy_pci_irq, pdev); - return retval; + card_name = "Cyclades-Ze"; + + readl(&ctl_addr->mail_box_0); + nchan = ZE_V1_NPORTS; } else { - cy_pci_addr2 = pci_iomap(pdev, 2, CyPCI_Zwin); - } + card_name = "Cyclades-8Zo"; - dev_dbg(&pdev->dev, "Cyclades-Z/PCI: relocate winaddr=0x%p " - "ctladdr=0x%p\n", cy_pci_addr2, cy_pci_addr0); #ifdef CY_PCI_DEBUG - if (mailbox == ZO_V1) { - cy_writel(&((struct RUNTIME_9060 *) - (cy_pci_addr0))->loc_addr_base, - WIN_CREG); - dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA id %lx, " - "ver %lx\n", (ulong)(0xff & - readl(&((struct CUSTOM_REG *) - cy_pci_addr2)->fpga_id)), - (ulong)(0xff & readl(&((struct CUSTOM_REG *) - cy_pci_addr2)->fpga_version))); - cy_writel(&((struct RUNTIME_9060 *) - cy_pci_addr0)->loc_addr_base, WIN_RAM); - } else { - dev_info(&pdev->dev, "Cyclades-Z/PCI: New Cyclades-Z " - "board. FPGA not loaded\n"); - } + if (mailbox == ZO_V1) { + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA " + "id %lx, ver %lx\n", (ulong)(0xff & + readl(&((struct CUSTOM_REG *)addr2)-> + fpga_id)), (ulong)(0xff & + readl(&((struct CUSTOM_REG *)addr2)-> + fpga_version))); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + } else { + dev_info(&pdev->dev, "Cyclades-Z/PCI: New " + "Cyclades-Z board. FPGA not loaded\n"); + } #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. - */ - if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) - cy_writel(cy_pci_addr2 + ID_ADDRESS, 0L); - - /* This must be a Cyclades-8Zo/PCI. The extendable - version will have a different device_id and will - be allocated its maximum number of ports. */ - cy_pci_nchan = 8; - - if ((cy_next_channel + cy_pci_nchan) > NR_PORTS) { - dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " - "channels are available. Change NR_PORTS in " - "cyclades.c and recompile kernel.\n"); - return -EIO; + /* 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. + */ + if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) + cy_writel(addr2 + ID_ADDRESS, 0L); + + retval = cyz_load_fw(pdev, addr2, addr0, irq); + if (retval) + goto err_unmap; + /* This must be a Cyclades-8Zo/PCI. The extendable + version will have a different device_id and will + be allocated its maximum number of ports. */ + 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 */ - dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " - "more cards can be used. Change NR_CARDS in " - "cyclades.c and recompile kernel.\n"); - return -EIO; + if ((cy_next_channel + nchan) > NR_PORTS) { + dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " + "channels are available. Change NR_PORTS in " + "cyclades.c and recompile kernel.\n"); + goto err_unmap; + } + /* fill the next cy_card structure available */ + for (card_no = 0; card_no < NR_CARDS; card_no++) { + if (cy_card[card_no].base_addr == NULL) + break; + } + if (card_no == NR_CARDS) { /* no more cy_cards available */ + dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " + "more cards can be used. Change NR_CARDS in " + "cyclades.c and recompile kernel.\n"); + goto err_unmap; + } + + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || + device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { + /* allocate IRQ */ + retval = request_irq(irq, cyy_interrupt, + IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]); + if (retval) { + dev_err(&pdev->dev, "could not allocate IRQ\n"); + goto err_unmap; } + cy_card[card_no].num_chips = nchan / 4; + } else { #ifdef CONFIG_CYZ_INTR /* allocate IRQ only if board has an IRQ */ - if ((cy_pci_irq != 0) && (cy_pci_irq != 255)) { - retval = request_irq(cy_pci_irq, cyz_interrupt, + if (irq != 0 && irq != 255) { + retval = request_irq(irq, cyz_interrupt, IRQF_SHARED, "Cyclades-Z", - &cy_card[j]); + &cy_card[card_no]); if (retval) { dev_err(&pdev->dev, "could not allocate IRQ\n"); - return retval; + goto err_unmap; } } #endif /* CONFIG_CYZ_INTR */ + cy_card[card_no].num_chips = -1; + } - /* set cy_card */ - cy_card[j].base_addr = cy_pci_addr2; - cy_card[j].ctl_addr = cy_pci_addr0; - cy_card[j].irq = cy_pci_irq; - cy_card[j].bus_index = 1; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = -1; - cy_init_card(&cy_card[j]); - pci_set_drvdata(pdev, &cy_card[j]); + /* set cy_card */ + cy_card[card_no].base_addr = addr2; + cy_card[card_no].ctl_addr = addr0; + cy_card[card_no].irq = irq; + cy_card[card_no].bus_index = 1; + cy_card[card_no].first_line = cy_next_channel; + retval = cy_init_card(&cy_card[card_no]); + if (retval) + goto err_null; - dev_info(&pdev->dev, "Cyclades-8Zo/PCI #%d found: %d channels " - "starting from port %d.\n", j + 1, cy_pci_nchan, - cy_next_channel); + pci_set_drvdata(pdev, &cy_card[card_no]); - for (j = cy_next_channel; - j < cy_next_channel + cy_pci_nchan; j++) - tty_register_device(cy_serial_driver, j, &pdev->dev); - cy_next_channel += cy_pci_nchan; + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || + device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { + /* enable interrupts in the PCI interface */ + plx_ver = readb(addr2 + CyPLX_VER) & 0x0f; + switch (plx_ver) { + case PLX_9050: + + cy_writeb(addr0 + 0x4c, 0x43); + break; + + case PLX_9060: + case PLX_9080: + default: /* Old boards, use PLX_9060 */ + plx_init(pdev, irq, addr0); + cy_writew(addr0 + 0x68, readw(addr0 + 0x68) | 0x0900); + break; + } } + dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from " + "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel); + for (i = cy_next_channel; i < cy_next_channel + nchan; i++) + tty_register_device(cy_serial_driver, i, &pdev->dev); + cy_next_channel += nchan; + return 0; +err_null: + cy_card[card_no].base_addr = NULL; + free_irq(irq, &cy_card[card_no]); +err_unmap: + pci_iounmap(pdev, addr0); + if (addr2) + pci_iounmap(pdev, addr2); +err_reg: + pci_release_regions(pdev); +err_dis: + pci_disable_device(pdev); +err: + return retval; } static void __devexit cy_pci_remove(struct pci_dev *pdev) @@ -5155,13 +5320,11 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev) pci_release_regions(pdev); cinfo->base_addr = NULL; - for (i = cinfo->first_line; i < cinfo->first_line + cinfo->nports; i++){ - cy_port[i].line = -1; - cy_port[i].magic = -1; - } for (i = cinfo->first_line; i < cinfo->first_line + cinfo->nports; i++) tty_unregister_device(cy_serial_driver, i); + cinfo->nports = 0; + kfree(cinfo->ports); } static struct pci_driver cy_pci_driver = { @@ -5177,7 +5340,7 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, int *eof, void *data) { struct cyclades_port *info; - int i; + unsigned int i, j; int len = 0; off_t begin = 0; off_t pos = 0; @@ -5191,33 +5354,34 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, len += size; /* Output one line for each known port */ - for (i = 0; i < NR_PORTS && cy_port[i].line >= 0; i++) { - info = &cy_port[i]; - - if (info->count) - size = sprintf(buf + len, "%3d %8lu %10lu %8lu %10lu " - "%8lu %9lu %6ld\n", info->line, - (cur_jifs - info->idle_stats.in_use) / HZ, - info->idle_stats.xmit_bytes, - (cur_jifs - info->idle_stats.xmit_idle) / HZ, - info->idle_stats.recv_bytes, - (cur_jifs - info->idle_stats.recv_idle) / HZ, - info->idle_stats.overruns, - (long)info->tty->ldisc.num); - else - size = sprintf(buf + len, "%3d %8lu %10lu %8lu %10lu " - "%8lu %9lu %6ld\n", - info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); - len += size; - pos = begin + len; - - if (pos < offset) { - len = 0; - begin = pos; + for (i = 0; i < NR_CARDS; i++) + for (j = 0; j < cy_card[i].nports; j++) { + info = &cy_card[i].ports[j]; + + if (info->count) + size = sprintf(buf + len, "%3d %8lu %10lu %8lu " + "%10lu %8lu %9lu %6ld\n", info->line, + (cur_jifs - info->idle_stats.in_use) / + HZ, info->idle_stats.xmit_bytes, + (cur_jifs - info->idle_stats.xmit_idle)/ + HZ, info->idle_stats.recv_bytes, + (cur_jifs - info->idle_stats.recv_idle)/ + HZ, info->idle_stats.overruns, + (long)info->tty->ldisc.num); + else + size = sprintf(buf + len, "%3d %8lu %10lu %8lu " + "%10lu %8lu %9lu %6ld\n", + info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + goto done; } - if (pos > offset + length) - goto done; - } *eof = 1; done: *start = buf + (offset - begin); /* Start of wanted data */ @@ -5272,7 +5436,7 @@ static const struct tty_operations cy_ops = { static int __init cy_init(void) { - unsigned int i, nboards; + unsigned int nboards; int retval = -ENOMEM; cy_serial_driver = alloc_tty_driver(NR_PORTS); @@ -5303,11 +5467,6 @@ static int __init cy_init(void) goto err_frtty; } - for (i = 0; i < NR_PORTS; i++) { - cy_port[i].line = -1; - cy_port[i].magic = -1; - } - /* 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 @@ -5336,6 +5495,7 @@ err: static void __exit cy_cleanup_module(void) { + struct cyclades_card *card; int i, e1; #ifndef CONFIG_CYZ_INTR @@ -5346,34 +5506,37 @@ static void __exit cy_cleanup_module(void) printk(KERN_ERR "failed to unregister Cyclades serial " "driver(%d)\n", e1); - put_tty_driver(cy_serial_driver); - #ifdef CONFIG_PCI pci_unregister_driver(&cy_pci_driver); #endif for (i = 0; i < NR_CARDS; i++) { - if (cy_card[i].base_addr) { + card = &cy_card[i]; + if (card->base_addr) { /* clear interrupt */ - cy_writeb(cy_card[i].base_addr + Cy_ClrIntr, 0); - iounmap(cy_card[i].base_addr); - if (cy_card[i].ctl_addr) - iounmap(cy_card[i].ctl_addr); - if (cy_card[i].irq + cy_writeb(card->base_addr + Cy_ClrIntr, 0); + iounmap(card->base_addr); + if (card->ctl_addr) + iounmap(card->ctl_addr); + if (card->irq #ifndef CONFIG_CYZ_INTR - && !IS_CYC_Z(cy_card[i]) + && !IS_CYC_Z(*card) #endif /* CONFIG_CYZ_INTR */ ) - free_irq(cy_card[i].irq, &cy_card[i]); - for (e1 = cy_card[i].first_line; - e1 < cy_card[i].first_line + - cy_card[i].nports; e1++) + free_irq(card->irq, card); + for (e1 = card->first_line; + e1 < card->first_line + + card->nports; e1++) tty_unregister_device(cy_serial_driver, e1); + kfree(card->ports); } } + + put_tty_driver(cy_serial_driver); } /* cy_cleanup_module */ module_init(cy_init); module_exit(cy_cleanup_module); MODULE_LICENSE("GPL"); +MODULE_VERSION(CY_VERSION);