📄 macserial.c
字号:
if (info->tty && C_CRTSCTS(info->tty)) { /* * For some reason, on the Power Macintosh, * it seems that the CTS bit is 1 when CTS is * *negated* and 0 when it is asserted. * The DCD bit doesn't seem to be inverted * like this. */ if ((status & CTS) == 0) { if (info->tx_stopped) {#ifdef SERIAL_DEBUG_FLOW printk("CTS up\n");#endif info->tx_stopped = 0; if (!info->tx_active) transmit_chars(info); } } else {#ifdef SERIAL_DEBUG_FLOW printk("CTS down\n");#endif info->tx_stopped = 1; } } /* Clear status condition... */ write_zsreg(info->zs_channel, 0, RES_EXT_INT); info->read_reg_zero = status;}static _INLINE_ void receive_special_dma(struct mac_serial *info){ unsigned char stat, flag; volatile struct dbdma_regs *rd = &info->rx->dma; int where = RX_BUF_SIZE; spin_lock(&info->rx_dma_lock); if ((ld_le32(&rd->status) & ACTIVE) != 0) dbdma_flush(rd); if (in_le32(&rd->cmdptr) == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1)) where -= in_le16(&info->rx->res_count); where--; stat = read_zsreg(info->zs_channel, R1); flag = stat_to_flag(stat); if (flag) { info->rx_flag_buf[info->rx_cbuf][where] = flag; /* reset the error indication */ write_zsreg(info->zs_channel, 0, ERR_RES); } spin_unlock(&info->rx_dma_lock);}/* * This is the serial driver's generic interrupt routine */static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct mac_serial *info = (struct mac_serial *) dev_id; unsigned char zs_intreg; int shift; if (!(info->flags & ZILOG_INITIALIZED)) { printk("rs_interrupt: irq %d, port not initialized\n", irq); disable_irq(irq); return; } /* NOTE: The read register 3, which holds the irq status, * does so for both channels on each chip. Although * the status value itself must be read from the A * channel and is only valid when read from channel A. * Yes... broken hardware... */#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT) if (info->zs_chan_a == info->zs_channel) shift = 3; /* Channel A */ else shift = 0; /* Channel B */ for (;;) { zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg);#endif if ((zs_intreg & CHAN_IRQMASK) == 0) break; if (zs_intreg & CHBRxIP) { /* If we are doing DMA, we only ask for interrupts on characters with errors or special conditions. */ if (info->dma_initted) receive_special_dma(info); else receive_chars(info, regs); } if (zs_intreg & CHBTxIP) transmit_chars(info); if (zs_intreg & CHBEXT) status_handle(info); }}/* Transmit DMA interrupt - not used at present */static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs){}/* * Receive DMA interrupt. */static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs){ struct mac_serial *info = (struct mac_serial *) dev_id; volatile struct dbdma_cmd *cd; if (!info->dma_initted) return; spin_lock(&info->rx_dma_lock); /* First, confirm that this interrupt is, indeed, coming */ /* from Rx DMA */ cd = info->rx_cmds[info->rx_cbuf] + 2; if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) { spin_unlock(&info->rx_dma_lock); return; } if (info->rx_fbuf != RX_NO_FBUF) { info->rx_cbuf = info->rx_fbuf; if (++info->rx_fbuf == info->rx_nbuf) info->rx_fbuf = 0; if (info->rx_fbuf == info->rx_ubuf) info->rx_fbuf = RX_NO_FBUF; } spin_unlock(&info->rx_dma_lock);}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * ------------------------------------------------------------ */static void rs_stop(struct tty_struct *tty){ struct mac_serial *info = (struct mac_serial *)tty->driver_data;#ifdef SERIAL_DEBUG_STOP printk("rs_stop %ld....\n", tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "rs_stop")) return;#if 0 save_flags(flags); cli(); if (info->curregs[5] & TxENAB) { info->curregs[5] &= ~TxENAB; info->pendregs[5] &= ~TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); } restore_flags(flags);#endif}static void rs_start(struct tty_struct *tty){ struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags;#ifdef SERIAL_DEBUG_STOP printk("rs_start %ld....\n", tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "rs_start")) return; save_flags(flags); cli();#if 0 if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) { info->curregs[5] |= TxENAB; info->pendregs[5] = info->curregs[5]; write_zsreg(info->zs_channel, 5, info->curregs[5]); }#else if (info->xmit_cnt && info->xmit_buf && !info->tx_active) { transmit_chars(info); }#endif restore_flags(flags);}/* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh(void){ run_task_queue(&tq_serial);}static void do_softint(void *private_){ struct mac_serial *info = (struct mac_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); }}static int startup(struct mac_serial * info, int can_sleep){ int delay; OPNDBG("startup() (ttyS%d, irq %d)\n", info->line, info->irq); if (info->flags & ZILOG_INITIALIZED) { OPNDBG(" -> already inited\n"); return 0; } if (!info->xmit_buf) { info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL); if (!info->xmit_buf) return -ENOMEM; } OPNDBG("starting up ttyS%d (irq %d)...\n", info->line, info->irq); delay = set_scc_power(info, 1); setup_scc(info); OPNDBG("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq); info->flags |= ZILOG_INITIALIZED; enable_irq(info->irq); if (info->dma_initted) { enable_irq(info->rx_dma_irq); } if (delay) { if (can_sleep) { /* we need to wait a bit before using the port */ current->state = TASK_INTERRUPTIBLE; schedule_timeout(delay * HZ / 1000); } else mdelay(delay); } return 0;}static _INLINE_ void rxdma_start(struct mac_serial * info, int current){ volatile struct dbdma_regs *rd = &info->rx->dma; volatile struct dbdma_cmd *cd = info->rx_cmds[current];//printk(KERN_DEBUG "SCC: rxdma_start\n"); st_le32(&rd->cmdptr, virt_to_bus(cd)); out_le32(&rd->control, (RUN << 16) | RUN);}static void rxdma_to_tty(struct mac_serial *info){ struct tty_struct *tty = info->tty; volatile struct dbdma_regs *rd = &info->rx->dma; unsigned long flags; int residue, available, space, do_queue; if (!tty) return; do_queue = 0; spin_lock_irqsave(&info->rx_dma_lock, flags);more: space = TTY_FLIPBUF_SIZE - tty->flip.count; if (!space) { do_queue++; goto out; } residue = 0; if (info->rx_ubuf == info->rx_cbuf) { if ((ld_le32(&rd->status) & ACTIVE) != 0) { dbdma_flush(rd); if (in_le32(&rd->cmdptr) == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1)) residue = in_le16(&info->rx->res_count); } } available = RX_BUF_SIZE - residue - info->rx_done_bytes; if (available > space) available = space; if (available) { memcpy(tty->flip.char_buf_ptr, info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes, available); memcpy(tty->flip.flag_buf_ptr, info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, available); tty->flip.char_buf_ptr += available; tty->flip.count += available; tty->flip.flag_buf_ptr += available; memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, 0, available); info->rx_done_bytes += available; do_queue++; } if (info->rx_done_bytes == RX_BUF_SIZE) { volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf]; if (info->rx_ubuf == info->rx_cbuf) goto out; /* mark rx_char_buf[rx_ubuf] free */ st_le16(&cd->command, DBDMA_NOP); cd++; st_le32(&cd->cmd_dep, 0); st_le32((unsigned int *)&cd->res_count, 0); cd++; st_le16(&cd->xfer_status, 0); if (info->rx_fbuf == RX_NO_FBUF) { info->rx_fbuf = info->rx_ubuf; if (!(ld_le32(&rd->status) & ACTIVE)) { dbdma_reset(&info->rx->dma); rxdma_start(info, info->rx_ubuf); info->rx_cbuf = info->rx_ubuf; } } info->rx_done_bytes = 0; if (++info->rx_ubuf == info->rx_nbuf) info->rx_ubuf = 0; if (info->rx_fbuf == info->rx_ubuf) info->rx_fbuf = RX_NO_FBUF; goto more; }out: spin_unlock_irqrestore(&info->rx_dma_lock, flags); if (do_queue) queue_task(&tty->flip.tqueue, &tq_timer);}static void poll_rxdma(void *private_){ struct mac_serial *info = (struct mac_serial *) private_; unsigned long flags; rxdma_to_tty(info); spin_lock_irqsave(&info->rx_dma_lock, flags); mod_timer(&info->poll_dma_timer, RX_DMA_TIMER); spin_unlock_irqrestore(&info->rx_dma_lock, flags);}static void dma_init(struct mac_serial * info){ int i, size; volatile struct dbdma_cmd *cd; unsigned char *p; info->rx_nbuf = 8; /* various mem set up */ size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2) + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds) + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf)) * info->rx_nbuf; info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA); if (info->dma_priv == NULL) return; memset(info->dma_priv, 0, size); info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv; info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf); info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf; p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf); for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) info->rx_char_buf[i] = p; for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) info->rx_flag_buf[i] = p; /* a bit of DMA programming */ cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p); st_le16(&cd->command, DBDMA_NOP); cd++; st_le16(&cd->req_count, RX_BUF_SIZE); st_le16(&cd->command, INPUT_MORE); st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0])); cd++; st_le16(&cd->req_count, 4); st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); st_le32(&cd->phy_addr, virt_to_bus(cd-2)); st_le32(&cd->cmd_dep, DBDMA_STOP); for (i = 1; i < info->rx_nbuf; i++) { info->rx_cmds[i] = ++cd; st_le16(&cd->command, DBDMA_NOP); cd++; st_le16(&cd->req_count, RX_BUF_SIZE); st_le16(&cd->command, INPUT_MORE); st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i])); cd++; st_le16(&cd->req_count, 4); st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); st_le32(&cd->phy_addr, virt_to_bus(cd-2)); st_le32(&cd->cmd_dep, DBDMA_STOP); } cd++; st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS); st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0])); /* setup DMA to our liking */ dbdma_reset(&info->rx->dma); st_le32(&info->rx->dma.intr_sel, 0x10001); st_le32(&info->rx->dma.br_sel, 0x10001); out_le32(&info->rx->dma.wait_sel, 0x10001); /* set various flags */ info->rx_ubuf = 0; info->rx_cbuf = 0; info->rx_fbuf = info->rx_ubuf + 1; if (info->rx_fbuf == info->rx_nbuf) info->rx_fbuf = RX_NO_FBUF; info->rx_done_bytes = 0; /* setup polling */ init_timer(&info->poll_dma_timer); info->poll_dma_timer.function = (void *)&poll_rxdma; info->poll_dma_timer.data = (unsigned long)info; info->dma_initted = 1;}static int setup_scc(struct mac_serial * info){ unsigned long flags; OPNDBG("setting up ttys%d SCC...\n", info->line); save_flags(flags); cli(); /* Disable interrupts */ /* * Reset the chip. */ write_zsreg(info->zs_channel, 9, (info->zs_channel == info->zs_chan_a? CHRA: CHRB)); udelay(10); write_zsreg(info->zs_channel, 9, 0); /* * Clear the receive FIFO. */ ZS_CLEARFIFO(info->zs_channel); info->xmit_fifo_size = 1; /* * Reset DMAs */ if (info->has_dma) dma_init(info); /* * Clear the interrupt registers. */ write_zsreg(info->zs_channel, 0, ERR_RES); write_zsreg(info->zs_channel, 0, RES_H_IUS); /* * Turn on RTS and DTR.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -