specialix.c
来自「linux 内核源代码」· C语言 代码 · 共 2,562 行 · 第 1/5 页
C
2,562 行
#if 0 /* It's time to find IRQ for this board */ for (retries = 0; retries < 5 && irqs <= 0; retries++) { irqs = probe_irq_on(); sx_init_CD186x(bp); /* Reset CD186x chip */ sx_out(bp, CD186x_CAR, 2); /* Select port 2 */ sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */ sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */ msleep(50); irqs = probe_irq_off(irqs); dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR)); dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR)); dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR)); dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR)); dprintk (SX_DEBUG_INIT, "\n"); /* Reset CD186x again */ if (!sx_init_CD186x(bp)) { /* Hmmm. This is dead code anyway. */ } dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n", val1, val2, val3); }#if 0 if (irqs <= 0) { printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n", board_No(bp), bp->base); sx_release_io_range(bp); func_exit(); return 1; }#endif printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs); if (irqs > 0) bp->irq = irqs;#endif /* Reset CD186x again */ if (!sx_init_CD186x(bp)) { sx_release_io_range(bp); func_exit(); return 1; } sx_request_io_range(bp); bp->flags |= SX_BOARD_PRESENT; /* Chip revcode pkgtype GFRCR SRCR bit 7 CD180 rev B 0x81 0 CD180 rev C 0x82 0 CD1864 rev A 0x82 1 CD1865 rev A 0x83 1 -- Do not use!!! Does not work. CD1865 rev B 0x84 1 -- Thanks to Gwen Wang, Cirrus Logic. */ switch (sx_in_off(bp, CD186x_GFRCR)) { case 0x82:chip = 1864;rev='A';break; case 0x83:chip = 1865;rev='A';break; case 0x84:chip = 1865;rev='B';break; case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */ default:chip=-1;rev='x'; } dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );#ifdef SPECIALIX_TIMER setup_timer(&missed_irq_timer, missed_irq, (unsigned long)bp); mod_timer(&missed_irq_timer, jiffies + sx_poll);#endif printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", board_No(bp), bp->base, bp->irq, chip, rev); func_exit(); return 0;}/* * * Interrupt processing routines. * */static inline void sx_mark_event(struct specialix_port * port, int event){ func_enter(); set_bit(event, &port->event); schedule_work(&port->tqueue); func_exit();}static inline struct specialix_port * sx_get_port(struct specialix_board * bp, unsigned char const * what){ unsigned char channel; struct specialix_port * port = NULL; channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel); if (channel < CD186x_NCH) { port = &sx_port[board_No(bp) * SX_NPORT + channel]; dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED); if (port->flags & ASYNC_INITIALIZED) { dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port); func_exit(); return port; } } printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", board_No(bp), what, channel); return NULL;}static inline void sx_receive_exc(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char status; unsigned char ch, flag; func_enter(); port = sx_get_port(bp, "Receive"); if (!port) { dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n"); func_exit(); return; } tty = port->tty; status = sx_in(bp, CD186x_RCSR); dprintk (SX_DEBUG_RX, "status: 0x%x\n", status); if (status & RCSR_OE) { port->overrun++; dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n", board_No(bp), port_No(port), port->overrun); } status &= port->mark_mask; /* This flip buffer check needs to be below the reading of the status register to reset the chip's IRQ.... */ if (tty_buffer_request_room(tty, 1) == 0) { dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n", board_No(bp), port_No(port)); func_exit(); return; } ch = sx_in(bp, CD186x_RDR); if (!status) { func_exit(); return; } if (status & RCSR_TOUT) { printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", board_No(bp), port_No(port)); func_exit(); return; } else if (status & RCSR_BREAK) { dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n", board_No(bp), port_No(port)); flag = TTY_BREAK; if (port->flags & ASYNC_SAK) do_SAK(tty); } else if (status & RCSR_PE) flag = TTY_PARITY; else if (status & RCSR_FE) flag = TTY_FRAME; else if (status & RCSR_OE) flag = TTY_OVERRUN; else flag = TTY_NORMAL; if(tty_insert_flip_char(tty, ch, flag)) tty_flip_buffer_push(tty); func_exit();}static inline void sx_receive(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char count; func_enter(); if (!(port = sx_get_port(bp, "Receive"))) { dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n"); func_exit(); return; } tty = port->tty; count = sx_in(bp, CD186x_RDCR); dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count); port->hits[count > 8 ? 9 : count]++; tty_buffer_request_room(tty, count); while (count--) tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL); tty_flip_buffer_push(tty); func_exit();}static inline void sx_transmit(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char count; func_enter(); if (!(port = sx_get_port(bp, "Transmit"))) { func_exit(); return; } dprintk (SX_DEBUG_TX, "port: %p\n", port); tty = port->tty; if (port->IER & IER_TXEMPTY) { /* FIFO drained */ sx_out(bp, CD186x_CAR, port_No(port)); port->IER &= ~IER_TXEMPTY; sx_out(bp, CD186x_IER, port->IER); func_exit(); return; } if ((port->xmit_cnt <= 0 && !port->break_length) || tty->stopped || tty->hw_stopped) { sx_out(bp, CD186x_CAR, port_No(port)); port->IER &= ~IER_TXRDY; sx_out(bp, CD186x_IER, port->IER); func_exit(); return; } if (port->break_length) { if (port->break_length > 0) { if (port->COR2 & COR2_ETC) { sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_SBRK); port->COR2 &= ~COR2_ETC; } count = min_t(int, port->break_length, 0xff); sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_DELAY); sx_out(bp, CD186x_TDR, count); if (!(port->break_length -= count)) port->break_length--; } else { sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_EBRK); sx_out(bp, CD186x_COR2, port->COR2); sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_CORCHG2); port->break_length = 0; } func_exit(); return; } count = CD186x_NFIFO; do { sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); if (--port->xmit_cnt <= 0) break; } while (--count > 0); if (port->xmit_cnt <= 0) { sx_out(bp, CD186x_CAR, port_No(port)); port->IER &= ~IER_TXRDY; sx_out(bp, CD186x_IER, port->IER); } if (port->xmit_cnt <= port->wakeup_chars) sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); func_exit();}static inline void sx_check_modem(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char mcr; int msvr_cd; dprintk (SX_DEBUG_SIGNALS, "Modem intr. "); if (!(port = sx_get_port(bp, "Modem"))) return; tty = port->tty; mcr = sx_in(bp, CD186x_MCR); printk ("mcr = %02x.\n", mcr); if ((mcr & MCR_CDCHG)) { dprintk (SX_DEBUG_SIGNALS, "CD just changed... "); msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD; if (msvr_cd) { dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n"); wake_up_interruptible(&port->open_wait); } else { dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n"); schedule_work(&port->tqueue_hangup); } }#ifdef SPECIALIX_BRAIN_DAMAGED_CTS if (mcr & MCR_CTSCHG) { if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } sx_out(bp, CD186x_IER, port->IER); } if (mcr & MCR_DSSXHG) { if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } sx_out(bp, CD186x_IER, port->IER); }#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ /* Clear change bits */ sx_out(bp, CD186x_MCR, 0);}/* The main interrupt processing routine */static irqreturn_t sx_interrupt(int irq, void *dev_id){ unsigned char status; unsigned char ack; struct specialix_board *bp; unsigned long loop = 0; int saved_reg; unsigned long flags; func_enter(); bp = dev_id; spin_lock_irqsave(&bp->lock, flags); dprintk (SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __FUNCTION__, port_No(sx_get_port(bp, "INT")), SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1); if (!(bp->flags & SX_BOARD_ACTIVE)) { dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq); spin_unlock_irqrestore(&bp->lock, flags); func_exit(); return IRQ_NONE; } saved_reg = bp->reg; while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) & (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint)))) { if (status & SRSR_RREQint) { ack = sx_in(bp, CD186x_RRAR); if (ack == (SX_ID | GIVR_IT_RCV)) sx_receive(bp); else if (ack == (SX_ID | GIVR_IT_REXC)) sx_receive_exc(bp); else printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n", board_No(bp), status, ack); } else if (status & SRSR_TREQint) { ack = sx_in(bp, CD186x_TRAR); if (ack == (SX_ID | GIVR_IT_TX)) sx_transmit(bp); else printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n", board_No(bp), status, ack, port_No (sx_get_port (bp, "Int"))); } else if (status & SRSR_MREQint) { ack = sx_in(bp, CD186x_MRAR); if (ack == (SX_ID | GIVR_IT_MODEM)) sx_check_modem(bp); else printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n", board_No(bp), status, ack); } sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ } bp->reg = saved_reg; outb (bp->reg, bp->base + SX_ADDR_REG); spin_unlock_irqrestore(&bp->lock, flags); func_exit(); return IRQ_HANDLED;}/* * Routines for open & close processing. */static void turn_ints_off (struct specialix_board *bp){ unsigned long flags; func_enter(); if (bp->flags & SX_BOARD_IS_PCI) { /* This was intended for enabeling the interrupt on the * PCI card. However it seems that it's already enabled * and as PCI interrupts can be shared, there is no real * reason to have to turn it off. */ } spin_lock_irqsave(&bp->lock, flags); (void) sx_in_off (bp, 0); /* Turn off interrupts. */ spin_unlock_irqrestore(&bp->lock, flags); func_exit();}static void turn_ints_on (struct specialix_board *bp){ unsigned long flags; func_enter(); if (bp->flags & SX_BOARD_IS_PCI) { /* play with the PCI chip. See comment above. */ } spin_lock_irqsave(&bp->lock, flags); (void) sx_in (bp, 0); /* Turn ON interrupts. */ spin_unlock_irqrestore(&bp->lock, flags); func_exit();}/* Called with disabled interrupts */static inline int sx_setup_board(struct specialix_board * bp){ int error; if (bp->flags & SX_BOARD_ACTIVE) return 0; if (bp->flags & SX_BOARD_IS_PCI) error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp); else error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp); if (error) return error; turn_ints_on (bp); bp->flags |= SX_BOARD_ACTIVE; return 0;}/* Called with disabled interrupts */static inline void sx_shutdown_board(struct specialix_board *bp){ func_enter(); if (!(bp->flags & SX_BOARD_ACTIVE)) { func_exit(); return; } bp->flags &= ~SX_BOARD_ACTIVE; dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n", bp->irq, board_No (bp)); free_irq(bp->irq, bp); turn_ints_off (bp);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?