cyclades.c
来自「linux 内核源代码」· C语言 代码 · 共 2,048 行 · 第 1/5 页
C
2,048 行
/* end of service */ cy_writeb(base_addr + (CyTIR << index), save_xir & 0x3f); cy_writeb(base_addr + (CyCAR << index), save_car);}static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, void __iomem *base_addr){ struct cyclades_port *info; 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 = 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) 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->delta_msr_wait); } if ((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)) { if (!(mdm_status & CyDCD)) { tty_hangup(info->tty); info->flags &= ~ASYNC_NORMAL_ACTIVE; } wake_up_interruptible(&info->open_wait); } if ((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)) { if (info->tty->hw_stopped) { if (mdm_status & CyCTS) { /* cy_start isn't used because... !!! */ info->tty->hw_stopped = 0; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyTxRdy); tty_wakeup(info->tty); } } else { if (!(mdm_status & CyCTS)) { /* cy_stop isn't used because ... !!! */ info->tty->hw_stopped = 1; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); } } }/* if (mdm_change & CyDSR) { } if (mdm_change & CyRI) { }*/end: /* end of service */ cy_writeb(base_addr + (CyMIR << index), save_xir & 0x3f); cy_writeb(base_addr + (CyCAR << index), 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 *//***********************************************************//********* End of block of Cyclom-Y specific code **********//******** Start of block of Cyclades-Z specific code *********//***********************************************************/static intcyz_fetch_msg(struct cyclades_card *cinfo, __u32 * channel, __u8 * cmd, __u32 * param){ struct FIRM_ID __iomem *firm_id; struct ZFW_CTRL __iomem *zfw_ctrl; struct BOARD_CTRL __iomem *board_ctrl; unsigned long loc_doorbell; firm_id = cinfo->base_addr + ID_ADDRESS; if (!ISZLOADED(*cinfo)) { return -1; } zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); board_ctrl = &zfw_ctrl->board_ctrl; loc_doorbell = readl(&((struct RUNTIME_9060 __iomem *) (cinfo->ctl_addr))->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(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))-> loc_doorbell, 0xffffffff); return 1; } return 0;} /* cyz_fetch_msg */static intcyz_issue_cmd(struct cyclades_card *cinfo, __u32 channel, __u8 cmd, __u32 param){ struct FIRM_ID __iomem *firm_id; struct ZFW_CTRL __iomem *zfw_ctrl; struct BOARD_CTRL __iomem *board_ctrl; __u32 __iomem *pci_doorbell; unsigned int index; firm_id = cinfo->base_addr + ID_ADDRESS; if (!ISZLOADED(*cinfo)) { return -1; } zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); board_ctrl = &zfw_ctrl->board_ctrl; index = 0; pci_doorbell = &((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->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 BUF_CTRL __iomem *buf_ctrl){ struct cyclades_card *cinfo = info->card; struct tty_struct *tty = info->tty; 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 BUF_CTRL __iomem *buf_ctrl){ struct cyclades_card *cinfo = info->card; struct tty_struct *tty = info->tty; 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->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->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 tty_struct *tty; struct cyclades_port *info; static struct FIRM_ID __iomem *firm_id; static struct ZFW_CTRL __iomem *zfw_ctrl; static struct BOARD_CTRL __iomem *board_ctrl; static struct CH_CTRL __iomem *ch_ctrl; static struct BUF_CTRL __iomem *buf_ctrl; __u32 channel; __u8 cmd; __u32 param; __u32 hw_ver, fw_ver; int special_count; int delta_count; firm_id = cinfo->base_addr + ID_ADDRESS; zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); board_ctrl = &zfw_ctrl->board_ctrl; fw_ver = readl(&board_ctrl->fw_version); hw_ver = readl(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))-> mail_box_0); while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { special_count = 0; delta_count = 0; info = &cinfo->ports[channel]; if ((tty = info->tty) == NULL) continue;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?