#include "dgnc_cls.h"
#include "dgnc_tty.h"
-static inline void cls_parse_isr(struct dgnc_board *brd, uint port);
-static inline void cls_clear_break(struct channel_t *ch, int force);
-static inline void cls_set_cts_flow_control(struct channel_t *ch);
-static inline void cls_set_rts_flow_control(struct channel_t *ch);
-static inline void cls_set_ixon_flow_control(struct channel_t *ch);
-static inline void cls_set_ixoff_flow_control(struct channel_t *ch);
-static inline void cls_set_no_output_flow_control(struct channel_t *ch);
-static inline void cls_set_no_input_flow_control(struct channel_t *ch);
-static void cls_parse_modem(struct channel_t *ch, unsigned char signals);
-static void cls_tasklet(unsigned long data);
-static void cls_vpd(struct dgnc_board *brd);
-static void cls_uart_init(struct channel_t *ch);
-static void cls_uart_off(struct channel_t *ch);
-static int cls_drain(struct tty_struct *tty, uint seconds);
-static void cls_param(struct tty_struct *tty);
-static void cls_assert_modem_signals(struct channel_t *ch);
-static void cls_flush_uart_write(struct channel_t *ch);
-static void cls_flush_uart_read(struct channel_t *ch);
-static void cls_disable_receiver(struct channel_t *ch);
-static void cls_enable_receiver(struct channel_t *ch);
-static void cls_send_break(struct channel_t *ch, int msecs);
-static void cls_send_start_character(struct channel_t *ch);
-static void cls_send_stop_character(struct channel_t *ch);
-static void cls_copy_data_from_uart_to_queue(struct channel_t *ch);
-static void cls_copy_data_from_queue_to_uart(struct channel_t *ch);
-static uint cls_get_uart_bytes_left(struct channel_t *ch);
-static void cls_send_immediate_char(struct channel_t *ch, unsigned char);
-static irqreturn_t cls_intr(int irq, void *voidbrd);
-
-struct board_ops dgnc_cls_ops = {
- .tasklet = cls_tasklet,
- .intr = cls_intr,
- .uart_init = cls_uart_init,
- .uart_off = cls_uart_off,
- .drain = cls_drain,
- .param = cls_param,
- .vpd = cls_vpd,
- .assert_modem_signals = cls_assert_modem_signals,
- .flush_uart_write = cls_flush_uart_write,
- .flush_uart_read = cls_flush_uart_read,
- .disable_receiver = cls_disable_receiver,
- .enable_receiver = cls_enable_receiver,
- .send_break = cls_send_break,
- .send_start_character = cls_send_start_character,
- .send_stop_character = cls_send_stop_character,
- .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart,
- .get_uart_bytes_left = cls_get_uart_bytes_left,
- .send_immediate_char = cls_send_immediate_char
-};
-
static inline void cls_set_cts_flow_control(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
-/* Parse the ISR register for the specific port */
-static inline void cls_parse_isr(struct dgnc_board *brd, uint port)
+static void cls_copy_data_from_uart_to_queue(struct channel_t *ch)
{
- struct channel_t *ch;
- unsigned char isr = 0;
+ int qleft = 0;
+ unsigned char linestatus = 0;
+ unsigned char error_mask = 0;
+ ushort head;
+ ushort tail;
unsigned long flags;
- /*
- * No need to verify board pointer, it was already
- * verified in the interrupt routine.
- */
-
- if (port >= brd->nasync)
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
return;
- ch = brd->channels[port];
- if (ch->magic != DGNC_CHANNEL_MAGIC)
- return;
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* cache head and tail of queue */
+ head = ch->ch_r_head;
+ tail = ch->ch_r_tail;
+
+ /* Store how much space we have left in the queue */
+ qleft = tail - head - 1;
+ if (qleft < 0)
+ qleft += RQUEUEMASK + 1;
+
+ /*
+ * Create a mask to determine whether we should
+ * insert the character (if any) into our queue.
+ */
+ if (ch->ch_c_iflag & IGNBRK)
+ error_mask |= UART_LSR_BI;
- /* Here we try to figure out what caused the interrupt to happen */
while (1) {
- isr = readb(&ch->ch_cls_uart->isr_fcr);
+ linestatus = readb(&ch->ch_cls_uart->lsr);
- /* Bail if no pending interrupt on port */
- if (isr & UART_IIR_NO_INT)
+ if (!(linestatus & (UART_LSR_DR)))
break;
- /* Receive Interrupt pending */
- if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) {
- /* Read data from uart -> queue */
- brd->intr_rx++;
- ch->ch_intr_rx++;
- cls_copy_data_from_uart_to_queue(ch);
- dgnc_check_queue_flow_control(ch);
+ /*
+ * Discard character if we are ignoring the error mask.
+ */
+ if (linestatus & error_mask) {
+ linestatus = 0;
+ readb(&ch->ch_cls_uart->txrx);
+ continue;
}
- /* Transmit Hold register empty pending */
- if (isr & UART_IIR_THRI) {
- /* Transfer data (if any) from Write Queue -> UART. */
- spin_lock_irqsave(&ch->ch_lock, flags);
- ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
- brd->intr_tx++;
- ch->ch_intr_tx++;
- spin_unlock_irqrestore(&ch->ch_lock, flags);
- cls_copy_data_from_queue_to_uart(ch);
+ /*
+ * If our queue is full, we have no choice but to drop some
+ * data. The assumption is that HWFLOW or SWFLOW should have
+ * stopped things way way before we got to this point.
+ *
+ * I decided that I wanted to ditch the oldest data first,
+ * I hope thats okay with everyone? Yes? Good.
+ */
+ while (qleft < 1) {
+ tail = (tail + 1) & RQUEUEMASK;
+ ch->ch_r_tail = tail;
+ ch->ch_err_overrun++;
+ qleft++;
}
- /* CTS/RTS change of state */
- if (isr & UART_IIR_CTSRTS) {
- brd->intr_modem++;
- ch->ch_intr_modem++;
- /*
- * Don't need to do anything, the cls_parse_modem
- * below will grab the updated modem signals.
- */
- }
+ ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE
+ | UART_LSR_FE);
+ ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx);
- /* Parse any modem signal changes */
- cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
+ qleft--;
+
+ if (ch->ch_equeue[head] & UART_LSR_PE)
+ ch->ch_err_parity++;
+ if (ch->ch_equeue[head] & UART_LSR_BI)
+ ch->ch_err_break++;
+ if (ch->ch_equeue[head] & UART_LSR_FE)
+ ch->ch_err_frame++;
+
+ /* Add to, and flip head if needed */
+ head = (head + 1) & RQUEUEMASK;
+ ch->ch_rxcount++;
}
+
+ /*
+ * Write new final heads to channel structure.
+ */
+ ch->ch_r_head = head & RQUEUEMASK;
+ ch->ch_e_head = head & EQUEUEMASK;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
}
-/*
- * cls_param()
- * Send any/all changes to the line to the UART.
- */
-static void cls_param(struct tty_struct *tty)
+/* Make the UART raise any of the output signals we want up */
+static void cls_assert_modem_signals(struct channel_t *ch)
{
- unsigned char lcr = 0;
- unsigned char uart_lcr = 0;
- unsigned char ier = 0;
- unsigned char uart_ier = 0;
- uint baud = 9600;
- int quot = 0;
- struct dgnc_board *bd;
- struct channel_t *ch;
- struct un_t *un;
+ unsigned char out;
- if (!tty || tty->magic != TTY_MAGIC)
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
return;
- un = (struct un_t *)tty->driver_data;
- if (!un || un->magic != DGNC_UNIT_MAGIC)
- return;
+ out = ch->ch_mostat;
+
+ if (ch->ch_flags & CH_LOOPBACK)
+ out |= UART_MCR_LOOP;
+
+ writeb(out, &ch->ch_cls_uart->mcr);
+
+ /* Give time for the UART to actually drop the signals */
+ udelay(10);
+}
+
+static void cls_copy_data_from_queue_to_uart(struct channel_t *ch)
+{
+ ushort head;
+ ushort tail;
+ int n;
+ int qlen;
+ uint len_written = 0;
+ unsigned long flags;
- ch = un->un_ch;
if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
return;
- bd = ch->ch_bd;
- if (!bd || bd->magic != DGNC_BOARD_MAGIC)
- return;
+ spin_lock_irqsave(&ch->ch_lock, flags);
- /*
- * If baud rate is zero, flush queues, and set mval to drop DTR.
- */
- if ((ch->ch_c_cflag & (CBAUD)) == 0) {
- ch->ch_r_head = 0;
- ch->ch_r_tail = 0;
- ch->ch_e_head = 0;
- ch->ch_e_tail = 0;
- ch->ch_w_head = 0;
- ch->ch_w_tail = 0;
+ /* No data to write to the UART */
+ if (ch->ch_w_tail == ch->ch_w_head)
+ goto exit_unlock;
- cls_flush_uart_write(ch);
- cls_flush_uart_read(ch);
+ /* If port is "stopped", don't send any data to the UART */
+ if ((ch->ch_flags & CH_FORCED_STOP) ||
+ (ch->ch_flags & CH_BREAK_SENDING))
+ goto exit_unlock;
- /* The baudrate is B0 so all modem lines are to be dropped. */
- ch->ch_flags |= (CH_BAUD0);
- ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
- cls_assert_modem_signals(ch);
- ch->ch_old_baud = 0;
- return;
- } else if (ch->ch_custom_speed) {
- baud = ch->ch_custom_speed;
- /* Handle transition from B0 */
- if (ch->ch_flags & CH_BAUD0) {
- ch->ch_flags &= ~(CH_BAUD0);
+ if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+ goto exit_unlock;
- /*
- * Bring back up RTS and DTR...
- * Also handle RTS or DTR toggle if set.
- */
- if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
- ch->ch_mostat |= (UART_MCR_RTS);
- if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
- ch->ch_mostat |= (UART_MCR_DTR);
- }
+ n = 32;
- } else {
- int iindex = 0;
- int jindex = 0;
+ /* cache head and tail of queue */
+ head = ch->ch_w_head & WQUEUEMASK;
+ tail = ch->ch_w_tail & WQUEUEMASK;
+ qlen = (head - tail) & WQUEUEMASK;
- ulong bauds[4][16] = {
- { /* slowbaud */
- 0, 50, 75, 110,
- 134, 150, 200, 300,
- 600, 1200, 1800, 2400,
- 4800, 9600, 19200, 38400 },
- { /* slowbaud & CBAUDEX */
- 0, 57600, 115200, 230400,
- 460800, 150, 200, 921600,
- 600, 1200, 1800, 2400,
- 4800, 9600, 19200, 38400 },
- { /* fastbaud */
- 0, 57600, 76800, 115200,
- 131657, 153600, 230400, 460800,
- 921600, 1200, 1800, 2400,
- 4800, 9600, 19200, 38400 },
- { /* fastbaud & CBAUDEX */
- 0, 57600, 115200, 230400,
- 460800, 150, 200, 921600,
- 600, 1200, 1800, 2400,
- 4800, 9600, 19200, 38400 }
- };
+ /* Find minimum of the FIFO space, versus queue length */
+ n = min(n, qlen);
+ while (n > 0) {
/*
- * Only use the TXPrint baud rate if the terminal
+ * If RTS Toggle mode is on, turn on RTS now if not already set,
+ * and make sure we get an event when the data transfer has
+ * completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_RTS)) {
+ ch->ch_mostat |= (UART_MCR_RTS);
+ cls_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+
+ /*
+ * If DTR Toggle mode is on, turn on DTR now if not already set,
+ * and make sure we get an event when the data transfer has
+ * completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_DTR)) {
+ ch->ch_mostat |= (UART_MCR_DTR);
+ cls_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+ writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx);
+ ch->ch_w_tail++;
+ ch->ch_w_tail &= WQUEUEMASK;
+ ch->ch_txcount++;
+ len_written++;
+ n--;
+ }
+
+ if (len_written > 0)
+ ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+exit_unlock:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+static void cls_parse_modem(struct channel_t *ch, unsigned char signals)
+{
+ unsigned char msignals = signals;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * Do altpin switching. Altpin switches DCD and DSR.
+ * This prolly breaks DSRPACE, so we should be more clever here.
+ */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
+ unsigned char mswap = signals;
+
+ if (mswap & UART_MSR_DDCD) {
+ msignals &= ~UART_MSR_DDCD;
+ msignals |= UART_MSR_DDSR;
+ }
+ if (mswap & UART_MSR_DDSR) {
+ msignals &= ~UART_MSR_DDSR;
+ msignals |= UART_MSR_DDCD;
+ }
+ if (mswap & UART_MSR_DCD) {
+ msignals &= ~UART_MSR_DCD;
+ msignals |= UART_MSR_DSR;
+ }
+ if (mswap & UART_MSR_DSR) {
+ msignals &= ~UART_MSR_DSR;
+ msignals |= UART_MSR_DCD;
+ }
+ }
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /*
+ * Scrub off lower bits. They signify delta's, which I don't
+ * care about
+ */
+ signals &= 0xf0;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ if (msignals & UART_MSR_DCD)
+ ch->ch_mistat |= UART_MSR_DCD;
+ else
+ ch->ch_mistat &= ~UART_MSR_DCD;
+
+ if (msignals & UART_MSR_DSR)
+ ch->ch_mistat |= UART_MSR_DSR;
+ else
+ ch->ch_mistat &= ~UART_MSR_DSR;
+
+ if (msignals & UART_MSR_RI)
+ ch->ch_mistat |= UART_MSR_RI;
+ else
+ ch->ch_mistat &= ~UART_MSR_RI;
+
+ if (msignals & UART_MSR_CTS)
+ ch->ch_mistat |= UART_MSR_CTS;
+ else
+ ch->ch_mistat &= ~UART_MSR_CTS;
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+/* Parse the ISR register for the specific port */
+static inline void cls_parse_isr(struct dgnc_board *brd, uint port)
+{
+ struct channel_t *ch;
+ unsigned char isr = 0;
+ unsigned long flags;
+
+ /*
+ * No need to verify board pointer, it was already
+ * verified in the interrupt routine.
+ */
+
+ if (port >= brd->nasync)
+ return;
+
+ ch = brd->channels[port];
+ if (ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /* Here we try to figure out what caused the interrupt to happen */
+ while (1) {
+ isr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Bail if no pending interrupt on port */
+ if (isr & UART_IIR_NO_INT)
+ break;
+
+ /* Receive Interrupt pending */
+ if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) {
+ /* Read data from uart -> queue */
+ brd->intr_rx++;
+ ch->ch_intr_rx++;
+ cls_copy_data_from_uart_to_queue(ch);
+ dgnc_check_queue_flow_control(ch);
+ }
+
+ /* Transmit Hold register empty pending */
+ if (isr & UART_IIR_THRI) {
+ /* Transfer data (if any) from Write Queue -> UART. */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ brd->intr_tx++;
+ ch->ch_intr_tx++;
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ cls_copy_data_from_queue_to_uart(ch);
+ }
+
+ /* CTS/RTS change of state */
+ if (isr & UART_IIR_CTSRTS) {
+ brd->intr_modem++;
+ ch->ch_intr_modem++;
+ /*
+ * Don't need to do anything, the cls_parse_modem
+ * below will grab the updated modem signals.
+ */
+ }
+
+ /* Parse any modem signal changes */
+ cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
+ }
+}
+
+/* Channel lock MUST be held before calling this function! */
+static void cls_flush_uart_write(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT),
+ &ch->ch_cls_uart->isr_fcr);
+ usleep_range(10, 20);
+
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+/* Channel lock MUST be held before calling this function! */
+static void cls_flush_uart_read(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * For complete POSIX compatibility, we should be purging the
+ * read FIFO in the UART here.
+ *
+ * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also
+ * incorrectly flushes write data as well as just basically trashing the
+ * FIFO.
+ *
+ * Presumably, this is a bug in this UART.
+ */
+
+ udelay(10);
+}
+
+/*
+ * cls_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void cls_param(struct tty_struct *tty)
+{
+ unsigned char lcr = 0;
+ unsigned char uart_lcr = 0;
+ unsigned char ier = 0;
+ unsigned char uart_ier = 0;
+ uint baud = 9600;
+ int quot = 0;
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = (struct un_t *)tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ /*
+ * If baud rate is zero, flush queues, and set mval to drop DTR.
+ */
+ if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+ ch->ch_r_head = 0;
+ ch->ch_r_tail = 0;
+ ch->ch_e_head = 0;
+ ch->ch_e_tail = 0;
+ ch->ch_w_head = 0;
+ ch->ch_w_tail = 0;
+
+ cls_flush_uart_write(ch);
+ cls_flush_uart_read(ch);
+
+ /* The baudrate is B0 so all modem lines are to be dropped. */
+ ch->ch_flags |= (CH_BAUD0);
+ ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+ cls_assert_modem_signals(ch);
+ ch->ch_old_baud = 0;
+ return;
+ } else if (ch->ch_custom_speed) {
+ baud = ch->ch_custom_speed;
+ /* Handle transition from B0 */
+ if (ch->ch_flags & CH_BAUD0) {
+ ch->ch_flags &= ~(CH_BAUD0);
+
+ /*
+ * Bring back up RTS and DTR...
+ * Also handle RTS or DTR toggle if set.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_RTS);
+ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_DTR);
+ }
+
+ } else {
+ int iindex = 0;
+ int jindex = 0;
+
+ ulong bauds[4][16] = {
+ { /* slowbaud */
+ 0, 50, 75, 110,
+ 134, 150, 200, 300,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* slowbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud */
+ 0, 57600, 76800, 115200,
+ 131657, 153600, 230400, 460800,
+ 921600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 }
+ };
+
+ /*
+ * Only use the TXPrint baud rate if the terminal
* unit is NOT open
*/
if (!(ch->ch_tun.un_flags & UN_ISOPEN) &&
brd->intr_count++;
- /*
- * Check the board's global interrupt offset to see if we
- * we actually do have an interrupt pending for us.
- */
- poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
-
- /* If 0, no interrupts pending */
- if (!poll_reg) {
- spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
- return IRQ_NONE;
- }
-
- /* Parse each port to find out what caused the interrupt */
- for (i = 0; i < brd->nasync; i++)
- cls_parse_isr(brd, i);
-
- /*
- * Schedule tasklet to more in-depth servicing at a better time.
- */
- tasklet_schedule(&brd->helper_tasklet);
-
- spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
-
- return IRQ_HANDLED;
-}
-
-static void cls_disable_receiver(struct channel_t *ch)
-{
- unsigned char tmp = readb(&ch->ch_cls_uart->ier);
-
- tmp &= ~(UART_IER_RDI);
- writeb(tmp, &ch->ch_cls_uart->ier);
-}
-
-static void cls_enable_receiver(struct channel_t *ch)
-{
- unsigned char tmp = readb(&ch->ch_cls_uart->ier);
-
- tmp |= (UART_IER_RDI);
- writeb(tmp, &ch->ch_cls_uart->ier);
-}
-
-static void cls_copy_data_from_uart_to_queue(struct channel_t *ch)
-{
- int qleft = 0;
- unsigned char linestatus = 0;
- unsigned char error_mask = 0;
- ushort head;
- ushort tail;
- unsigned long flags;
-
- if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
- return;
-
- spin_lock_irqsave(&ch->ch_lock, flags);
-
- /* cache head and tail of queue */
- head = ch->ch_r_head;
- tail = ch->ch_r_tail;
-
- /* Store how much space we have left in the queue */
- qleft = tail - head - 1;
- if (qleft < 0)
- qleft += RQUEUEMASK + 1;
-
- /*
- * Create a mask to determine whether we should
- * insert the character (if any) into our queue.
- */
- if (ch->ch_c_iflag & IGNBRK)
- error_mask |= UART_LSR_BI;
-
- while (1) {
- linestatus = readb(&ch->ch_cls_uart->lsr);
-
- if (!(linestatus & (UART_LSR_DR)))
- break;
-
- /*
- * Discard character if we are ignoring the error mask.
- */
- if (linestatus & error_mask) {
- linestatus = 0;
- readb(&ch->ch_cls_uart->txrx);
- continue;
- }
-
- /*
- * If our queue is full, we have no choice but to drop some
- * data. The assumption is that HWFLOW or SWFLOW should have
- * stopped things way way before we got to this point.
- *
- * I decided that I wanted to ditch the oldest data first,
- * I hope thats okay with everyone? Yes? Good.
- */
- while (qleft < 1) {
- tail = (tail + 1) & RQUEUEMASK;
- ch->ch_r_tail = tail;
- ch->ch_err_overrun++;
- qleft++;
- }
-
- ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE
- | UART_LSR_FE);
- ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx);
-
- qleft--;
-
- if (ch->ch_equeue[head] & UART_LSR_PE)
- ch->ch_err_parity++;
- if (ch->ch_equeue[head] & UART_LSR_BI)
- ch->ch_err_break++;
- if (ch->ch_equeue[head] & UART_LSR_FE)
- ch->ch_err_frame++;
-
- /* Add to, and flip head if needed */
- head = (head + 1) & RQUEUEMASK;
- ch->ch_rxcount++;
+ /*
+ * Check the board's global interrupt offset to see if we
+ * we actually do have an interrupt pending for us.
+ */
+ poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
+
+ /* If 0, no interrupts pending */
+ if (!poll_reg) {
+ spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+ return IRQ_NONE;
}
+ /* Parse each port to find out what caused the interrupt */
+ for (i = 0; i < brd->nasync; i++)
+ cls_parse_isr(brd, i);
+
/*
- * Write new final heads to channel structure.
+ * Schedule tasklet to more in-depth servicing at a better time.
*/
- ch->ch_r_head = head & RQUEUEMASK;
- ch->ch_e_head = head & EQUEUEMASK;
+ tasklet_schedule(&brd->helper_tasklet);
- spin_unlock_irqrestore(&ch->ch_lock, flags);
+ spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void cls_disable_receiver(struct channel_t *ch)
+{
+ unsigned char tmp = readb(&ch->ch_cls_uart->ier);
+
+ tmp &= ~(UART_IER_RDI);
+ writeb(tmp, &ch->ch_cls_uart->ier);
+}
+
+static void cls_enable_receiver(struct channel_t *ch)
+{
+ unsigned char tmp = readb(&ch->ch_cls_uart->ier);
+
+ tmp |= (UART_IER_RDI);
+ writeb(tmp, &ch->ch_cls_uart->ier);
}
/*
((un->un_flags & UN_EMPTY) == 0));
}
-/* Channel lock MUST be held before calling this function! */
-static void cls_flush_uart_write(struct channel_t *ch)
-{
- if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
- return;
-
- writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT),
- &ch->ch_cls_uart->isr_fcr);
- usleep_range(10, 20);
-
- ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
-}
-
-/* Channel lock MUST be held before calling this function! */
-static void cls_flush_uart_read(struct channel_t *ch)
-{
- if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
- return;
-
- /*
- * For complete POSIX compatibility, we should be purging the
- * read FIFO in the UART here.
- *
- * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also
- * incorrectly flushes write data as well as just basically trashing the
- * FIFO.
- *
- * Presumably, this is a bug in this UART.
- */
-
- udelay(10);
-}
-
-static void cls_copy_data_from_queue_to_uart(struct channel_t *ch)
-{
- ushort head;
- ushort tail;
- int n;
- int qlen;
- uint len_written = 0;
- unsigned long flags;
-
- if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
- return;
-
- spin_lock_irqsave(&ch->ch_lock, flags);
-
- /* No data to write to the UART */
- if (ch->ch_w_tail == ch->ch_w_head)
- goto exit_unlock;
-
- /* If port is "stopped", don't send any data to the UART */
- if ((ch->ch_flags & CH_FORCED_STOP) ||
- (ch->ch_flags & CH_BREAK_SENDING))
- goto exit_unlock;
-
- if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
- goto exit_unlock;
-
- n = 32;
-
- /* cache head and tail of queue */
- head = ch->ch_w_head & WQUEUEMASK;
- tail = ch->ch_w_tail & WQUEUEMASK;
- qlen = (head - tail) & WQUEUEMASK;
-
- /* Find minimum of the FIFO space, versus queue length */
- n = min(n, qlen);
-
- while (n > 0) {
- /*
- * If RTS Toggle mode is on, turn on RTS now if not already set,
- * and make sure we get an event when the data transfer has
- * completed.
- */
- if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
- if (!(ch->ch_mostat & UART_MCR_RTS)) {
- ch->ch_mostat |= (UART_MCR_RTS);
- cls_assert_modem_signals(ch);
- }
- ch->ch_tun.un_flags |= (UN_EMPTY);
- }
-
- /*
- * If DTR Toggle mode is on, turn on DTR now if not already set,
- * and make sure we get an event when the data transfer has
- * completed.
- */
- if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
- if (!(ch->ch_mostat & UART_MCR_DTR)) {
- ch->ch_mostat |= (UART_MCR_DTR);
- cls_assert_modem_signals(ch);
- }
- ch->ch_tun.un_flags |= (UN_EMPTY);
- }
- writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx);
- ch->ch_w_tail++;
- ch->ch_w_tail &= WQUEUEMASK;
- ch->ch_txcount++;
- len_written++;
- n--;
- }
-
- if (len_written > 0)
- ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
-
-exit_unlock:
- spin_unlock_irqrestore(&ch->ch_lock, flags);
-}
-
-static void cls_parse_modem(struct channel_t *ch, unsigned char signals)
-{
- unsigned char msignals = signals;
- unsigned long flags;
-
- if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
- return;
-
- /*
- * Do altpin switching. Altpin switches DCD and DSR.
- * This prolly breaks DSRPACE, so we should be more clever here.
- */
- spin_lock_irqsave(&ch->ch_lock, flags);
- if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
- unsigned char mswap = signals;
-
- if (mswap & UART_MSR_DDCD) {
- msignals &= ~UART_MSR_DDCD;
- msignals |= UART_MSR_DDSR;
- }
- if (mswap & UART_MSR_DDSR) {
- msignals &= ~UART_MSR_DDSR;
- msignals |= UART_MSR_DDCD;
- }
- if (mswap & UART_MSR_DCD) {
- msignals &= ~UART_MSR_DCD;
- msignals |= UART_MSR_DSR;
- }
- if (mswap & UART_MSR_DSR) {
- msignals &= ~UART_MSR_DSR;
- msignals |= UART_MSR_DCD;
- }
- }
- spin_unlock_irqrestore(&ch->ch_lock, flags);
-
- /*
- * Scrub off lower bits. They signify delta's, which I don't
- * care about
- */
- signals &= 0xf0;
-
- spin_lock_irqsave(&ch->ch_lock, flags);
- if (msignals & UART_MSR_DCD)
- ch->ch_mistat |= UART_MSR_DCD;
- else
- ch->ch_mistat &= ~UART_MSR_DCD;
-
- if (msignals & UART_MSR_DSR)
- ch->ch_mistat |= UART_MSR_DSR;
- else
- ch->ch_mistat &= ~UART_MSR_DSR;
-
- if (msignals & UART_MSR_RI)
- ch->ch_mistat |= UART_MSR_RI;
- else
- ch->ch_mistat &= ~UART_MSR_RI;
-
- if (msignals & UART_MSR_CTS)
- ch->ch_mistat |= UART_MSR_CTS;
- else
- ch->ch_mistat &= ~UART_MSR_CTS;
- spin_unlock_irqrestore(&ch->ch_lock, flags);
-}
-
-/* Make the UART raise any of the output signals we want up */
-static void cls_assert_modem_signals(struct channel_t *ch)
-{
- unsigned char out;
-
- if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
- return;
-
- out = ch->ch_mostat;
-
- if (ch->ch_flags & CH_LOOPBACK)
- out |= UART_MCR_LOOP;
-
- writeb(out, &ch->ch_cls_uart->mcr);
-
- /* Give time for the UART to actually drop the signals */
- udelay(10);
-}
-
static void cls_send_start_character(struct channel_t *ch)
{
if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
iounmap(re_map_vpdbase);
}
+
+struct board_ops dgnc_cls_ops = {
+ .tasklet = cls_tasklet,
+ .intr = cls_intr,
+ .uart_init = cls_uart_init,
+ .uart_off = cls_uart_off,
+ .drain = cls_drain,
+ .param = cls_param,
+ .vpd = cls_vpd,
+ .assert_modem_signals = cls_assert_modem_signals,
+ .flush_uart_write = cls_flush_uart_write,
+ .flush_uart_read = cls_flush_uart_read,
+ .disable_receiver = cls_disable_receiver,
+ .enable_receiver = cls_enable_receiver,
+ .send_break = cls_send_break,
+ .send_start_character = cls_send_start_character,
+ .send_stop_character = cls_send_stop_character,
+ .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart,
+ .get_uart_bytes_left = cls_get_uart_bytes_left,
+ .send_immediate_char = cls_send_immediate_char
+};