📄 specialix.c
字号:
printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n", board_No(bp), bp->base); return 1; } /* Check the DSR lines that Specialix uses as board identification */ val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR); val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", board_No(bp), val1, val2);#endif /* They managed to switch the bit order between the docs and the IO8+ card. The new PCI card now conforms to old docs. They changed the PCI docs to reflect the situation on the old card. */ val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2; if (val1 != val2) { printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", board_No(bp), val2, bp->base, val1); return 1; }#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 */ sx_long_delay(HZ/20); irqs = probe_irq_off(irqs);#if SPECIALIX_DEBUG > 2 printk (KERN_DEBUG "SRSR = %02x, ", sx_in(bp, CD186x_SRSR)); printk ( "TRAR = %02x, ", sx_in(bp, CD186x_TRAR)); printk ( "GIVR = %02x, ", sx_in(bp, CD186x_GIVR)); printk ( "GICR = %02x, ", sx_in(bp, CD186x_GICR)); printk ( "\n");#endif /* Reset CD186x again */ if (!sx_init_CD186x(bp)) { /* Hmmm. This is dead code anyway. */ }#if SPECIALIX_DEBUG > 2 printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", val1, val2, val3); #endif } #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); 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)) { return -EIO; } 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'; }#if SPECIALIX_DEBUG > 2 printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );#endif#ifdef SPECIALIX_TIMER init_timer (&missed_irq_timer); missed_irq_timer.function = missed_irq; missed_irq_timer.data = (unsigned long) bp; missed_irq_timer.expires = jiffies + HZ; add_timer (&missed_irq_timer);#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); return 0;}/* * * Interrupt processing routines. * */extern inline void sx_mark_event(struct specialix_port * port, int event){ /* * I'm not quite happy with current scheme all serial * drivers use their own BH routine. * It seems this easily can be done with one BH routine * serving for all serial drivers. * For now I must introduce another one - SPECIALIX_BH. * Still hope this will be changed in near future. * -- Dmitry. */ set_bit(event, &port->event); queue_task(&port->tqueue, &tq_specialix); mark_bh(SPECIALIX_BH);}extern inline struct specialix_port * sx_get_port(struct specialix_board * bp, unsigned char const * what){ unsigned char channel; struct specialix_port * port; channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; if (channel < CD186x_NCH) { port = &sx_port[board_No(bp) * SX_NPORT + channel]; if (port->flags & ASYNC_INITIALIZED) { return port; } } printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", board_No(bp), what, channel); return NULL;}extern inline void sx_receive_exc(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char status; unsigned char ch; if (!(port = sx_get_port(bp, "Receive"))) return; tty = port->tty; if (tty->flip.count >= TTY_FLIPBUF_SIZE) { printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", board_No(bp), port_No(port)); return; } #ifdef SX_REPORT_OVERRUN status = sx_in(bp, CD186x_RCSR); if (status & RCSR_OE) { port->overrun++;#if SPECIALIX_DEBUG printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", board_No(bp), port_No(port), port->overrun);#endif } status &= port->mark_mask;#else status = sx_in(bp, CD186x_RCSR) & port->mark_mask;#endif ch = sx_in(bp, CD186x_RDR); if (!status) { return; } if (status & RCSR_TOUT) { printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", board_No(bp), port_No(port)); return; } else if (status & RCSR_BREAK) {#ifdef SPECIALIX_DEBUG printk(KERN_DEBUG "sx%d: port %d: Handling break...\n", board_No(bp), port_No(port));#endif *tty->flip.flag_buf_ptr++ = TTY_BREAK; if (port->flags & ASYNC_SAK) do_SAK(tty); } else if (status & RCSR_PE) *tty->flip.flag_buf_ptr++ = TTY_PARITY; else if (status & RCSR_FE) *tty->flip.flag_buf_ptr++ = TTY_FRAME; else if (status & RCSR_OE) *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; else *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; tty->flip.count++; queue_task(&tty->flip.tqueue, &tq_timer);}extern inline void sx_receive(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char count; if (!(port = sx_get_port(bp, "Receive"))) return; tty = port->tty; count = sx_in(bp, CD186x_RDCR); #ifdef SX_REPORT_FIFO port->hits[count > 8 ? 9 : count]++;#endif while (count--) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", board_No(bp), port_No(port)); break; } *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR); *tty->flip.flag_buf_ptr++ = 0; tty->flip.count++; } queue_task(&tty->flip.tqueue, &tq_timer);}extern inline void sx_transmit(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char count; if (!(port = sx_get_port(bp, "Transmit"))) return; 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); 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); 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(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; } 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);}extern inline void sx_check_modem(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char mcr;#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "Modem intr. ");#endif 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)) {#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "CD just changed... ");#endif if (sx_in(bp, CD186x_MSVR) & MSVR_CD) {#ifdef SPECIALIX_DEBUG printk ( "Waking up guys in open.\n");#endif wake_up_interruptible(&port->open_wait); } else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && (port->flags & ASYNC_CALLOUT_NOHUP))) {#ifdef SPECIALIX_DEBUG printk ( "Sending HUP.\n");#endif MOD_INC_USE_COUNT; if (schedule_task(&port->tqueue_hangup) == 0) MOD_DEC_USE_COUNT; } else {#ifdef SPECIALIX_DEBUG printk ( "Don't need to send HUP.\n");#endif } } #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 void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs){ unsigned char status; unsigned char ack; struct specialix_board *bp; unsigned long loop = 0; int saved_reg; bp = dev_id; if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq);#endif return; } 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: Bad receive ack 0x%02x.\n", board_No(bp), 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: Bad transmit ack 0x%02x.\n", board_No(bp), ack); } 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: Bad modem ack 0x%02x.\n", board_No(bp), ack); } sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ } bp->reg = saved_reg; outb (bp->reg, bp->base + SX_ADDR_REG);}/* * Routines for open & close processing. */void turn_ints_off (struct specialix_board *bp){ 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. */ } (void) sx_in_off (bp, 0); /* Turn off interrupts. */}void turn_ints_on (struct specialix_board *bp){ if (bp->flags & SX_BOARD_IS_PCI) { /* play with the PCI chip. See comment above. */ } (void) sx_in (bp, 0); /* Turn ON interrupts. */}/* Called with disabled interrupts */extern inline int sx_setup_board(struct specialix_board * bp){ int error;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -