162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2003 Digi International (www.digi.com) 462306a36Sopenharmony_ci * Scott H Kilau <Scott_Kilau at digi dot com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This is shared code between Digi's CVS archive and the 962306a36Sopenharmony_ci * Linux Kernel sources. 1062306a36Sopenharmony_ci * Changing the source just for reformatting needlessly breaks 1162306a36Sopenharmony_ci * our CVS diff history. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Send any bug fixes/changes to: Eng.Linux at digi dot com. 1462306a36Sopenharmony_ci * Thank you. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/delay.h> /* For udelay */ 1962306a36Sopenharmony_ci#include <linux/io.h> /* For read[bwl]/write[bwl] */ 2062306a36Sopenharmony_ci#include <linux/serial.h> /* For struct async_serial */ 2162306a36Sopenharmony_ci#include <linux/serial_reg.h> /* For the various UART offsets */ 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/tty.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "jsm.h" /* Driver main header file */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct { 2862306a36Sopenharmony_ci unsigned int rate; 2962306a36Sopenharmony_ci unsigned int cflag; 3062306a36Sopenharmony_ci} baud_rates[] = { 3162306a36Sopenharmony_ci { 921600, B921600 }, 3262306a36Sopenharmony_ci { 460800, B460800 }, 3362306a36Sopenharmony_ci { 230400, B230400 }, 3462306a36Sopenharmony_ci { 115200, B115200 }, 3562306a36Sopenharmony_ci { 57600, B57600 }, 3662306a36Sopenharmony_ci { 38400, B38400 }, 3762306a36Sopenharmony_ci { 19200, B19200 }, 3862306a36Sopenharmony_ci { 9600, B9600 }, 3962306a36Sopenharmony_ci { 4800, B4800 }, 4062306a36Sopenharmony_ci { 2400, B2400 }, 4162306a36Sopenharmony_ci { 1200, B1200 }, 4262306a36Sopenharmony_ci { 600, B600 }, 4362306a36Sopenharmony_ci { 300, B300 }, 4462306a36Sopenharmony_ci { 200, B200 }, 4562306a36Sopenharmony_ci { 150, B150 }, 4662306a36Sopenharmony_ci { 134, B134 }, 4762306a36Sopenharmony_ci { 110, B110 }, 4862306a36Sopenharmony_ci { 75, B75 }, 4962306a36Sopenharmony_ci { 50, B50 }, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void cls_set_cts_flow_control(struct jsm_channel *ch) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci u8 lcrb = readb(&ch->ch_cls_uart->lcr); 5562306a36Sopenharmony_ci u8 ier = readb(&ch->ch_cls_uart->ier); 5662306a36Sopenharmony_ci u8 isr_fcr = 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* 5962306a36Sopenharmony_ci * The Enhanced Register Set may only be accessed when 6062306a36Sopenharmony_ci * the Line Control Register is set to 0xBFh. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Turn on CTS flow control, turn off IXON flow control */ 6762306a36Sopenharmony_ci isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_CTSDSR); 6862306a36Sopenharmony_ci isr_fcr &= ~(UART_EXAR654_EFR_IXON); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Write old LCR value back out, which turns enhanced access off */ 7362306a36Sopenharmony_ci writeb(lcrb, &ch->ch_cls_uart->lcr); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * Enable interrupts for CTS flow, turn off interrupts for 7762306a36Sopenharmony_ci * received XOFF chars 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci ier |= (UART_EXAR654_IER_CTSDSR); 8062306a36Sopenharmony_ci ier &= ~(UART_EXAR654_IER_XOFF); 8162306a36Sopenharmony_ci writeb(ier, &ch->ch_cls_uart->ier); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Set the usual FIFO values */ 8462306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 | 8762306a36Sopenharmony_ci UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), 8862306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ch->ch_t_tlevel = 16; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void cls_set_ixon_flow_control(struct jsm_channel *ch) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u8 lcrb = readb(&ch->ch_cls_uart->lcr); 9662306a36Sopenharmony_ci u8 ier = readb(&ch->ch_cls_uart->ier); 9762306a36Sopenharmony_ci u8 isr_fcr = 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * The Enhanced Register Set may only be accessed when 10162306a36Sopenharmony_ci * the Line Control Register is set to 0xBFh. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Turn on IXON flow control, turn off CTS flow control */ 10862306a36Sopenharmony_ci isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXON); 10962306a36Sopenharmony_ci isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Now set our current start/stop chars while in enhanced mode */ 11462306a36Sopenharmony_ci writeb(ch->ch_startc, &ch->ch_cls_uart->mcr); 11562306a36Sopenharmony_ci writeb(0, &ch->ch_cls_uart->lsr); 11662306a36Sopenharmony_ci writeb(ch->ch_stopc, &ch->ch_cls_uart->msr); 11762306a36Sopenharmony_ci writeb(0, &ch->ch_cls_uart->spr); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Write old LCR value back out, which turns enhanced access off */ 12062306a36Sopenharmony_ci writeb(lcrb, &ch->ch_cls_uart->lcr); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * Disable interrupts for CTS flow, turn on interrupts for 12462306a36Sopenharmony_ci * received XOFF chars 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci ier &= ~(UART_EXAR654_IER_CTSDSR); 12762306a36Sopenharmony_ci ier |= (UART_EXAR654_IER_XOFF); 12862306a36Sopenharmony_ci writeb(ier, &ch->ch_cls_uart->ier); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Set the usual FIFO values */ 13162306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | 13462306a36Sopenharmony_ci UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), 13562306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void cls_set_no_output_flow_control(struct jsm_channel *ch) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci u8 lcrb = readb(&ch->ch_cls_uart->lcr); 14162306a36Sopenharmony_ci u8 ier = readb(&ch->ch_cls_uart->ier); 14262306a36Sopenharmony_ci u8 isr_fcr = 0; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * The Enhanced Register Set may only be accessed when 14662306a36Sopenharmony_ci * the Line Control Register is set to 0xBFh. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Turn off IXON flow control, turn off CTS flow control */ 15362306a36Sopenharmony_ci isr_fcr |= (UART_EXAR654_EFR_ECB); 15462306a36Sopenharmony_ci isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR | UART_EXAR654_EFR_IXON); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Write old LCR value back out, which turns enhanced access off */ 15962306a36Sopenharmony_ci writeb(lcrb, &ch->ch_cls_uart->lcr); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * Disable interrupts for CTS flow, turn off interrupts for 16362306a36Sopenharmony_ci * received XOFF chars 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci ier &= ~(UART_EXAR654_IER_CTSDSR); 16662306a36Sopenharmony_ci ier &= ~(UART_EXAR654_IER_XOFF); 16762306a36Sopenharmony_ci writeb(ier, &ch->ch_cls_uart->ier); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Set the usual FIFO values */ 17062306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | 17362306a36Sopenharmony_ci UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), 17462306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ch->ch_r_watermark = 0; 17762306a36Sopenharmony_ci ch->ch_t_tlevel = 16; 17862306a36Sopenharmony_ci ch->ch_r_tlevel = 16; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void cls_set_rts_flow_control(struct jsm_channel *ch) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci u8 lcrb = readb(&ch->ch_cls_uart->lcr); 18462306a36Sopenharmony_ci u8 ier = readb(&ch->ch_cls_uart->ier); 18562306a36Sopenharmony_ci u8 isr_fcr = 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* 18862306a36Sopenharmony_ci * The Enhanced Register Set may only be accessed when 18962306a36Sopenharmony_ci * the Line Control Register is set to 0xBFh. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Turn on RTS flow control, turn off IXOFF flow control */ 19662306a36Sopenharmony_ci isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_RTSDTR); 19762306a36Sopenharmony_ci isr_fcr &= ~(UART_EXAR654_EFR_IXOFF); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Write old LCR value back out, which turns enhanced access off */ 20262306a36Sopenharmony_ci writeb(lcrb, &ch->ch_cls_uart->lcr); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Enable interrupts for RTS flow */ 20562306a36Sopenharmony_ci ier |= (UART_EXAR654_IER_RTSDTR); 20662306a36Sopenharmony_ci writeb(ier, &ch->ch_cls_uart->ier); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* Set the usual FIFO values */ 20962306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 | 21262306a36Sopenharmony_ci UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), 21362306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ch->ch_r_watermark = 4; 21662306a36Sopenharmony_ci ch->ch_r_tlevel = 8; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void cls_set_ixoff_flow_control(struct jsm_channel *ch) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci u8 lcrb = readb(&ch->ch_cls_uart->lcr); 22262306a36Sopenharmony_ci u8 ier = readb(&ch->ch_cls_uart->ier); 22362306a36Sopenharmony_ci u8 isr_fcr = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * The Enhanced Register Set may only be accessed when 22762306a36Sopenharmony_ci * the Line Control Register is set to 0xBFh. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Turn on IXOFF flow control, turn off RTS flow control */ 23462306a36Sopenharmony_ci isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXOFF); 23562306a36Sopenharmony_ci isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Now set our current start/stop chars while in enhanced mode */ 24062306a36Sopenharmony_ci writeb(ch->ch_startc, &ch->ch_cls_uart->mcr); 24162306a36Sopenharmony_ci writeb(0, &ch->ch_cls_uart->lsr); 24262306a36Sopenharmony_ci writeb(ch->ch_stopc, &ch->ch_cls_uart->msr); 24362306a36Sopenharmony_ci writeb(0, &ch->ch_cls_uart->spr); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Write old LCR value back out, which turns enhanced access off */ 24662306a36Sopenharmony_ci writeb(lcrb, &ch->ch_cls_uart->lcr); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Disable interrupts for RTS flow */ 24962306a36Sopenharmony_ci ier &= ~(UART_EXAR654_IER_RTSDTR); 25062306a36Sopenharmony_ci writeb(ier, &ch->ch_cls_uart->ier); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Set the usual FIFO values */ 25362306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | 25662306a36Sopenharmony_ci UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), 25762306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void cls_set_no_input_flow_control(struct jsm_channel *ch) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci u8 lcrb = readb(&ch->ch_cls_uart->lcr); 26362306a36Sopenharmony_ci u8 ier = readb(&ch->ch_cls_uart->ier); 26462306a36Sopenharmony_ci u8 isr_fcr = 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * The Enhanced Register Set may only be accessed when 26862306a36Sopenharmony_ci * the Line Control Register is set to 0xBFh. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Turn off IXOFF flow control, turn off RTS flow control */ 27562306a36Sopenharmony_ci isr_fcr |= (UART_EXAR654_EFR_ECB); 27662306a36Sopenharmony_ci isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR | UART_EXAR654_EFR_IXOFF); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Write old LCR value back out, which turns enhanced access off */ 28162306a36Sopenharmony_ci writeb(lcrb, &ch->ch_cls_uart->lcr); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Disable interrupts for RTS flow */ 28462306a36Sopenharmony_ci ier &= ~(UART_EXAR654_IER_RTSDTR); 28562306a36Sopenharmony_ci writeb(ier, &ch->ch_cls_uart->ier); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Set the usual FIFO values */ 28862306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | 29162306a36Sopenharmony_ci UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), 29262306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ch->ch_t_tlevel = 16; 29562306a36Sopenharmony_ci ch->ch_r_tlevel = 16; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * cls_clear_break. 30062306a36Sopenharmony_ci * Determines whether its time to shut off break condition. 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * No locks are assumed to be held when calling this function. 30362306a36Sopenharmony_ci * channel lock is held and released in this function. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic void cls_clear_break(struct jsm_channel *ch) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci unsigned long lock_flags; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci spin_lock_irqsave(&ch->ch_lock, lock_flags); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Turn break off, and unset some variables */ 31262306a36Sopenharmony_ci if (ch->ch_flags & CH_BREAK_SENDING) { 31362306a36Sopenharmony_ci u8 temp = readb(&ch->ch_cls_uart->lcr); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ch->ch_flags &= ~(CH_BREAK_SENDING); 31862306a36Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, 31962306a36Sopenharmony_ci "clear break Finishing UART_LCR_SBC! finished: %lx\n", 32062306a36Sopenharmony_ci jiffies); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void cls_disable_receiver(struct jsm_channel *ch) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci u8 tmp = readb(&ch->ch_cls_uart->ier); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci tmp &= ~(UART_IER_RDI); 33062306a36Sopenharmony_ci writeb(tmp, &ch->ch_cls_uart->ier); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void cls_enable_receiver(struct jsm_channel *ch) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci u8 tmp = readb(&ch->ch_cls_uart->ier); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci tmp |= (UART_IER_RDI); 33862306a36Sopenharmony_ci writeb(tmp, &ch->ch_cls_uart->ier); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* Make the UART raise any of the output signals we want up */ 34262306a36Sopenharmony_cistatic void cls_assert_modem_signals(struct jsm_channel *ch) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci if (!ch) 34562306a36Sopenharmony_ci return; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci writeb(ch->ch_mostat, &ch->ch_cls_uart->mcr); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void cls_copy_data_from_uart_to_queue(struct jsm_channel *ch) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci int qleft = 0; 35362306a36Sopenharmony_ci u8 linestatus; 35462306a36Sopenharmony_ci u8 error_mask = 0; 35562306a36Sopenharmony_ci u16 head; 35662306a36Sopenharmony_ci u16 tail; 35762306a36Sopenharmony_ci unsigned long flags; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!ch) 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci spin_lock_irqsave(&ch->ch_lock, flags); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* cache head and tail of queue */ 36562306a36Sopenharmony_ci head = ch->ch_r_head & RQUEUEMASK; 36662306a36Sopenharmony_ci tail = ch->ch_r_tail & RQUEUEMASK; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ch->ch_cached_lsr = 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Store how much space we have left in the queue */ 37162306a36Sopenharmony_ci qleft = tail - head - 1; 37262306a36Sopenharmony_ci if (qleft < 0) 37362306a36Sopenharmony_ci qleft += RQUEUEMASK + 1; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Create a mask to determine whether we should 37762306a36Sopenharmony_ci * insert the character (if any) into our queue. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci if (ch->ch_c_iflag & IGNBRK) 38062306a36Sopenharmony_ci error_mask |= UART_LSR_BI; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci while (1) { 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * Grab the linestatus register, we need to 38562306a36Sopenharmony_ci * check to see if there is any data to read 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci linestatus = readb(&ch->ch_cls_uart->lsr); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Break out if there is no data to fetch */ 39062306a36Sopenharmony_ci if (!(linestatus & UART_LSR_DR)) 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * Discard character if we are ignoring the error mask 39562306a36Sopenharmony_ci * which in this case is the break signal. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci if (linestatus & error_mask) { 39862306a36Sopenharmony_ci linestatus = 0; 39962306a36Sopenharmony_ci readb(&ch->ch_cls_uart->txrx); 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* 40462306a36Sopenharmony_ci * If our queue is full, we have no choice but to drop some 40562306a36Sopenharmony_ci * data. The assumption is that HWFLOW or SWFLOW should have 40662306a36Sopenharmony_ci * stopped things way way before we got to this point. 40762306a36Sopenharmony_ci * 40862306a36Sopenharmony_ci * I decided that I wanted to ditch the oldest data first, 40962306a36Sopenharmony_ci * I hope thats okay with everyone? Yes? Good. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci while (qleft < 1) { 41262306a36Sopenharmony_ci tail = (tail + 1) & RQUEUEMASK; 41362306a36Sopenharmony_ci ch->ch_r_tail = tail; 41462306a36Sopenharmony_ci ch->ch_err_overrun++; 41562306a36Sopenharmony_ci qleft++; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE 41962306a36Sopenharmony_ci | UART_LSR_FE); 42062306a36Sopenharmony_ci ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci qleft--; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (ch->ch_equeue[head] & UART_LSR_PE) 42562306a36Sopenharmony_ci ch->ch_err_parity++; 42662306a36Sopenharmony_ci if (ch->ch_equeue[head] & UART_LSR_BI) 42762306a36Sopenharmony_ci ch->ch_err_break++; 42862306a36Sopenharmony_ci if (ch->ch_equeue[head] & UART_LSR_FE) 42962306a36Sopenharmony_ci ch->ch_err_frame++; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Add to, and flip head if needed */ 43262306a36Sopenharmony_ci head = (head + 1) & RQUEUEMASK; 43362306a36Sopenharmony_ci ch->ch_rxcount++; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * Write new final heads to channel structure. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci ch->ch_r_head = head & RQUEUEMASK; 44062306a36Sopenharmony_ci ch->ch_e_head = head & EQUEUEMASK; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, flags); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci u16 tail; 44862306a36Sopenharmony_ci int n; 44962306a36Sopenharmony_ci int qlen; 45062306a36Sopenharmony_ci u32 len_written = 0; 45162306a36Sopenharmony_ci struct circ_buf *circ; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!ch) 45462306a36Sopenharmony_ci return; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci circ = &ch->uart_port.state->xmit; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* No data to write to the UART */ 45962306a36Sopenharmony_ci if (uart_circ_empty(circ)) 46062306a36Sopenharmony_ci return; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* If port is "stopped", don't send any data to the UART */ 46362306a36Sopenharmony_ci if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) 46462306a36Sopenharmony_ci return; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* We have to do it this way, because of the EXAR TXFIFO count bug. */ 46762306a36Sopenharmony_ci if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM))) 46862306a36Sopenharmony_ci return; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci n = 32; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* cache tail of queue */ 47362306a36Sopenharmony_ci tail = circ->tail & (UART_XMIT_SIZE - 1); 47462306a36Sopenharmony_ci qlen = uart_circ_chars_pending(circ); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Find minimum of the FIFO space, versus queue length */ 47762306a36Sopenharmony_ci n = min(n, qlen); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci while (n > 0) { 48062306a36Sopenharmony_ci writeb(circ->buf[tail], &ch->ch_cls_uart->txrx); 48162306a36Sopenharmony_ci tail = (tail + 1) & (UART_XMIT_SIZE - 1); 48262306a36Sopenharmony_ci n--; 48362306a36Sopenharmony_ci ch->ch_txcount++; 48462306a36Sopenharmony_ci len_written++; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Update the final tail */ 48862306a36Sopenharmony_ci circ->tail = tail & (UART_XMIT_SIZE - 1); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (len_written > ch->ch_t_tlevel) 49162306a36Sopenharmony_ci ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (uart_circ_empty(circ)) 49462306a36Sopenharmony_ci uart_write_wakeup(&ch->uart_port); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void cls_parse_modem(struct jsm_channel *ch, u8 signals) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci u8 msignals = signals; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, 50262306a36Sopenharmony_ci "neo_parse_modem: port: %d msignals: %x\n", 50362306a36Sopenharmony_ci ch->ch_portnum, msignals); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * Scrub off lower bits. 50762306a36Sopenharmony_ci * They signify delta's, which I don't care about 50862306a36Sopenharmony_ci * Keep DDCD and DDSR though 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci msignals &= 0xf8; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (msignals & UART_MSR_DDCD) 51362306a36Sopenharmony_ci uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_DCD); 51462306a36Sopenharmony_ci if (msignals & UART_MSR_DDSR) 51562306a36Sopenharmony_ci uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_CTS); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (msignals & UART_MSR_DCD) 51862306a36Sopenharmony_ci ch->ch_mistat |= UART_MSR_DCD; 51962306a36Sopenharmony_ci else 52062306a36Sopenharmony_ci ch->ch_mistat &= ~UART_MSR_DCD; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (msignals & UART_MSR_DSR) 52362306a36Sopenharmony_ci ch->ch_mistat |= UART_MSR_DSR; 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci ch->ch_mistat &= ~UART_MSR_DSR; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (msignals & UART_MSR_RI) 52862306a36Sopenharmony_ci ch->ch_mistat |= UART_MSR_RI; 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci ch->ch_mistat &= ~UART_MSR_RI; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (msignals & UART_MSR_CTS) 53362306a36Sopenharmony_ci ch->ch_mistat |= UART_MSR_CTS; 53462306a36Sopenharmony_ci else 53562306a36Sopenharmony_ci ch->ch_mistat &= ~UART_MSR_CTS; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, 53862306a36Sopenharmony_ci "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n", 53962306a36Sopenharmony_ci ch->ch_portnum, 54062306a36Sopenharmony_ci !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR), 54162306a36Sopenharmony_ci !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS), 54262306a36Sopenharmony_ci !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS), 54362306a36Sopenharmony_ci !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR), 54462306a36Sopenharmony_ci !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI), 54562306a36Sopenharmony_ci !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD)); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/* Parse the ISR register for the specific port */ 54962306a36Sopenharmony_cistatic inline void cls_parse_isr(struct jsm_board *brd, uint port) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct jsm_channel *ch; 55262306a36Sopenharmony_ci u8 isr = 0; 55362306a36Sopenharmony_ci unsigned long flags; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * No need to verify board pointer, it was already 55762306a36Sopenharmony_ci * verified in the interrupt routine. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (port >= brd->nasync) 56162306a36Sopenharmony_ci return; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ch = brd->channels[port]; 56462306a36Sopenharmony_ci if (!ch) 56562306a36Sopenharmony_ci return; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Here we try to figure out what caused the interrupt to happen */ 56862306a36Sopenharmony_ci while (1) { 56962306a36Sopenharmony_ci isr = readb(&ch->ch_cls_uart->isr_fcr); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Bail if no pending interrupt on port */ 57262306a36Sopenharmony_ci if (isr & UART_IIR_NO_INT) 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Receive Interrupt pending */ 57662306a36Sopenharmony_ci if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) { 57762306a36Sopenharmony_ci /* Read data from uart -> queue */ 57862306a36Sopenharmony_ci cls_copy_data_from_uart_to_queue(ch); 57962306a36Sopenharmony_ci jsm_check_queue_flow_control(ch); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Transmit Hold register empty pending */ 58362306a36Sopenharmony_ci if (isr & UART_IIR_THRI) { 58462306a36Sopenharmony_ci /* Transfer data (if any) from Write Queue -> UART. */ 58562306a36Sopenharmony_ci spin_lock_irqsave(&ch->ch_lock, flags); 58662306a36Sopenharmony_ci ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); 58762306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, flags); 58862306a36Sopenharmony_ci cls_copy_data_from_queue_to_uart(ch); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * CTS/RTS change of state: 59362306a36Sopenharmony_ci * Don't need to do anything, the cls_parse_modem 59462306a36Sopenharmony_ci * below will grab the updated modem signals. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Parse any modem signal changes */ 59862306a36Sopenharmony_ci cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/* Channel lock MUST be held before calling this function! */ 60362306a36Sopenharmony_cistatic void cls_flush_uart_write(struct jsm_channel *ch) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci u8 tmp = 0; 60662306a36Sopenharmony_ci u8 i = 0; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (!ch) 60962306a36Sopenharmony_ci return; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), 61262306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 61562306a36Sopenharmony_ci /* Check to see if the UART feels it completely flushed FIFO */ 61662306a36Sopenharmony_ci tmp = readb(&ch->ch_cls_uart->isr_fcr); 61762306a36Sopenharmony_ci if (tmp & UART_FCR_CLEAR_XMIT) { 61862306a36Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, 61962306a36Sopenharmony_ci "Still flushing TX UART... i: %d\n", i); 62062306a36Sopenharmony_ci udelay(10); 62162306a36Sopenharmony_ci } else 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci/* Channel lock MUST be held before calling this function! */ 62962306a36Sopenharmony_cistatic void cls_flush_uart_read(struct jsm_channel *ch) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci if (!ch) 63262306a36Sopenharmony_ci return; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * For complete POSIX compatibility, we should be purging the 63662306a36Sopenharmony_ci * read FIFO in the UART here. 63762306a36Sopenharmony_ci * 63862306a36Sopenharmony_ci * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also 63962306a36Sopenharmony_ci * incorrectly flushes write data as well as just basically trashing the 64062306a36Sopenharmony_ci * FIFO. 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * Presumably, this is a bug in this UART. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci udelay(10); 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void cls_send_start_character(struct jsm_channel *ch) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci if (!ch) 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (ch->ch_startc != __DISABLED_CHAR) { 65462306a36Sopenharmony_ci ch->ch_xon_sends++; 65562306a36Sopenharmony_ci writeb(ch->ch_startc, &ch->ch_cls_uart->txrx); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void cls_send_stop_character(struct jsm_channel *ch) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci if (!ch) 66262306a36Sopenharmony_ci return; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (ch->ch_stopc != __DISABLED_CHAR) { 66562306a36Sopenharmony_ci ch->ch_xoff_sends++; 66662306a36Sopenharmony_ci writeb(ch->ch_stopc, &ch->ch_cls_uart->txrx); 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* 67162306a36Sopenharmony_ci * cls_param() 67262306a36Sopenharmony_ci * Send any/all changes to the line to the UART. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_cistatic void cls_param(struct jsm_channel *ch) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci u8 lcr = 0; 67762306a36Sopenharmony_ci u8 uart_lcr = 0; 67862306a36Sopenharmony_ci u8 ier = 0; 67962306a36Sopenharmony_ci u32 baud = 9600; 68062306a36Sopenharmony_ci int quot = 0; 68162306a36Sopenharmony_ci struct jsm_board *bd; 68262306a36Sopenharmony_ci int i; 68362306a36Sopenharmony_ci unsigned int cflag; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci bd = ch->ch_bd; 68662306a36Sopenharmony_ci if (!bd) 68762306a36Sopenharmony_ci return; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* 69062306a36Sopenharmony_ci * If baud rate is zero, flush queues, and set mval to drop DTR. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci if ((ch->ch_c_cflag & CBAUD) == B0) { 69362306a36Sopenharmony_ci ch->ch_r_head = 0; 69462306a36Sopenharmony_ci ch->ch_r_tail = 0; 69562306a36Sopenharmony_ci ch->ch_e_head = 0; 69662306a36Sopenharmony_ci ch->ch_e_tail = 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci cls_flush_uart_write(ch); 69962306a36Sopenharmony_ci cls_flush_uart_read(ch); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* The baudrate is B0 so all modem lines are to be dropped. */ 70262306a36Sopenharmony_ci ch->ch_flags |= (CH_BAUD0); 70362306a36Sopenharmony_ci ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR); 70462306a36Sopenharmony_ci cls_assert_modem_signals(ch); 70562306a36Sopenharmony_ci return; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci cflag = C_BAUD(ch->uart_port.state->port.tty); 70962306a36Sopenharmony_ci baud = 9600; 71062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { 71162306a36Sopenharmony_ci if (baud_rates[i].cflag == cflag) { 71262306a36Sopenharmony_ci baud = baud_rates[i].rate; 71362306a36Sopenharmony_ci break; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (ch->ch_flags & CH_BAUD0) 71862306a36Sopenharmony_ci ch->ch_flags &= ~(CH_BAUD0); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (ch->ch_c_cflag & PARENB) 72162306a36Sopenharmony_ci lcr |= UART_LCR_PARITY; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (!(ch->ch_c_cflag & PARODD)) 72462306a36Sopenharmony_ci lcr |= UART_LCR_EPAR; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (ch->ch_c_cflag & CMSPAR) 72762306a36Sopenharmony_ci lcr |= UART_LCR_SPAR; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (ch->ch_c_cflag & CSTOPB) 73062306a36Sopenharmony_ci lcr |= UART_LCR_STOP; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci lcr |= UART_LCR_WLEN(tty_get_char_size(ch->ch_c_cflag)); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci ier = readb(&ch->ch_cls_uart->ier); 73562306a36Sopenharmony_ci uart_lcr = readb(&ch->ch_cls_uart->lcr); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci quot = ch->ch_bd->bd_dividend / baud; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (quot != 0) { 74062306a36Sopenharmony_ci writeb(UART_LCR_DLAB, &ch->ch_cls_uart->lcr); 74162306a36Sopenharmony_ci writeb((quot & 0xff), &ch->ch_cls_uart->txrx); 74262306a36Sopenharmony_ci writeb((quot >> 8), &ch->ch_cls_uart->ier); 74362306a36Sopenharmony_ci writeb(lcr, &ch->ch_cls_uart->lcr); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (uart_lcr != lcr) 74762306a36Sopenharmony_ci writeb(lcr, &ch->ch_cls_uart->lcr); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (ch->ch_c_cflag & CREAD) 75062306a36Sopenharmony_ci ier |= (UART_IER_RDI | UART_IER_RLSI); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ier |= (UART_IER_THRI | UART_IER_MSI); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci writeb(ier, &ch->ch_cls_uart->ier); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (ch->ch_c_cflag & CRTSCTS) 75762306a36Sopenharmony_ci cls_set_cts_flow_control(ch); 75862306a36Sopenharmony_ci else if (ch->ch_c_iflag & IXON) { 75962306a36Sopenharmony_ci /* 76062306a36Sopenharmony_ci * If start/stop is set to disable, 76162306a36Sopenharmony_ci * then we should disable flow control. 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_ci if ((ch->ch_startc == __DISABLED_CHAR) || 76462306a36Sopenharmony_ci (ch->ch_stopc == __DISABLED_CHAR)) 76562306a36Sopenharmony_ci cls_set_no_output_flow_control(ch); 76662306a36Sopenharmony_ci else 76762306a36Sopenharmony_ci cls_set_ixon_flow_control(ch); 76862306a36Sopenharmony_ci } else 76962306a36Sopenharmony_ci cls_set_no_output_flow_control(ch); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (ch->ch_c_cflag & CRTSCTS) 77262306a36Sopenharmony_ci cls_set_rts_flow_control(ch); 77362306a36Sopenharmony_ci else if (ch->ch_c_iflag & IXOFF) { 77462306a36Sopenharmony_ci /* 77562306a36Sopenharmony_ci * If start/stop is set to disable, 77662306a36Sopenharmony_ci * then we should disable flow control. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci if ((ch->ch_startc == __DISABLED_CHAR) || 77962306a36Sopenharmony_ci (ch->ch_stopc == __DISABLED_CHAR)) 78062306a36Sopenharmony_ci cls_set_no_input_flow_control(ch); 78162306a36Sopenharmony_ci else 78262306a36Sopenharmony_ci cls_set_ixoff_flow_control(ch); 78362306a36Sopenharmony_ci } else 78462306a36Sopenharmony_ci cls_set_no_input_flow_control(ch); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci cls_assert_modem_signals(ch); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* get current status of the modem signals now */ 78962306a36Sopenharmony_ci cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* 79362306a36Sopenharmony_ci * cls_intr() 79462306a36Sopenharmony_ci * 79562306a36Sopenharmony_ci * Classic specific interrupt handler. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_cistatic irqreturn_t cls_intr(int irq, void *voidbrd) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct jsm_board *brd = voidbrd; 80062306a36Sopenharmony_ci unsigned long lock_flags; 80162306a36Sopenharmony_ci unsigned char uart_poll; 80262306a36Sopenharmony_ci uint i = 0; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Lock out the slow poller from running on this board. */ 80562306a36Sopenharmony_ci spin_lock_irqsave(&brd->bd_intr_lock, lock_flags); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* 80862306a36Sopenharmony_ci * Check the board's global interrupt offset to see if we 80962306a36Sopenharmony_ci * acctually do have an interrupt pending on us. 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_ci uart_poll = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci jsm_dbg(INTR, &brd->pci_dev, "%s:%d uart_poll: %x\n", 81462306a36Sopenharmony_ci __FILE__, __LINE__, uart_poll); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (!uart_poll) { 81762306a36Sopenharmony_ci jsm_dbg(INTR, &brd->pci_dev, 81862306a36Sopenharmony_ci "Kernel interrupted to me, but no pending interrupts...\n"); 81962306a36Sopenharmony_ci spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); 82062306a36Sopenharmony_ci return IRQ_NONE; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* At this point, we have at least SOMETHING to service, dig further. */ 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Parse each port to find out what caused the interrupt */ 82662306a36Sopenharmony_ci for (i = 0; i < brd->nasync; i++) 82762306a36Sopenharmony_ci cls_parse_isr(brd, i); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return IRQ_HANDLED; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci/* Inits UART */ 83562306a36Sopenharmony_cistatic void cls_uart_init(struct jsm_channel *ch) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); 83862306a36Sopenharmony_ci unsigned char isr_fcr = 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci writeb(0, &ch->ch_cls_uart->ier); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* 84362306a36Sopenharmony_ci * The Enhanced Register Set may only be accessed when 84462306a36Sopenharmony_ci * the Line Control Register is set to 0xBFh. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* Turn on Enhanced/Extended controls */ 85162306a36Sopenharmony_ci isr_fcr |= (UART_EXAR654_EFR_ECB); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* Write old LCR value back out, which turns enhanced access off */ 85662306a36Sopenharmony_ci writeb(lcrb, &ch->ch_cls_uart->lcr); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* Clear out UART and FIFO */ 85962306a36Sopenharmony_ci readb(&ch->ch_cls_uart->txrx); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), 86262306a36Sopenharmony_ci &ch->ch_cls_uart->isr_fcr); 86362306a36Sopenharmony_ci udelay(10); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci ch->ch_flags |= (CH_FIFO_ENABLED | CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci readb(&ch->ch_cls_uart->lsr); 86862306a36Sopenharmony_ci readb(&ch->ch_cls_uart->msr); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/* 87262306a36Sopenharmony_ci * Turns off UART. 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_cistatic void cls_uart_off(struct jsm_channel *ch) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci /* Stop all interrupts from accurring. */ 87762306a36Sopenharmony_ci writeb(0, &ch->ch_cls_uart->ier); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci/* 88162306a36Sopenharmony_ci * cls_get_uarts_bytes_left. 88262306a36Sopenharmony_ci * Returns 0 is nothing left in the FIFO, returns 1 otherwise. 88362306a36Sopenharmony_ci * 88462306a36Sopenharmony_ci * The channel lock MUST be held by the calling function. 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_cistatic u32 cls_get_uart_bytes_left(struct jsm_channel *ch) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci u8 left = 0; 88962306a36Sopenharmony_ci u8 lsr = readb(&ch->ch_cls_uart->lsr); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Determine whether the Transmitter is empty or not */ 89262306a36Sopenharmony_ci if (!(lsr & UART_LSR_TEMT)) 89362306a36Sopenharmony_ci left = 1; 89462306a36Sopenharmony_ci else { 89562306a36Sopenharmony_ci ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); 89662306a36Sopenharmony_ci left = 0; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return left; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/* 90362306a36Sopenharmony_ci * cls_send_break. 90462306a36Sopenharmony_ci * Starts sending a break thru the UART. 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * The channel lock MUST be held by the calling function. 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_cistatic void cls_send_break(struct jsm_channel *ch) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci /* Tell the UART to start sending the break */ 91162306a36Sopenharmony_ci if (!(ch->ch_flags & CH_BREAK_SENDING)) { 91262306a36Sopenharmony_ci u8 temp = readb(&ch->ch_cls_uart->lcr); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci writeb((temp | UART_LCR_SBC), &ch->ch_cls_uart->lcr); 91562306a36Sopenharmony_ci ch->ch_flags |= (CH_BREAK_SENDING); 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci/* 92062306a36Sopenharmony_ci * cls_send_immediate_char. 92162306a36Sopenharmony_ci * Sends a specific character as soon as possible to the UART, 92262306a36Sopenharmony_ci * jumping over any bytes that might be in the write queue. 92362306a36Sopenharmony_ci * 92462306a36Sopenharmony_ci * The channel lock MUST be held by the calling function. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_cistatic void cls_send_immediate_char(struct jsm_channel *ch, unsigned char c) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci writeb(c, &ch->ch_cls_uart->txrx); 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistruct board_ops jsm_cls_ops = { 93262306a36Sopenharmony_ci .intr = cls_intr, 93362306a36Sopenharmony_ci .uart_init = cls_uart_init, 93462306a36Sopenharmony_ci .uart_off = cls_uart_off, 93562306a36Sopenharmony_ci .param = cls_param, 93662306a36Sopenharmony_ci .assert_modem_signals = cls_assert_modem_signals, 93762306a36Sopenharmony_ci .flush_uart_write = cls_flush_uart_write, 93862306a36Sopenharmony_ci .flush_uart_read = cls_flush_uart_read, 93962306a36Sopenharmony_ci .disable_receiver = cls_disable_receiver, 94062306a36Sopenharmony_ci .enable_receiver = cls_enable_receiver, 94162306a36Sopenharmony_ci .send_break = cls_send_break, 94262306a36Sopenharmony_ci .clear_break = cls_clear_break, 94362306a36Sopenharmony_ci .send_start_character = cls_send_start_character, 94462306a36Sopenharmony_ci .send_stop_character = cls_send_stop_character, 94562306a36Sopenharmony_ci .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart, 94662306a36Sopenharmony_ci .get_uart_bytes_left = cls_get_uart_bytes_left, 94762306a36Sopenharmony_ci .send_immediate_char = cls_send_immediate_char 94862306a36Sopenharmony_ci}; 94962306a36Sopenharmony_ci 950