// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2003 Digi International (www.digi.com) * Scott H Kilau <Scott_Kilau at digi dot com> * * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! * * This is shared code between Digi's CVS archive and the * Linux Kernel sources. * Changing the source just for reformatting needlessly breaks * our CVS diff history. * * Send any bug fixes/changes to: Eng.Linux at digi dot com. * Thank you. *
*/
#include <linux/delay.h> /* For udelay */ #include <linux/io.h> /* For read[bwl]/write[bwl] */ #include <linux/serial.h> /* For struct async_serial */ #include <linux/serial_reg.h> /* For the various UART offsets */ #include <linux/pci.h> #include <linux/tty.h>
/* * The Enhanced Register Set may only be accessed when * the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on CTS flow control, turn off IXON flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_CTSDSR);
isr_fcr &= ~(UART_EXAR654_EFR_IXON);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* * Enable interrupts for CTS flow, turn off interrupts for * received XOFF chars
*/
ier |= (UART_EXAR654_IER_CTSDSR);
ier &= ~(UART_EXAR654_IER_XOFF);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
/* * The Enhanced Register Set may only be accessed when * the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on IXON flow control, turn off CTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXON);
isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Now set our current start/stop chars while in enhanced mode */
writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
writeb(0, &ch->ch_cls_uart->lsr);
writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
writeb(0, &ch->ch_cls_uart->spr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* * Disable interrupts for CTS flow, turn on interrupts for * received XOFF chars
*/
ier &= ~(UART_EXAR654_IER_CTSDSR);
ier |= (UART_EXAR654_IER_XOFF);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
/* * The Enhanced Register Set may only be accessed when * the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn off IXON flow control, turn off CTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB);
isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR | UART_EXAR654_EFR_IXON);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* * Disable interrupts for CTS flow, turn off interrupts for * received XOFF chars
*/
ier &= ~(UART_EXAR654_IER_CTSDSR);
ier &= ~(UART_EXAR654_IER_XOFF);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
/* * The Enhanced Register Set may only be accessed when * the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on RTS flow control, turn off IXOFF flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_RTSDTR);
isr_fcr &= ~(UART_EXAR654_EFR_IXOFF);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* * The Enhanced Register Set may only be accessed when * the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on IXOFF flow control, turn off RTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXOFF);
isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Now set our current start/stop chars while in enhanced mode */
writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
writeb(0, &ch->ch_cls_uart->lsr);
writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
writeb(0, &ch->ch_cls_uart->spr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* * The Enhanced Register Set may only be accessed when * the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn off IXOFF flow control, turn off RTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB);
isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR | UART_EXAR654_EFR_IXOFF);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* * cls_clear_break. * Determines whether its time to shut off break condition. * * No locks are assumed to be held when calling this function. * channel lock is held and released in this function.
*/ staticvoid cls_clear_break(struct jsm_channel *ch)
{ unsignedlong 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_cls_uart->lcr);
/* cache head and tail of queue */
head = ch->ch_r_head & RQUEUEMASK;
tail = ch->ch_r_tail & RQUEUEMASK;
ch->ch_cached_lsr = 0;
/* 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) { /* * Grab the linestatus register, we need to * check to see if there is any data to read
*/
linestatus = readb(&ch->ch_cls_uart->lsr);
/* Break out if there is no data to fetch */ if (!(linestatus & UART_LSR_DR)) break;
/* * Discard character if we are ignoring the error mask * which in this case is the break signal.
*/ if (linestatus & error_mask) {
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++;
}
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;
for (i = 0; i < 10; i++) { /* Check to see if the UART feels it completely flushed FIFO */
tmp = readb(&ch->ch_cls_uart->isr_fcr); if (tmp & UART_FCR_CLEAR_XMIT) {
jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "Still flushing TX UART... i: %d\n", i);
udelay(10);
} else break;
}
/* Channel lock MUST be held before calling this function! */ staticvoid cls_flush_uart_read(struct jsm_channel *ch)
{ if (!ch) 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);
}
staticvoid cls_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_cls_uart->txrx);
}
}
staticvoid cls_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_cls_uart->txrx);
}
}
/* * cls_param() * Send any/all changes to the line to the UART.
*/ staticvoid cls_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; int i; unsignedint cflag;
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) == B0) {
ch->ch_r_head = 0;
ch->ch_r_tail = 0;
ch->ch_e_head = 0;
ch->ch_e_tail = 0;
/* 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); return;
}
cflag = C_BAUD(ch->uart_port.state->port.tty);
baud = 9600; for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { if (baud_rates[i].cflag == cflag) {
baud = baud_rates[i].rate; break;
}
}
if (ch->ch_flags & CH_BAUD0)
ch->ch_flags &= ~(CH_BAUD0);
if (ch->ch_c_cflag & PARENB)
lcr |= UART_LCR_PARITY;
if (!(ch->ch_c_cflag & PARODD))
lcr |= UART_LCR_EPAR;
if (ch->ch_c_cflag & CMSPAR)
lcr |= UART_LCR_SPAR;
if (ch->ch_c_cflag & CSTOPB)
lcr |= UART_LCR_STOP;
if (uart_lcr != lcr)
writeb(lcr, &ch->ch_cls_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_cls_uart->ier);
if (ch->ch_c_cflag & CRTSCTS)
cls_set_cts_flow_control(ch); elseif (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))
cls_set_no_output_flow_control(ch); else
cls_set_ixon_flow_control(ch);
} else
cls_set_no_output_flow_control(ch);
if (ch->ch_c_cflag & CRTSCTS)
cls_set_rts_flow_control(ch); elseif (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))
cls_set_no_input_flow_control(ch); else
cls_set_ixoff_flow_control(ch);
} else
cls_set_no_input_flow_control(ch);
cls_assert_modem_signals(ch);
/* get current status of the modem signals now */
cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
}
/* Lock out the slow poller from running on this board. */
spin_lock_irqsave(&brd->bd_intr_lock, lock_flags);
/* * Check the board's global interrupt offset to see if we * acctually do have an interrupt pending on us.
*/
uart_poll = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
if (!uart_poll) {
jsm_dbg(INTR, &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. */
/* Parse each port to find out what caused the interrupt */ for (i = 0; i < brd->nasync; i++)
cls_parse_isr(brd, i);
/* * The Enhanced Register Set may only be accessed when * the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on Enhanced/Extended controls */
isr_fcr |= (UART_EXAR654_EFR_ECB);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* Clear out UART and FIFO */
readb(&ch->ch_cls_uart->txrx);
/* * Turns off UART.
*/ staticvoid cls_uart_off(struct jsm_channel *ch)
{ /* Stop all interrupts from accurring. */
writeb(0, &ch->ch_cls_uart->ier);
}
/* * cls_send_break. * Starts sending a break thru the UART. * * The channel lock MUST be held by the calling function.
*/ staticvoid cls_send_break(struct jsm_channel *ch)
{ /* Tell the UART to start sending the break */ if (!(ch->ch_flags & CH_BREAK_SENDING)) {
u8 temp = readb(&ch->ch_cls_uart->lcr);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.