2005-04-17 02:20:36 +04:00
|
|
|
/************************************************************************
|
|
|
|
* Copyright 2003 Digi International (www.digi.com)
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004 IBM Corporation. All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
|
|
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
|
|
|
|
* MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* Contact Information:
|
|
|
|
* Scott H Kilau <Scott_Kilau@digi.com>
|
2006-04-01 03:04:20 +04:00
|
|
|
* Wendy Xiong <wendyx@us.ibm.com>
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
***********************************************************************/
|
|
|
|
#include <linux/delay.h> /* For udelay */
|
|
|
|
#include <linux/serial_reg.h> /* For the various UART offsets */
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
|
|
|
|
#include "jsm.h" /* Driver main header file */
|
|
|
|
|
|
|
|
static u32 jsm_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function allows calls to ensure that all outstanding
|
|
|
|
* PCI writes have been completed, by doing a PCI read against
|
|
|
|
* a non-destructive, read-only location on the Neo card.
|
|
|
|
*
|
|
|
|
* In this case, we are reading the DVID (Read-only Device Identification)
|
|
|
|
* value of the Neo card.
|
|
|
|
*/
|
|
|
|
static inline void neo_pci_posting_flush(struct jsm_board *bd)
|
|
|
|
{
|
|
|
|
readb(bd->re_map_membase + 0x8D);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_set_cts_flow_control(struct jsm_channel *ch)
|
|
|
|
{
|
2005-07-27 22:43:49 +04:00
|
|
|
u8 ier, efr;
|
|
|
|
ier = readb(&ch->ch_neo_uart->ier);
|
|
|
|
efr = readb(&ch->ch_neo_uart->efr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
|
|
|
|
|
|
|
|
/* Turn on auto CTS flow control */
|
|
|
|
ier |= (UART_17158_IER_CTSDSR);
|
|
|
|
efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR);
|
|
|
|
|
|
|
|
/* Turn off auto Xon flow control */
|
|
|
|
efr &= ~(UART_17158_EFR_IXON);
|
|
|
|
|
|
|
|
/* Why? Becuz Exar's spec says we have to zero it out before setting it */
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on UART enhanced bits */
|
|
|
|
writeb(efr, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on table D, with 8 char hi/low watermarks */
|
|
|
|
writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
|
|
|
|
|
|
|
|
/* Feed the UART our trigger levels */
|
|
|
|
writeb(8, &ch->ch_neo_uart->tfifo);
|
|
|
|
ch->ch_t_tlevel = 8;
|
|
|
|
|
|
|
|
writeb(ier, &ch->ch_neo_uart->ier);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_set_rts_flow_control(struct jsm_channel *ch)
|
|
|
|
{
|
2005-07-27 22:43:49 +04:00
|
|
|
u8 ier, efr;
|
|
|
|
ier = readb(&ch->ch_neo_uart->ier);
|
|
|
|
efr = readb(&ch->ch_neo_uart->efr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
|
|
|
|
|
|
|
|
/* Turn on auto RTS flow control */
|
|
|
|
ier |= (UART_17158_IER_RTSDTR);
|
|
|
|
efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR);
|
|
|
|
|
|
|
|
/* Turn off auto Xoff flow control */
|
|
|
|
ier &= ~(UART_17158_IER_XOFF);
|
|
|
|
efr &= ~(UART_17158_EFR_IXOFF);
|
|
|
|
|
|
|
|
/* Why? Becuz Exar's spec says we have to zero it out before setting it */
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on UART enhanced bits */
|
|
|
|
writeb(efr, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
|
|
|
|
ch->ch_r_watermark = 4;
|
|
|
|
|
|
|
|
writeb(56, &ch->ch_neo_uart->rfifo);
|
|
|
|
ch->ch_r_tlevel = 56;
|
|
|
|
|
|
|
|
writeb(ier, &ch->ch_neo_uart->ier);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From the Neo UART spec sheet:
|
|
|
|
* The auto RTS/DTR function must be started by asserting
|
|
|
|
* RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after
|
|
|
|
* it is enabled.
|
|
|
|
*/
|
|
|
|
ch->ch_mostat |= (UART_MCR_RTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void neo_set_ixon_flow_control(struct jsm_channel *ch)
|
|
|
|
{
|
2005-07-27 22:43:49 +04:00
|
|
|
u8 ier, efr;
|
|
|
|
ier = readb(&ch->ch_neo_uart->ier);
|
|
|
|
efr = readb(&ch->ch_neo_uart->efr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
|
|
|
|
|
|
|
|
/* Turn off auto CTS flow control */
|
|
|
|
ier &= ~(UART_17158_IER_CTSDSR);
|
|
|
|
efr &= ~(UART_17158_EFR_CTSDSR);
|
|
|
|
|
|
|
|
/* Turn on auto Xon flow control */
|
|
|
|
efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON);
|
|
|
|
|
|
|
|
/* Why? Becuz Exar's spec says we have to zero it out before setting it */
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on UART enhanced bits */
|
|
|
|
writeb(efr, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
|
|
|
|
ch->ch_r_watermark = 4;
|
|
|
|
|
|
|
|
writeb(32, &ch->ch_neo_uart->rfifo);
|
|
|
|
ch->ch_r_tlevel = 32;
|
|
|
|
|
|
|
|
/* Tell UART what start/stop chars it should be looking for */
|
|
|
|
writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
|
|
|
|
writeb(0, &ch->ch_neo_uart->xonchar2);
|
|
|
|
|
|
|
|
writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
|
|
|
|
writeb(0, &ch->ch_neo_uart->xoffchar2);
|
|
|
|
|
|
|
|
writeb(ier, &ch->ch_neo_uart->ier);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_set_ixoff_flow_control(struct jsm_channel *ch)
|
|
|
|
{
|
2005-07-27 22:43:49 +04:00
|
|
|
u8 ier, efr;
|
|
|
|
ier = readb(&ch->ch_neo_uart->ier);
|
|
|
|
efr = readb(&ch->ch_neo_uart->efr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
|
|
|
|
|
|
|
|
/* Turn off auto RTS flow control */
|
|
|
|
ier &= ~(UART_17158_IER_RTSDTR);
|
|
|
|
efr &= ~(UART_17158_EFR_RTSDTR);
|
|
|
|
|
|
|
|
/* Turn on auto Xoff flow control */
|
|
|
|
ier |= (UART_17158_IER_XOFF);
|
|
|
|
efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
|
|
|
|
|
|
|
|
/* Why? Becuz Exar's spec says we have to zero it out before setting it */
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on UART enhanced bits */
|
|
|
|
writeb(efr, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on table D, with 8 char hi/low watermarks */
|
|
|
|
writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
|
|
|
|
|
|
|
|
writeb(8, &ch->ch_neo_uart->tfifo);
|
|
|
|
ch->ch_t_tlevel = 8;
|
|
|
|
|
|
|
|
/* Tell UART what start/stop chars it should be looking for */
|
|
|
|
writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
|
|
|
|
writeb(0, &ch->ch_neo_uart->xonchar2);
|
|
|
|
|
|
|
|
writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
|
|
|
|
writeb(0, &ch->ch_neo_uart->xoffchar2);
|
|
|
|
|
|
|
|
writeb(ier, &ch->ch_neo_uart->ier);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_set_no_input_flow_control(struct jsm_channel *ch)
|
|
|
|
{
|
2005-07-27 22:43:49 +04:00
|
|
|
u8 ier, efr;
|
|
|
|
ier = readb(&ch->ch_neo_uart->ier);
|
|
|
|
efr = readb(&ch->ch_neo_uart->efr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
|
|
|
|
|
|
|
|
/* Turn off auto RTS flow control */
|
|
|
|
ier &= ~(UART_17158_IER_RTSDTR);
|
|
|
|
efr &= ~(UART_17158_EFR_RTSDTR);
|
|
|
|
|
|
|
|
/* Turn off auto Xoff flow control */
|
|
|
|
ier &= ~(UART_17158_IER_XOFF);
|
|
|
|
if (ch->ch_c_iflag & IXON)
|
|
|
|
efr &= ~(UART_17158_EFR_IXOFF);
|
|
|
|
else
|
|
|
|
efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
|
|
|
|
|
|
|
|
/* Why? Becuz Exar's spec says we have to zero it out before setting it */
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on UART enhanced bits */
|
|
|
|
writeb(efr, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on table D, with 8 char hi/low watermarks */
|
|
|
|
writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
|
|
|
|
|
|
|
|
ch->ch_r_watermark = 0;
|
|
|
|
|
|
|
|
writeb(16, &ch->ch_neo_uart->tfifo);
|
|
|
|
ch->ch_t_tlevel = 16;
|
|
|
|
|
|
|
|
writeb(16, &ch->ch_neo_uart->rfifo);
|
|
|
|
ch->ch_r_tlevel = 16;
|
|
|
|
|
|
|
|
writeb(ier, &ch->ch_neo_uart->ier);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_set_no_output_flow_control(struct jsm_channel *ch)
|
|
|
|
{
|
2005-07-27 22:43:49 +04:00
|
|
|
u8 ier, efr;
|
|
|
|
ier = readb(&ch->ch_neo_uart->ier);
|
|
|
|
efr = readb(&ch->ch_neo_uart->efr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
|
|
|
|
|
|
|
|
/* Turn off auto CTS flow control */
|
|
|
|
ier &= ~(UART_17158_IER_CTSDSR);
|
|
|
|
efr &= ~(UART_17158_EFR_CTSDSR);
|
|
|
|
|
|
|
|
/* Turn off auto Xon flow control */
|
|
|
|
if (ch->ch_c_iflag & IXOFF)
|
|
|
|
efr &= ~(UART_17158_EFR_IXON);
|
|
|
|
else
|
|
|
|
efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON);
|
|
|
|
|
|
|
|
/* Why? Becuz Exar's spec says we have to zero it out before setting it */
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on UART enhanced bits */
|
|
|
|
writeb(efr, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Turn on table D, with 8 char hi/low watermarks */
|
|
|
|
writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
|
|
|
|
|
|
|
|
ch->ch_r_watermark = 0;
|
|
|
|
|
|
|
|
writeb(16, &ch->ch_neo_uart->tfifo);
|
|
|
|
ch->ch_t_tlevel = 16;
|
|
|
|
|
|
|
|
writeb(16, &ch->ch_neo_uart->rfifo);
|
|
|
|
ch->ch_r_tlevel = 16;
|
|
|
|
|
|
|
|
writeb(ier, &ch->ch_neo_uart->ier);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* if hardware flow control is set, then skip this whole thing */
|
|
|
|
if (ch->ch_c_cflag & CRTSCTS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n");
|
|
|
|
|
|
|
|
/* Tell UART what start/stop chars it should be looking for */
|
|
|
|
writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
|
|
|
|
writeb(0, &ch->ch_neo_uart->xonchar2);
|
|
|
|
|
|
|
|
writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
|
|
|
|
writeb(0, &ch->ch_neo_uart->xoffchar2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
int qleft = 0;
|
|
|
|
u8 linestatus = 0;
|
|
|
|
u8 error_mask = 0;
|
|
|
|
int n = 0;
|
|
|
|
int total = 0;
|
|
|
|
u16 head;
|
|
|
|
u16 tail;
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* cache head and tail of queue */
|
|
|
|
head = ch->ch_r_head & RQUEUEMASK;
|
|
|
|
tail = ch->ch_r_tail & RQUEUEMASK;
|
|
|
|
|
|
|
|
/* Get our cached LSR */
|
|
|
|
linestatus = ch->ch_cached_lsr;
|
|
|
|
ch->ch_cached_lsr = 0;
|
|
|
|
|
|
|
|
/* Store how much space we have left in the queue */
|
|
|
|
if ((qleft = tail - head - 1) < 0)
|
|
|
|
qleft += RQUEUEMASK + 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the UART is not in FIFO mode, force the FIFO copy to
|
|
|
|
* NOT be run, by setting total to 0.
|
|
|
|
*
|
|
|
|
* On the other hand, if the UART IS in FIFO mode, then ask
|
|
|
|
* the UART to give us an approximation of data it has RX'ed.
|
|
|
|
*/
|
|
|
|
if (!(ch->ch_flags & CH_FIFO_ENABLED))
|
|
|
|
total = 0;
|
|
|
|
else {
|
|
|
|
total = readb(&ch->ch_neo_uart->rfifo);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* EXAR chip bug - RX FIFO COUNT - Fudge factor.
|
|
|
|
*
|
|
|
|
* This resolves a problem/bug with the Exar chip that sometimes
|
|
|
|
* returns a bogus value in the rfifo register.
|
|
|
|
* The count can be any where from 0-3 bytes "off".
|
|
|
|
* Bizarre, but true.
|
|
|
|
*/
|
|
|
|
total -= 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, bound the copy to make sure we don't overflow
|
|
|
|
* our own queue...
|
|
|
|
* The byte by byte copy loop below this loop this will
|
|
|
|
* deal with the queue overflow possibility.
|
|
|
|
*/
|
|
|
|
total = min(total, qleft);
|
|
|
|
|
|
|
|
while (total > 0) {
|
|
|
|
/*
|
|
|
|
* Grab the linestatus register, we need to check
|
|
|
|
* to see if there are any errors in the FIFO.
|
|
|
|
*/
|
|
|
|
linestatus = readb(&ch->ch_neo_uart->lsr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Break out if there is a FIFO error somewhere.
|
|
|
|
* This will allow us to go byte by byte down below,
|
|
|
|
* finding the exact location of the error.
|
|
|
|
*/
|
|
|
|
if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Make sure we don't go over the end of our queue */
|
|
|
|
n = min(((u32) total), (RQUEUESIZE - (u32) head));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cut down n even further if needed, this is to fix
|
|
|
|
* a problem with memcpy_fromio() with the Neo on the
|
|
|
|
* IBM pSeries platform.
|
|
|
|
* 15 bytes max appears to be the magic number.
|
|
|
|
*/
|
|
|
|
n = min((u32) n, (u32) 12);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we are grabbing the linestatus register, which
|
|
|
|
* will reset some bits after our read, we need to ensure
|
|
|
|
* we don't miss our TX FIFO emptys.
|
|
|
|
*/
|
|
|
|
if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR))
|
|
|
|
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
|
|
|
|
linestatus = 0;
|
|
|
|
|
|
|
|
/* Copy data from uart to the queue */
|
|
|
|
memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n);
|
|
|
|
/*
|
|
|
|
* Since RX_FIFO_DATA_ERROR was 0, we are guarenteed
|
|
|
|
* that all the data currently in the FIFO is free of
|
|
|
|
* breaks and parity/frame/orun errors.
|
|
|
|
*/
|
|
|
|
memset(ch->ch_equeue + head, 0, n);
|
|
|
|
|
|
|
|
/* Add to and flip head if needed */
|
|
|
|
head = (head + n) & RQUEUEMASK;
|
|
|
|
total -= n;
|
|
|
|
qleft -= n;
|
|
|
|
ch->ch_rxcount += n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now cleanup any leftover bytes still in the UART.
|
|
|
|
* Also deal with any possible queue overflow here as well.
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Its possible we have a linestatus from the loop above
|
|
|
|
* this, so we "OR" on any extra bits.
|
|
|
|
*/
|
|
|
|
linestatus |= readb(&ch->ch_neo_uart->lsr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the chip tells us there is no more data pending to
|
|
|
|
* be read, we can then leave.
|
|
|
|
* But before we do, cache the linestatus, just in case.
|
|
|
|
*/
|
|
|
|
if (!(linestatus & UART_LSR_DR)) {
|
|
|
|
ch->ch_cached_lsr = linestatus;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No need to store this bit */
|
|
|
|
linestatus &= ~UART_LSR_DR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we are grabbing the linestatus register, which
|
|
|
|
* will reset some bits after our read, we need to ensure
|
|
|
|
* we don't miss our TX FIFO emptys.
|
|
|
|
*/
|
|
|
|
if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
|
|
|
|
linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR);
|
|
|
|
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Discard character if we are ignoring the error mask.
|
|
|
|
*/
|
|
|
|
if (linestatus & error_mask) {
|
|
|
|
u8 discard;
|
|
|
|
linestatus = 0;
|
|
|
|
memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1);
|
|
|
|
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) {
|
|
|
|
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Queue full, dropping DATA:%x LSR:%x\n",
|
|
|
|
ch->ch_rqueue[tail], ch->ch_equeue[tail]);
|
|
|
|
|
|
|
|
ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK;
|
|
|
|
ch->ch_err_overrun++;
|
|
|
|
qleft++;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
|
|
|
|
ch->ch_equeue[head] = (u8) linestatus;
|
|
|
|
|
|
|
|
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]);
|
|
|
|
|
|
|
|
/* Ditch any remaining linestatus value. */
|
|
|
|
linestatus = 0;
|
|
|
|
|
|
|
|
/* Add to and flip head if needed */
|
|
|
|
head = (head + 1) & RQUEUEMASK;
|
|
|
|
|
|
|
|
qleft--;
|
|
|
|
ch->ch_rxcount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write new final heads to channel structure.
|
|
|
|
*/
|
|
|
|
ch->ch_r_head = head & RQUEUEMASK;
|
|
|
|
ch->ch_e_head = head & EQUEUEMASK;
|
|
|
|
jsm_input(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u16 head;
|
|
|
|
u16 tail;
|
|
|
|
int n;
|
|
|
|
int s;
|
|
|
|
int qlen;
|
|
|
|
u32 len_written = 0;
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* No data to write to the UART */
|
|
|
|
if (ch->ch_w_tail == ch->ch_w_head)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* If port is "stopped", don't send any data to the UART */
|
|
|
|
if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING))
|
|
|
|
return;
|
|
|
|
/*
|
|
|
|
* If FIFOs are disabled. Send data directly to txrx register
|
|
|
|
*/
|
|
|
|
if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
|
|
|
|
u8 lsrbits = readb(&ch->ch_neo_uart->lsr);
|
|
|
|
|
|
|
|
ch->ch_cached_lsr |= lsrbits;
|
|
|
|
if (ch->ch_cached_lsr & UART_LSR_THRE) {
|
|
|
|
ch->ch_cached_lsr &= ~(UART_LSR_THRE);
|
|
|
|
|
|
|
|
writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx);
|
|
|
|
jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]);
|
|
|
|
ch->ch_w_tail++;
|
|
|
|
ch->ch_w_tail &= WQUEUEMASK;
|
|
|
|
ch->ch_txcount++;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to do it this way, because of the EXAR TXFIFO count bug.
|
|
|
|
*/
|
|
|
|
if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
len_written = 0;
|
|
|
|
n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
|
|
|
|
|
|
|
|
/* 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) {
|
|
|
|
|
|
|
|
s = ((head >= tail) ? head : WQUEUESIZE) - tail;
|
|
|
|
s = min(s, n);
|
|
|
|
|
|
|
|
if (s <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s);
|
|
|
|
/* Add and flip queue if needed */
|
|
|
|
tail = (tail + s) & WQUEUEMASK;
|
|
|
|
n -= s;
|
|
|
|
ch->ch_txcount += s;
|
|
|
|
len_written += s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the final tail */
|
|
|
|
ch->ch_w_tail = tail & WQUEUEMASK;
|
|
|
|
|
|
|
|
if (len_written >= ch->ch_t_tlevel)
|
|
|
|
ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
|
|
|
|
if (!jsm_tty_write(&ch->uart_port))
|
|
|
|
uart_write_wakeup(&ch->uart_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
|
|
|
|
{
|
|
|
|
u8 msignals = signals;
|
|
|
|
|
|
|
|
jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals);
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Scrub off lower bits. They signify delta's, which I don't care about */
|
2007-05-08 11:26:33 +04:00
|
|
|
/* Keep DDCD and DDSR though */
|
|
|
|
msignals &= 0xf8;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-05-08 11:26:33 +04:00
|
|
|
if (msignals & UART_MSR_DDCD)
|
|
|
|
uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_DCD);
|
|
|
|
if (msignals & UART_MSR_DDSR)
|
|
|
|
uart_handle_cts_change(&ch->uart_port, msignals & UART_MSR_CTS);
|
2005-04-17 02:20:36 +04:00
|
|
|
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;
|
|
|
|
|
|
|
|
jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
|
|
|
|
ch->ch_portnum,
|
|
|
|
!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
|
|
|
|
!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
|
|
|
|
!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS),
|
|
|
|
!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR),
|
|
|
|
!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI),
|
|
|
|
!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make the UART raise any of the output signals we want up */
|
|
|
|
static void neo_assert_modem_signals(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u8 out;
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
out = ch->ch_mostat;
|
|
|
|
|
|
|
|
writeb(out, &ch->ch_neo_uart->mcr);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Flush the WRITE FIFO on the Neo.
|
|
|
|
*
|
|
|
|
* NOTE: Channel lock MUST be held before calling this function!
|
|
|
|
*/
|
|
|
|
static void neo_flush_uart_write(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u8 tmp = 0;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
|
|
|
|
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
|
|
|
|
/* Check to see if the UART feels it completely flushed the FIFO. */
|
|
|
|
tmp = readb(&ch->ch_neo_uart->isr_fcr);
|
|
|
|
if (tmp & 4) {
|
|
|
|
jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Still flushing TX UART... i: %d\n", i);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Flush the READ FIFO on the Neo.
|
|
|
|
*
|
|
|
|
* NOTE: Channel lock MUST be held before calling this function!
|
|
|
|
*/
|
|
|
|
static void neo_flush_uart_read(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u8 tmp = 0;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr);
|
|
|
|
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
|
|
|
|
/* Check to see if the UART feels it completely flushed the FIFO. */
|
|
|
|
tmp = readb(&ch->ch_neo_uart->isr_fcr);
|
|
|
|
if (tmp & 2) {
|
|
|
|
jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Still flushing RX UART... i: %d\n", i);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No locks are assumed to be held when calling this function.
|
|
|
|
*/
|
2005-05-01 19:59:29 +04:00
|
|
|
static void neo_clear_break(struct jsm_channel *ch, int force)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
unsigned long lock_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
|
|
|
|
/* Turn break off, and unset some variables */
|
|
|
|
if (ch->ch_flags & CH_BREAK_SENDING) {
|
|
|
|
u8 temp = readb(&ch->ch_neo_uart->lcr);
|
|
|
|
writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
|
|
|
|
|
|
|
|
ch->ch_flags &= ~(CH_BREAK_SENDING);
|
|
|
|
jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the ISR register.
|
|
|
|
*/
|
|
|
|
static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
|
|
|
|
{
|
|
|
|
struct jsm_channel *ch;
|
|
|
|
u8 isr;
|
|
|
|
u8 cause;
|
|
|
|
unsigned long lock_flags;
|
|
|
|
|
|
|
|
if (!brd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (port > brd->maxports)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ch = brd->channels[port];
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Here we try to figure out what caused the interrupt to happen */
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
isr = readb(&ch->ch_neo_uart->isr_fcr);
|
|
|
|
|
|
|
|
/* Bail if no pending interrupt */
|
|
|
|
if (isr & UART_IIR_NO_INT)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Yank off the upper 2 bits, which just show that the FIFO's are enabled.
|
|
|
|
*/
|
|
|
|
isr &= ~(UART_17158_IIR_FIFO_ENABLED);
|
|
|
|
|
|
|
|
jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"%s:%d isr: %x\n", __FILE__, __LINE__, isr);
|
|
|
|
|
|
|
|
if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
|
|
|
|
/* Read data from uart -> queue */
|
|
|
|
neo_copy_data_from_uart_to_queue(ch);
|
|
|
|
|
|
|
|
/* Call our tty layer to enforce queue flow control if needed. */
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
jsm_check_queue_flow_control(ch);
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isr & UART_IIR_THRI) {
|
|
|
|
/* Transfer data (if any) from Write Queue -> UART. */
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
neo_copy_data_from_queue_to_uart(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isr & UART_17158_IIR_XONXOFF) {
|
|
|
|
cause = readb(&ch->ch_neo_uart->xoffchar1);
|
|
|
|
|
|
|
|
jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since the UART detected either an XON or
|
|
|
|
* XOFF match, we need to figure out which
|
|
|
|
* one it was, so we can suspend or resume data flow.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
if (cause == UART_17158_XON_DETECT) {
|
|
|
|
/* Is output stopped right now, if so, resume it */
|
|
|
|
if (brd->channels[port]->ch_flags & CH_STOP) {
|
|
|
|
ch->ch_flags &= ~(CH_STOP);
|
|
|
|
}
|
|
|
|
jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Port %d. XON detected in incoming data\n", port);
|
|
|
|
}
|
|
|
|
else if (cause == UART_17158_XOFF_DETECT) {
|
|
|
|
if (!(brd->channels[port]->ch_flags & CH_STOP)) {
|
|
|
|
ch->ch_flags |= CH_STOP;
|
|
|
|
jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Setting CH_STOP\n");
|
|
|
|
}
|
|
|
|
jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"Port: %d. XOFF detected in incoming data\n", port);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) {
|
|
|
|
/*
|
|
|
|
* If we get here, this means the hardware is doing auto flow control.
|
|
|
|
* Check to see whether RTS/DTR or CTS/DSR caused this interrupt.
|
|
|
|
*/
|
|
|
|
cause = readb(&ch->ch_neo_uart->mcr);
|
|
|
|
|
|
|
|
/* Which pin is doing auto flow? RTS or DTR? */
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
if ((cause & 0x4) == 0) {
|
|
|
|
if (cause & UART_MCR_RTS)
|
|
|
|
ch->ch_mostat |= UART_MCR_RTS;
|
|
|
|
else
|
|
|
|
ch->ch_mostat &= ~(UART_MCR_RTS);
|
|
|
|
} else {
|
|
|
|
if (cause & UART_MCR_DTR)
|
|
|
|
ch->ch_mostat |= UART_MCR_DTR;
|
|
|
|
else
|
|
|
|
ch->ch_mostat &= ~(UART_MCR_DTR);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse any modem signal changes */
|
|
|
|
jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"MOD_STAT: sending to parse_modem_sigs\n");
|
|
|
|
neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
|
|
|
|
{
|
|
|
|
struct jsm_channel *ch;
|
|
|
|
int linestatus;
|
|
|
|
unsigned long lock_flags;
|
|
|
|
|
|
|
|
if (!brd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (port > brd->maxports)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ch = brd->channels[port];
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
linestatus = readb(&ch->ch_neo_uart->lsr);
|
|
|
|
|
|
|
|
jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
|
|
|
|
"%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus);
|
|
|
|
|
|
|
|
ch->ch_cached_lsr |= linestatus;
|
|
|
|
|
|
|
|
if (ch->ch_cached_lsr & UART_LSR_DR) {
|
|
|
|
/* Read data from uart -> queue */
|
|
|
|
neo_copy_data_from_uart_to_queue(ch);
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
jsm_check_queue_flow_control(ch);
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a special flag. It indicates that at least 1
|
|
|
|
* RX error (parity, framing, or break) has happened.
|
|
|
|
* Mark this in our struct, which will tell me that I have
|
|
|
|
*to do the special RX+LSR read for this FIFO load.
|
|
|
|
*/
|
|
|
|
if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
|
|
|
|
jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
|
|
|
|
"%s:%d Port: %d Got an RX error, need to parse LSR\n",
|
|
|
|
__FILE__, __LINE__, port);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The next 3 tests should *NOT* happen, as the above test
|
|
|
|
* should encapsulate all 3... At least, thats what Exar says.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (linestatus & UART_LSR_PE) {
|
|
|
|
ch->ch_err_parity++;
|
|
|
|
jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
|
|
|
|
"%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (linestatus & UART_LSR_FE) {
|
|
|
|
ch->ch_err_frame++;
|
|
|
|
jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
|
|
|
|
"%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (linestatus & UART_LSR_BI) {
|
|
|
|
ch->ch_err_break++;
|
|
|
|
jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
|
|
|
|
"%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (linestatus & UART_LSR_OE) {
|
|
|
|
/*
|
|
|
|
* Rx Oruns. Exar says that an orun will NOT corrupt
|
|
|
|
* the FIFO. It will just replace the holding register
|
|
|
|
* with this new data byte. So basically just ignore this.
|
|
|
|
* Probably we should eventually have an orun stat in our driver...
|
|
|
|
*/
|
|
|
|
ch->ch_err_overrun++;
|
|
|
|
jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
|
|
|
|
"%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (linestatus & UART_LSR_THRE) {
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
|
|
|
|
/* Transfer data (if any) from Write Queue -> UART. */
|
|
|
|
neo_copy_data_from_queue_to_uart(ch);
|
|
|
|
}
|
|
|
|
else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
|
|
|
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
|
|
|
|
|
|
|
/* Transfer data (if any) from Write Queue -> UART. */
|
|
|
|
neo_copy_data_from_queue_to_uart(ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* neo_param()
|
|
|
|
* Send any/all changes to the line to the UART.
|
|
|
|
*/
|
|
|
|
static void neo_param(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u8 lcr = 0;
|
|
|
|
u8 uart_lcr = 0;
|
|
|
|
u8 ier = 0;
|
|
|
|
u32 baud = 9600;
|
|
|
|
int quot = 0;
|
|
|
|
struct jsm_board *bd;
|
|
|
|
|
|
|
|
bd = ch->ch_bd;
|
|
|
|
if (!bd)
|
|
|
|
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 = ch->ch_r_tail = 0;
|
|
|
|
ch->ch_e_head = ch->ch_e_tail = 0;
|
|
|
|
ch->ch_w_head = ch->ch_w_tail = 0;
|
|
|
|
|
|
|
|
neo_flush_uart_write(ch);
|
|
|
|
neo_flush_uart_read(ch);
|
|
|
|
|
|
|
|
ch->ch_flags |= (CH_BAUD0);
|
|
|
|
ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
|
|
|
|
neo_assert_modem_signals(ch);
|
|
|
|
ch->ch_old_baud = 0;
|
|
|
|
return;
|
|
|
|
|
|
|
|
} else if (ch->ch_custom_speed) {
|
|
|
|
baud = ch->ch_custom_speed;
|
|
|
|
if (ch->ch_flags & CH_BAUD0)
|
|
|
|
ch->ch_flags &= ~(CH_BAUD0);
|
2006-02-01 14:05:20 +03:00
|
|
|
} else {
|
|
|
|
int i;
|
|
|
|
unsigned int cflag;
|
|
|
|
static struct {
|
|
|
|
unsigned int rate;
|
|
|
|
unsigned int cflag;
|
|
|
|
} baud_rates[] = {
|
|
|
|
{ 921600, B921600 },
|
|
|
|
{ 460800, B460800 },
|
|
|
|
{ 230400, B230400 },
|
|
|
|
{ 115200, B115200 },
|
|
|
|
{ 57600, B57600 },
|
|
|
|
{ 38400, B38400 },
|
|
|
|
{ 19200, B19200 },
|
|
|
|
{ 9600, B9600 },
|
|
|
|
{ 4800, B4800 },
|
|
|
|
{ 2400, B2400 },
|
|
|
|
{ 1200, B1200 },
|
|
|
|
{ 600, B600 },
|
|
|
|
{ 300, B300 },
|
|
|
|
{ 200, B200 },
|
|
|
|
{ 150, B150 },
|
|
|
|
{ 134, B134 },
|
|
|
|
{ 110, B110 },
|
|
|
|
{ 75, B75 },
|
|
|
|
{ 50, B50 },
|
|
|
|
};
|
|
|
|
|
|
|
|
cflag = C_BAUD(ch->uart_port.info->tty);
|
|
|
|
baud = 9600;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(baud_rates); i++) {
|
|
|
|
if (baud_rates[i].cflag == cflag) {
|
|
|
|
baud = baud_rates[i].rate;
|
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-01 14:05:20 +03:00
|
|
|
if (ch->ch_flags & CH_BAUD0)
|
|
|
|
ch->ch_flags &= ~(CH_BAUD0);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
if (ch->ch_c_cflag & PARENB)
|
|
|
|
lcr |= UART_LCR_PARITY;
|
|
|
|
|
|
|
|
if (!(ch->ch_c_cflag & PARODD))
|
|
|
|
lcr |= UART_LCR_EPAR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Not all platforms support mark/space parity,
|
|
|
|
* so this will hide behind an ifdef.
|
|
|
|
*/
|
|
|
|
#ifdef CMSPAR
|
|
|
|
if (ch->ch_c_cflag & CMSPAR)
|
|
|
|
lcr |= UART_LCR_SPAR;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ch->ch_c_cflag & CSTOPB)
|
|
|
|
lcr |= UART_LCR_STOP;
|
|
|
|
|
|
|
|
switch (ch->ch_c_cflag & CSIZE) {
|
|
|
|
case CS5:
|
|
|
|
lcr |= UART_LCR_WLEN5;
|
|
|
|
break;
|
|
|
|
case CS6:
|
|
|
|
lcr |= UART_LCR_WLEN6;
|
|
|
|
break;
|
|
|
|
case CS7:
|
|
|
|
lcr |= UART_LCR_WLEN7;
|
|
|
|
break;
|
|
|
|
case CS8:
|
|
|
|
default:
|
|
|
|
lcr |= UART_LCR_WLEN8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ier = readb(&ch->ch_neo_uart->ier);
|
|
|
|
uart_lcr = readb(&ch->ch_neo_uart->lcr);
|
|
|
|
|
|
|
|
if (baud == 0)
|
|
|
|
baud = 9600;
|
|
|
|
|
|
|
|
quot = ch->ch_bd->bd_dividend / baud;
|
|
|
|
|
|
|
|
if (quot != 0) {
|
|
|
|
ch->ch_old_baud = baud;
|
|
|
|
writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
|
|
|
|
writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
|
|
|
|
writeb((quot >> 8), &ch->ch_neo_uart->ier);
|
|
|
|
writeb(lcr, &ch->ch_neo_uart->lcr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uart_lcr != lcr)
|
|
|
|
writeb(lcr, &ch->ch_neo_uart->lcr);
|
|
|
|
|
|
|
|
if (ch->ch_c_cflag & CREAD)
|
|
|
|
ier |= (UART_IER_RDI | UART_IER_RLSI);
|
|
|
|
|
|
|
|
ier |= (UART_IER_THRI | UART_IER_MSI);
|
|
|
|
|
|
|
|
writeb(ier, &ch->ch_neo_uart->ier);
|
|
|
|
|
|
|
|
/* Set new start/stop chars */
|
|
|
|
neo_set_new_start_stop_chars(ch);
|
|
|
|
|
|
|
|
if (ch->ch_c_cflag & CRTSCTS)
|
|
|
|
neo_set_cts_flow_control(ch);
|
|
|
|
else if (ch->ch_c_iflag & IXON) {
|
|
|
|
/* If start/stop is set to disable, then we should disable flow control */
|
|
|
|
if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
|
|
|
|
neo_set_no_output_flow_control(ch);
|
|
|
|
else
|
|
|
|
neo_set_ixon_flow_control(ch);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
neo_set_no_output_flow_control(ch);
|
|
|
|
|
|
|
|
if (ch->ch_c_cflag & CRTSCTS)
|
|
|
|
neo_set_rts_flow_control(ch);
|
|
|
|
else if (ch->ch_c_iflag & IXOFF) {
|
|
|
|
/* If start/stop is set to disable, then we should disable flow control */
|
|
|
|
if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
|
|
|
|
neo_set_no_input_flow_control(ch);
|
|
|
|
else
|
|
|
|
neo_set_ixoff_flow_control(ch);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
neo_set_no_input_flow_control(ch);
|
|
|
|
/*
|
|
|
|
* Adjust the RX FIFO Trigger level if baud is less than 9600.
|
|
|
|
* Not exactly elegant, but this is needed because of the Exar chip's
|
|
|
|
* delay on firing off the RX FIFO interrupt on slower baud rates.
|
|
|
|
*/
|
|
|
|
if (baud < 9600) {
|
|
|
|
writeb(1, &ch->ch_neo_uart->rfifo);
|
|
|
|
ch->ch_r_tlevel = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
neo_assert_modem_signals(ch);
|
|
|
|
|
|
|
|
/* Get current status of the modem signals now */
|
|
|
|
neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* jsm_neo_intr()
|
|
|
|
*
|
|
|
|
* Neo specific interrupt handler.
|
|
|
|
*/
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 17:55:46 +04:00
|
|
|
static irqreturn_t neo_intr(int irq, void *voidbrd)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-10-06 23:00:58 +04:00
|
|
|
struct jsm_board *brd = voidbrd;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct jsm_channel *ch;
|
|
|
|
int port = 0;
|
|
|
|
int type = 0;
|
|
|
|
int current_port;
|
|
|
|
u32 tmp;
|
|
|
|
u32 uart_poll;
|
|
|
|
unsigned long lock_flags;
|
|
|
|
unsigned long lock_flags2;
|
|
|
|
int outofloop_count = 0;
|
|
|
|
|
|
|
|
brd->intr_count++;
|
|
|
|
|
|
|
|
/* Lock out the slow poller from running on this board. */
|
|
|
|
spin_lock_irqsave(&brd->bd_intr_lock, lock_flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read in "extended" IRQ information from the 32bit Neo register.
|
|
|
|
* Bits 0-7: What port triggered the interrupt.
|
|
|
|
* Bits 8-31: Each 3bits indicate what type of interrupt occurred.
|
|
|
|
*/
|
|
|
|
uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
|
|
|
|
|
|
|
|
jsm_printk(INTR, INFO, &brd->pci_dev,
|
|
|
|
"%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll);
|
|
|
|
|
|
|
|
if (!uart_poll) {
|
|
|
|
jsm_printk(INTR, INFO, &brd->pci_dev,
|
|
|
|
"Kernel interrupted to me, but no pending interrupts...\n");
|
|
|
|
spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
|
|
|
|
return IRQ_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* At this point, we have at least SOMETHING to service, dig further... */
|
|
|
|
|
|
|
|
current_port = 0;
|
|
|
|
|
|
|
|
/* Loop on each port */
|
|
|
|
while (((uart_poll & 0xff) != 0) && (outofloop_count < 0xff)){
|
|
|
|
|
|
|
|
tmp = uart_poll;
|
|
|
|
outofloop_count++;
|
|
|
|
|
|
|
|
/* Check current port to see if it has interrupt pending */
|
|
|
|
if ((tmp & jsm_offset_table[current_port]) != 0) {
|
|
|
|
port = current_port;
|
|
|
|
type = tmp >> (8 + (port * 3));
|
|
|
|
type &= 0x7;
|
|
|
|
} else {
|
|
|
|
current_port++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsm_printk(INTR, INFO, &brd->pci_dev,
|
|
|
|
"%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type);
|
|
|
|
|
|
|
|
/* Remove this port + type from uart_poll */
|
|
|
|
uart_poll &= ~(jsm_offset_table[port]);
|
|
|
|
|
|
|
|
if (!type) {
|
|
|
|
/* If no type, just ignore it, and move onto next port */
|
|
|
|
jsm_printk(INTR, ERR, &brd->pci_dev,
|
|
|
|
"Interrupt with no type! port: %d\n", port);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Switch on type of interrupt we have */
|
|
|
|
switch (type) {
|
|
|
|
|
|
|
|
case UART_17158_RXRDY_TIMEOUT:
|
|
|
|
/*
|
|
|
|
* RXRDY Time-out is cleared by reading data in the
|
|
|
|
* RX FIFO until it falls below the trigger level.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Verify the port is in range. */
|
|
|
|
if (port > brd->nasync)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ch = brd->channels[port];
|
|
|
|
neo_copy_data_from_uart_to_queue(ch);
|
|
|
|
|
|
|
|
/* Call our tty layer to enforce queue flow control if needed. */
|
|
|
|
spin_lock_irqsave(&ch->ch_lock, lock_flags2);
|
|
|
|
jsm_check_queue_flow_control(ch);
|
|
|
|
spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case UART_17158_RX_LINE_STATUS:
|
|
|
|
/*
|
|
|
|
* RXRDY and RX LINE Status (logic OR of LSR[4:1])
|
|
|
|
*/
|
|
|
|
neo_parse_lsr(brd, port);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case UART_17158_TXRDY:
|
|
|
|
/*
|
|
|
|
* TXRDY interrupt clears after reading ISR register for the UART channel.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Yes, this is odd...
|
|
|
|
* Why would I check EVERY possibility of type of
|
|
|
|
* interrupt, when we know its TXRDY???
|
|
|
|
* Becuz for some reason, even tho we got triggered for TXRDY,
|
|
|
|
* it seems to be occassionally wrong. Instead of TX, which
|
|
|
|
* it should be, I was getting things like RXDY too. Weird.
|
|
|
|
*/
|
|
|
|
neo_parse_isr(brd, port);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case UART_17158_MSR:
|
|
|
|
/*
|
|
|
|
* MSR or flow control was seen.
|
|
|
|
*/
|
|
|
|
neo_parse_isr(brd, port);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* The UART triggered us with a bogus interrupt type.
|
|
|
|
* It appears the Exar chip, when REALLY bogged down, will throw
|
|
|
|
* these once and awhile.
|
|
|
|
* Its harmless, just ignore it and move on.
|
|
|
|
*/
|
|
|
|
jsm_printk(INTR, ERR, &brd->pci_dev,
|
|
|
|
"%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
|
|
|
|
|
|
|
|
jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n");
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Neo specific way of turning off the receiver.
|
|
|
|
* Used as a way to enforce queue flow control when in
|
|
|
|
* hardware flow control mode.
|
|
|
|
*/
|
|
|
|
static void neo_disable_receiver(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u8 tmp = readb(&ch->ch_neo_uart->ier);
|
|
|
|
tmp &= ~(UART_IER_RDI);
|
|
|
|
writeb(tmp, &ch->ch_neo_uart->ier);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Neo specific way of turning on the receiver.
|
|
|
|
* Used as a way to un-enforce queue flow control when in
|
|
|
|
* hardware flow control mode.
|
|
|
|
*/
|
|
|
|
static void neo_enable_receiver(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u8 tmp = readb(&ch->ch_neo_uart->ier);
|
|
|
|
tmp |= (UART_IER_RDI);
|
|
|
|
writeb(tmp, &ch->ch_neo_uart->ier);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_send_start_character(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ch->ch_startc != __DISABLED_CHAR) {
|
|
|
|
ch->ch_xon_sends++;
|
|
|
|
writeb(ch->ch_startc, &ch->ch_neo_uart->txrx);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void neo_send_stop_character(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ch->ch_stopc != __DISABLED_CHAR) {
|
|
|
|
ch->ch_xoff_sends++;
|
|
|
|
writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* neo_uart_init
|
|
|
|
*/
|
|
|
|
static void neo_uart_init(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
writeb(0, &ch->ch_neo_uart->ier);
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Clear out UART and FIFO */
|
|
|
|
readb(&ch->ch_neo_uart->txrx);
|
|
|
|
writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
|
|
|
|
readb(&ch->ch_neo_uart->lsr);
|
|
|
|
readb(&ch->ch_neo_uart->msr);
|
|
|
|
|
|
|
|
ch->ch_flags |= CH_FIFO_ENABLED;
|
|
|
|
|
|
|
|
/* Assert any signals we want up */
|
|
|
|
writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make the UART completely turn off.
|
|
|
|
*/
|
|
|
|
static void neo_uart_off(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
/* Turn off UART enhanced bits */
|
|
|
|
writeb(0, &ch->ch_neo_uart->efr);
|
|
|
|
|
|
|
|
/* Stop all interrupts from occurring. */
|
|
|
|
writeb(0, &ch->ch_neo_uart->ier);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 neo_get_uart_bytes_left(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
u8 left = 0;
|
|
|
|
u8 lsr = readb(&ch->ch_neo_uart->lsr);
|
|
|
|
|
|
|
|
/* We must cache the LSR as some of the bits get reset once read... */
|
|
|
|
ch->ch_cached_lsr |= lsr;
|
|
|
|
|
|
|
|
/* Determine whether the Transmitter is empty or not */
|
|
|
|
if (!(lsr & UART_LSR_TEMT))
|
|
|
|
left = 1;
|
|
|
|
else {
|
|
|
|
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
|
|
|
|
left = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return left;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Channel lock MUST be held by the calling function! */
|
|
|
|
static void neo_send_break(struct jsm_channel *ch)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Set the time we should stop sending the break.
|
|
|
|
* If we are already sending a break, toss away the existing
|
|
|
|
* time to stop, and use this new value instead.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Tell the UART to start sending the break */
|
|
|
|
if (!(ch->ch_flags & CH_BREAK_SENDING)) {
|
|
|
|
u8 temp = readb(&ch->ch_neo_uart->lcr);
|
|
|
|
writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr);
|
|
|
|
ch->ch_flags |= (CH_BREAK_SENDING);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* neo_send_immediate_char.
|
|
|
|
*
|
|
|
|
* Sends a specific character as soon as possible to the UART,
|
|
|
|
* jumping over any bytes that might be in the write queue.
|
|
|
|
*
|
|
|
|
* The channel lock MUST be held by the calling function.
|
|
|
|
*/
|
|
|
|
static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c)
|
|
|
|
{
|
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
writeb(c, &ch->ch_neo_uart->txrx);
|
|
|
|
|
|
|
|
/* flush write operation */
|
|
|
|
neo_pci_posting_flush(ch->ch_bd);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct board_ops jsm_neo_ops = {
|
|
|
|
.intr = neo_intr,
|
|
|
|
.uart_init = neo_uart_init,
|
|
|
|
.uart_off = neo_uart_off,
|
|
|
|
.param = neo_param,
|
|
|
|
.assert_modem_signals = neo_assert_modem_signals,
|
|
|
|
.flush_uart_write = neo_flush_uart_write,
|
|
|
|
.flush_uart_read = neo_flush_uart_read,
|
|
|
|
.disable_receiver = neo_disable_receiver,
|
|
|
|
.enable_receiver = neo_enable_receiver,
|
|
|
|
.send_break = neo_send_break,
|
|
|
|
.clear_break = neo_clear_break,
|
|
|
|
.send_start_character = neo_send_start_character,
|
|
|
|
.send_stop_character = neo_send_stop_character,
|
|
|
|
.copy_data_from_queue_to_uart = neo_copy_data_from_queue_to_uart,
|
|
|
|
.get_uart_bytes_left = neo_get_uart_bytes_left,
|
|
|
|
.send_immediate_char = neo_send_immediate_char
|
|
|
|
};
|