162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/************************************************************************ 362306a36Sopenharmony_ci * Copyright 2003 Digi International (www.digi.com) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 IBM Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Contact Information: 862306a36Sopenharmony_ci * Scott H Kilau <Scott_Kilau@digi.com> 962306a36Sopenharmony_ci * Ananda Venkatarman <mansarov@us.ibm.com> 1062306a36Sopenharmony_ci * Modifications: 1162306a36Sopenharmony_ci * 01/19/06: changed jsm_input routine to use the dynamically allocated 1262306a36Sopenharmony_ci * tty_buffer changes. Contributors: Scott Kilau and Ananda V. 1362306a36Sopenharmony_ci ***********************************************************************/ 1462306a36Sopenharmony_ci#include <linux/tty.h> 1562306a36Sopenharmony_ci#include <linux/tty_flip.h> 1662306a36Sopenharmony_ci#include <linux/serial_reg.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> /* For udelay */ 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "jsm.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic DECLARE_BITMAP(linemap, MAXLINES); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void jsm_carrier(struct jsm_channel *ch); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic inline int jsm_get_mstat(struct jsm_channel *ch) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci unsigned char mstat; 3062306a36Sopenharmony_ci int result; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "start\n"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci mstat = (ch->ch_mostat | ch->ch_mistat); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci result = 0; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (mstat & UART_MCR_DTR) 3962306a36Sopenharmony_ci result |= TIOCM_DTR; 4062306a36Sopenharmony_ci if (mstat & UART_MCR_RTS) 4162306a36Sopenharmony_ci result |= TIOCM_RTS; 4262306a36Sopenharmony_ci if (mstat & UART_MSR_CTS) 4362306a36Sopenharmony_ci result |= TIOCM_CTS; 4462306a36Sopenharmony_ci if (mstat & UART_MSR_DSR) 4562306a36Sopenharmony_ci result |= TIOCM_DSR; 4662306a36Sopenharmony_ci if (mstat & UART_MSR_RI) 4762306a36Sopenharmony_ci result |= TIOCM_RI; 4862306a36Sopenharmony_ci if (mstat & UART_MSR_DCD) 4962306a36Sopenharmony_ci result |= TIOCM_CD; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); 5262306a36Sopenharmony_ci return result; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic unsigned int jsm_tty_tx_empty(struct uart_port *port) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return TIOCSER_TEMT; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Return modem signals to ld. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic unsigned int jsm_tty_get_mctrl(struct uart_port *port) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int result; 6662306a36Sopenharmony_ci struct jsm_channel *channel = 6762306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci result = jsm_get_mstat(channel); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (result < 0) 7462306a36Sopenharmony_ci return -ENXIO; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return result; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * jsm_set_modem_info() 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Set modem signals, called by ld. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistatic void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct jsm_channel *channel = 8962306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (mctrl & TIOCM_RTS) 9462306a36Sopenharmony_ci channel->ch_mostat |= UART_MCR_RTS; 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci channel->ch_mostat &= ~UART_MCR_RTS; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (mctrl & TIOCM_DTR) 9962306a36Sopenharmony_ci channel->ch_mostat |= UART_MCR_DTR; 10062306a36Sopenharmony_ci else 10162306a36Sopenharmony_ci channel->ch_mostat &= ~UART_MCR_DTR; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci channel->ch_bd->bd_ops->assert_modem_signals(channel); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 10662306a36Sopenharmony_ci udelay(10); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * jsm_tty_write() 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * Take data from the user or kernel and send it out to the FEP. 11362306a36Sopenharmony_ci * In here exists all the Transparent Print magic as well. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic void jsm_tty_write(struct uart_port *port) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct jsm_channel *channel; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci channel = container_of(port, struct jsm_channel, uart_port); 12062306a36Sopenharmony_ci channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void jsm_tty_start_tx(struct uart_port *port) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct jsm_channel *channel = 12662306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci channel->ch_flags &= ~(CH_STOP); 13162306a36Sopenharmony_ci jsm_tty_write(port); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void jsm_tty_stop_tx(struct uart_port *port) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct jsm_channel *channel = 13962306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci channel->ch_flags |= (CH_STOP); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void jsm_tty_send_xchar(struct uart_port *port, char ch) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci unsigned long lock_flags; 15162306a36Sopenharmony_ci struct jsm_channel *channel = 15262306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 15362306a36Sopenharmony_ci struct ktermios *termios; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 15662306a36Sopenharmony_ci termios = &port->state->port.tty->termios; 15762306a36Sopenharmony_ci if (ch == termios->c_cc[VSTART]) 15862306a36Sopenharmony_ci channel->ch_bd->bd_ops->send_start_character(channel); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (ch == termios->c_cc[VSTOP]) 16162306a36Sopenharmony_ci channel->ch_bd->bd_ops->send_stop_character(channel); 16262306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void jsm_tty_stop_rx(struct uart_port *port) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct jsm_channel *channel = 16862306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci channel->ch_bd->bd_ops->disable_receiver(channel); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void jsm_tty_break(struct uart_port *port, int break_state) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci unsigned long lock_flags; 17662306a36Sopenharmony_ci struct jsm_channel *channel = 17762306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 18062306a36Sopenharmony_ci if (break_state == -1) 18162306a36Sopenharmony_ci channel->ch_bd->bd_ops->send_break(channel); 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci channel->ch_bd->bd_ops->clear_break(channel); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int jsm_tty_open(struct uart_port *port) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci unsigned long lock_flags; 19162306a36Sopenharmony_ci struct jsm_board *brd; 19262306a36Sopenharmony_ci struct jsm_channel *channel = 19362306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 19462306a36Sopenharmony_ci struct ktermios *termios; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Get board pointer from our array of majors we have allocated */ 19762306a36Sopenharmony_ci brd = channel->ch_bd; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * Allocate channel buffers for read/write/error. 20162306a36Sopenharmony_ci * Set flag, so we don't get trounced on. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci channel->ch_flags |= (CH_OPENING); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Drop locks, as malloc with GFP_KERNEL can sleep */ 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!channel->ch_rqueue) { 20862306a36Sopenharmony_ci channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL); 20962306a36Sopenharmony_ci if (!channel->ch_rqueue) { 21062306a36Sopenharmony_ci jsm_dbg(INIT, &channel->ch_bd->pci_dev, 21162306a36Sopenharmony_ci "unable to allocate read queue buf\n"); 21262306a36Sopenharmony_ci return -ENOMEM; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci if (!channel->ch_equeue) { 21662306a36Sopenharmony_ci channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL); 21762306a36Sopenharmony_ci if (!channel->ch_equeue) { 21862306a36Sopenharmony_ci jsm_dbg(INIT, &channel->ch_bd->pci_dev, 21962306a36Sopenharmony_ci "unable to allocate error queue buf\n"); 22062306a36Sopenharmony_ci return -ENOMEM; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci channel->ch_flags &= ~(CH_OPENING); 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * Initialize if neither terminal is open. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci jsm_dbg(OPEN, &channel->ch_bd->pci_dev, 22962306a36Sopenharmony_ci "jsm_open: initializing channel in open...\n"); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * Flush input queues. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci channel->ch_r_head = channel->ch_r_tail = 0; 23562306a36Sopenharmony_ci channel->ch_e_head = channel->ch_e_tail = 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci brd->bd_ops->flush_uart_write(channel); 23862306a36Sopenharmony_ci brd->bd_ops->flush_uart_read(channel); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci channel->ch_flags = 0; 24162306a36Sopenharmony_ci channel->ch_cached_lsr = 0; 24262306a36Sopenharmony_ci channel->ch_stops_sent = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 24562306a36Sopenharmony_ci termios = &port->state->port.tty->termios; 24662306a36Sopenharmony_ci channel->ch_c_cflag = termios->c_cflag; 24762306a36Sopenharmony_ci channel->ch_c_iflag = termios->c_iflag; 24862306a36Sopenharmony_ci channel->ch_c_oflag = termios->c_oflag; 24962306a36Sopenharmony_ci channel->ch_c_lflag = termios->c_lflag; 25062306a36Sopenharmony_ci channel->ch_startc = termios->c_cc[VSTART]; 25162306a36Sopenharmony_ci channel->ch_stopc = termios->c_cc[VSTOP]; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Tell UART to init itself */ 25462306a36Sopenharmony_ci brd->bd_ops->uart_init(channel); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Run param in case we changed anything 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci brd->bd_ops->param(channel); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci jsm_carrier(channel); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci channel->ch_open_count++; 26462306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n"); 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void jsm_tty_close(struct uart_port *port) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct jsm_board *bd; 27362306a36Sopenharmony_ci struct jsm_channel *channel = 27462306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n"); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci bd = channel->ch_bd; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci channel->ch_flags &= ~(CH_STOPI); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci channel->ch_open_count--; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * If we have HUPCL set, lower DTR and RTS 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci if (channel->ch_c_cflag & HUPCL) { 28862306a36Sopenharmony_ci jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, 28962306a36Sopenharmony_ci "Close. HUPCL set, dropping DTR/RTS\n"); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Drop RTS/DTR */ 29262306a36Sopenharmony_ci channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS); 29362306a36Sopenharmony_ci bd->bd_ops->assert_modem_signals(channel); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Turn off UART interrupts for this port */ 29762306a36Sopenharmony_ci channel->ch_bd->bd_ops->uart_off(channel); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "finish\n"); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic void jsm_tty_set_termios(struct uart_port *port, 30362306a36Sopenharmony_ci struct ktermios *termios, 30462306a36Sopenharmony_ci const struct ktermios *old_termios) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci unsigned long lock_flags; 30762306a36Sopenharmony_ci struct jsm_channel *channel = 30862306a36Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 31162306a36Sopenharmony_ci channel->ch_c_cflag = termios->c_cflag; 31262306a36Sopenharmony_ci channel->ch_c_iflag = termios->c_iflag; 31362306a36Sopenharmony_ci channel->ch_c_oflag = termios->c_oflag; 31462306a36Sopenharmony_ci channel->ch_c_lflag = termios->c_lflag; 31562306a36Sopenharmony_ci channel->ch_startc = termios->c_cc[VSTART]; 31662306a36Sopenharmony_ci channel->ch_stopc = termios->c_cc[VSTOP]; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci channel->ch_bd->bd_ops->param(channel); 31962306a36Sopenharmony_ci jsm_carrier(channel); 32062306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const char *jsm_tty_type(struct uart_port *port) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci return "jsm"; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void jsm_tty_release_port(struct uart_port *port) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int jsm_tty_request_port(struct uart_port *port) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void jsm_config_port(struct uart_port *port, int flags) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci port->type = PORT_JSM; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic const struct uart_ops jsm_ops = { 34362306a36Sopenharmony_ci .tx_empty = jsm_tty_tx_empty, 34462306a36Sopenharmony_ci .set_mctrl = jsm_tty_set_mctrl, 34562306a36Sopenharmony_ci .get_mctrl = jsm_tty_get_mctrl, 34662306a36Sopenharmony_ci .stop_tx = jsm_tty_stop_tx, 34762306a36Sopenharmony_ci .start_tx = jsm_tty_start_tx, 34862306a36Sopenharmony_ci .send_xchar = jsm_tty_send_xchar, 34962306a36Sopenharmony_ci .stop_rx = jsm_tty_stop_rx, 35062306a36Sopenharmony_ci .break_ctl = jsm_tty_break, 35162306a36Sopenharmony_ci .startup = jsm_tty_open, 35262306a36Sopenharmony_ci .shutdown = jsm_tty_close, 35362306a36Sopenharmony_ci .set_termios = jsm_tty_set_termios, 35462306a36Sopenharmony_ci .type = jsm_tty_type, 35562306a36Sopenharmony_ci .release_port = jsm_tty_release_port, 35662306a36Sopenharmony_ci .request_port = jsm_tty_request_port, 35762306a36Sopenharmony_ci .config_port = jsm_config_port, 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* 36162306a36Sopenharmony_ci * jsm_tty_init() 36262306a36Sopenharmony_ci * 36362306a36Sopenharmony_ci * Init the tty subsystem. Called once per board after board has been 36462306a36Sopenharmony_ci * downloaded and init'ed. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ciint jsm_tty_init(struct jsm_board *brd) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int i; 36962306a36Sopenharmony_ci void __iomem *vaddr; 37062306a36Sopenharmony_ci struct jsm_channel *ch; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!brd) 37362306a36Sopenharmony_ci return -ENXIO; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "start\n"); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * Initialize board structure elements. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci brd->nasync = brd->maxports; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * Allocate channel memory that might not have been allocated 38562306a36Sopenharmony_ci * when the driver was first loaded. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci for (i = 0; i < brd->nasync; i++) { 38862306a36Sopenharmony_ci if (!brd->channels[i]) { 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * Okay to malloc with GFP_KERNEL, we are not at 39262306a36Sopenharmony_ci * interrupt context, and there are no locks held. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL); 39562306a36Sopenharmony_ci if (!brd->channels[i]) { 39662306a36Sopenharmony_ci jsm_dbg(CORE, &brd->pci_dev, 39762306a36Sopenharmony_ci "%s:%d Unable to allocate memory for channel struct\n", 39862306a36Sopenharmony_ci __FILE__, __LINE__); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ch = brd->channels[0]; 40462306a36Sopenharmony_ci vaddr = brd->re_map_membase; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Set up channel variables */ 40762306a36Sopenharmony_ci for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!brd->channels[i]) 41062306a36Sopenharmony_ci continue; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci spin_lock_init(&ch->ch_lock); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (brd->bd_uart_offset == 0x200) 41562306a36Sopenharmony_ci ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i); 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci ch->ch_cls_uart = vaddr + (brd->bd_uart_offset * i); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ch->ch_bd = brd; 42062306a36Sopenharmony_ci ch->ch_portnum = i; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* .25 second delay */ 42362306a36Sopenharmony_ci ch->ch_close_delay = 250; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci init_waitqueue_head(&ch->ch_flags_wait); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "finish\n"); 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ciint jsm_uart_port_init(struct jsm_board *brd) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci int i, rc; 43562306a36Sopenharmony_ci unsigned int line; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!brd) 43862306a36Sopenharmony_ci return -ENXIO; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "start\n"); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* 44362306a36Sopenharmony_ci * Initialize board structure elements. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci brd->nasync = brd->maxports; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Set up channel variables */ 44962306a36Sopenharmony_ci for (i = 0; i < brd->nasync; i++) { 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (!brd->channels[i]) 45262306a36Sopenharmony_ci continue; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci brd->channels[i]->uart_port.irq = brd->irq; 45562306a36Sopenharmony_ci brd->channels[i]->uart_port.uartclk = 14745600; 45662306a36Sopenharmony_ci brd->channels[i]->uart_port.type = PORT_JSM; 45762306a36Sopenharmony_ci brd->channels[i]->uart_port.iotype = UPIO_MEM; 45862306a36Sopenharmony_ci brd->channels[i]->uart_port.membase = brd->re_map_membase; 45962306a36Sopenharmony_ci brd->channels[i]->uart_port.fifosize = 16; 46062306a36Sopenharmony_ci brd->channels[i]->uart_port.ops = &jsm_ops; 46162306a36Sopenharmony_ci line = find_first_zero_bit(linemap, MAXLINES); 46262306a36Sopenharmony_ci if (line >= MAXLINES) { 46362306a36Sopenharmony_ci printk(KERN_INFO "jsm: linemap is full, added device failed\n"); 46462306a36Sopenharmony_ci continue; 46562306a36Sopenharmony_ci } else 46662306a36Sopenharmony_ci set_bit(line, linemap); 46762306a36Sopenharmony_ci brd->channels[i]->uart_port.line = line; 46862306a36Sopenharmony_ci rc = uart_add_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); 46962306a36Sopenharmony_ci if (rc) { 47062306a36Sopenharmony_ci printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i); 47162306a36Sopenharmony_ci return rc; 47262306a36Sopenharmony_ci } else 47362306a36Sopenharmony_ci printk(KERN_INFO "jsm: Port %d added\n", i); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "finish\n"); 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciint jsm_remove_uart_port(struct jsm_board *brd) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci int i; 48362306a36Sopenharmony_ci struct jsm_channel *ch; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (!brd) 48662306a36Sopenharmony_ci return -ENXIO; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "start\n"); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * Initialize board structure elements. 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci brd->nasync = brd->maxports; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* Set up channel variables */ 49762306a36Sopenharmony_ci for (i = 0; i < brd->nasync; i++) { 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!brd->channels[i]) 50062306a36Sopenharmony_ci continue; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ch = brd->channels[i]; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci clear_bit(ch->uart_port.line, linemap); 50562306a36Sopenharmony_ci uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "finish\n"); 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_civoid jsm_input(struct jsm_channel *ch) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct jsm_board *bd; 51562306a36Sopenharmony_ci struct tty_struct *tp; 51662306a36Sopenharmony_ci struct tty_port *port; 51762306a36Sopenharmony_ci u32 rmask; 51862306a36Sopenharmony_ci u16 head; 51962306a36Sopenharmony_ci u16 tail; 52062306a36Sopenharmony_ci int data_len; 52162306a36Sopenharmony_ci unsigned long lock_flags; 52262306a36Sopenharmony_ci int len = 0; 52362306a36Sopenharmony_ci int s = 0; 52462306a36Sopenharmony_ci int i = 0; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci port = &ch->uart_port.state->port; 52962306a36Sopenharmony_ci tp = port->tty; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci bd = ch->ch_bd; 53262306a36Sopenharmony_ci if (!bd) 53362306a36Sopenharmony_ci return; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci spin_lock_irqsave(&ch->ch_lock, lock_flags); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* 53862306a36Sopenharmony_ci *Figure the number of characters in the buffer. 53962306a36Sopenharmony_ci *Exit immediately if none. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci rmask = RQUEUEMASK; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci head = ch->ch_r_head & rmask; 54562306a36Sopenharmony_ci tail = ch->ch_r_tail & rmask; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci data_len = (head - tail) & rmask; 54862306a36Sopenharmony_ci if (data_len == 0) { 54962306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 55062306a36Sopenharmony_ci return; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci *If the device is not open, or CREAD is off, flush 55762306a36Sopenharmony_ci *input data and return immediately. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci if (!tp || !C_CREAD(tp)) { 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 56262306a36Sopenharmony_ci "input. dropping %d bytes on port %d...\n", 56362306a36Sopenharmony_ci data_len, ch->ch_portnum); 56462306a36Sopenharmony_ci ch->ch_r_head = tail; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Force queue flow control to be released, if needed */ 56762306a36Sopenharmony_ci jsm_check_queue_flow_control(ch); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 57062306a36Sopenharmony_ci return; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* 57462306a36Sopenharmony_ci * If we are throttled, simply don't read any data. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci if (ch->ch_flags & CH_STOPI) { 57762306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 57862306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 57962306a36Sopenharmony_ci "Port %d throttled, not reading any data. head: %x tail: %x\n", 58062306a36Sopenharmony_ci ch->ch_portnum, head, tail); 58162306a36Sopenharmony_ci return; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci len = tty_buffer_request_room(port, data_len); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* 58962306a36Sopenharmony_ci * len now contains the most amount of data we can copy, 59062306a36Sopenharmony_ci * bounded either by the flip buffer size or the amount 59162306a36Sopenharmony_ci * of data the card actually has pending... 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci while (len) { 59462306a36Sopenharmony_ci s = ((head >= tail) ? head : RQUEUESIZE) - tail; 59562306a36Sopenharmony_ci s = min(s, len); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (s <= 0) 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * If conditions are such that ld needs to see all 60262306a36Sopenharmony_ci * UART errors, we will have to walk each character 60362306a36Sopenharmony_ci * and error byte and send them to the buffer one at 60462306a36Sopenharmony_ci * a time. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { 60862306a36Sopenharmony_ci for (i = 0; i < s; i++) { 60962306a36Sopenharmony_ci u8 chr = ch->ch_rqueue[tail + i]; 61062306a36Sopenharmony_ci u8 error = ch->ch_equeue[tail + i]; 61162306a36Sopenharmony_ci char flag = TTY_NORMAL; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Give the Linux ld the flags in the format it 61562306a36Sopenharmony_ci * likes. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci if (error & UART_LSR_BI) 61862306a36Sopenharmony_ci flag = TTY_BREAK; 61962306a36Sopenharmony_ci else if (error & UART_LSR_PE) 62062306a36Sopenharmony_ci flag = TTY_PARITY; 62162306a36Sopenharmony_ci else if (error & UART_LSR_FE) 62262306a36Sopenharmony_ci flag = TTY_FRAME; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci tty_insert_flip_char(port, chr, flag); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } else { 62762306a36Sopenharmony_ci tty_insert_flip_string(port, ch->ch_rqueue + tail, s); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci tail += s; 63062306a36Sopenharmony_ci len -= s; 63162306a36Sopenharmony_ci /* Flip queue if needed */ 63262306a36Sopenharmony_ci tail &= rmask; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ch->ch_r_tail = tail & rmask; 63662306a36Sopenharmony_ci ch->ch_e_tail = tail & rmask; 63762306a36Sopenharmony_ci jsm_check_queue_flow_control(ch); 63862306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Tell the tty layer its okay to "eat" the data now */ 64162306a36Sopenharmony_ci tty_flip_buffer_push(port); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic void jsm_carrier(struct jsm_channel *ch) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct jsm_board *bd; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci int virt_carrier = 0; 65162306a36Sopenharmony_ci int phys_carrier = 0; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "start\n"); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci bd = ch->ch_bd; 65662306a36Sopenharmony_ci if (!bd) 65762306a36Sopenharmony_ci return; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (ch->ch_mistat & UART_MSR_DCD) { 66062306a36Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "mistat: %x D_CD: %x\n", 66162306a36Sopenharmony_ci ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD); 66262306a36Sopenharmony_ci phys_carrier = 1; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (ch->ch_c_cflag & CLOCAL) 66662306a36Sopenharmony_ci virt_carrier = 1; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "DCD: physical: %d virt: %d\n", 66962306a36Sopenharmony_ci phys_carrier, virt_carrier); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * Test for a VIRTUAL carrier transition to HIGH. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* 67762306a36Sopenharmony_ci * When carrier rises, wake any threads waiting 67862306a36Sopenharmony_ci * for carrier in the open routine. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: virt DCD rose\n"); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (waitqueue_active(&(ch->ch_flags_wait))) 68462306a36Sopenharmony_ci wake_up_interruptible(&ch->ch_flags_wait); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* 68862306a36Sopenharmony_ci * Test for a PHYSICAL carrier transition to HIGH. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* 69362306a36Sopenharmony_ci * When carrier rises, wake any threads waiting 69462306a36Sopenharmony_ci * for carrier in the open routine. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, 69862306a36Sopenharmony_ci "carrier: physical DCD rose\n"); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (waitqueue_active(&(ch->ch_flags_wait))) 70162306a36Sopenharmony_ci wake_up_interruptible(&ch->ch_flags_wait); 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci * Test for a PHYSICAL transition to low, so long as we aren't 70662306a36Sopenharmony_ci * currently ignoring physical transitions (which is what "virtual 70762306a36Sopenharmony_ci * carrier" indicates). 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * The transition of the virtual carrier to low really doesn't 71062306a36Sopenharmony_ci * matter... it really only means "ignore carrier state", not 71162306a36Sopenharmony_ci * "make pretend that carrier is there". 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) 71462306a36Sopenharmony_ci && (phys_carrier == 0)) { 71562306a36Sopenharmony_ci /* 71662306a36Sopenharmony_ci * When carrier drops: 71762306a36Sopenharmony_ci * 71862306a36Sopenharmony_ci * Drop carrier on all open units. 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci * Flush queues, waking up any task waiting in the 72162306a36Sopenharmony_ci * line discipline. 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * Send a hangup to the control terminal. 72462306a36Sopenharmony_ci * 72562306a36Sopenharmony_ci * Enable all select calls. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci if (waitqueue_active(&(ch->ch_flags_wait))) 72862306a36Sopenharmony_ci wake_up_interruptible(&ch->ch_flags_wait); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Make sure that our cached values reflect the current reality. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci if (virt_carrier == 1) 73562306a36Sopenharmony_ci ch->ch_flags |= CH_FCAR; 73662306a36Sopenharmony_ci else 73762306a36Sopenharmony_ci ch->ch_flags &= ~CH_FCAR; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (phys_carrier == 1) 74062306a36Sopenharmony_ci ch->ch_flags |= CH_CD; 74162306a36Sopenharmony_ci else 74262306a36Sopenharmony_ci ch->ch_flags &= ~CH_CD; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_civoid jsm_check_queue_flow_control(struct jsm_channel *ch) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct board_ops *bd_ops = ch->ch_bd->bd_ops; 74962306a36Sopenharmony_ci int qleft; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Store how much space we have left in the queue */ 75262306a36Sopenharmony_ci qleft = ch->ch_r_tail - ch->ch_r_head - 1; 75362306a36Sopenharmony_ci if (qleft < 0) 75462306a36Sopenharmony_ci qleft += RQUEUEMASK + 1; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* 75762306a36Sopenharmony_ci * Check to see if we should enforce flow control on our queue because 75862306a36Sopenharmony_ci * the ld (or user) isn't reading data out of our queue fast enuf. 75962306a36Sopenharmony_ci * 76062306a36Sopenharmony_ci * NOTE: This is done based on what the current flow control of the 76162306a36Sopenharmony_ci * port is set for. 76262306a36Sopenharmony_ci * 76362306a36Sopenharmony_ci * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt. 76462306a36Sopenharmony_ci * This will cause the UART's FIFO to back up, and force 76562306a36Sopenharmony_ci * the RTS signal to be dropped. 76662306a36Sopenharmony_ci * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to 76762306a36Sopenharmony_ci * the other side, in hopes it will stop sending data to us. 76862306a36Sopenharmony_ci * 3) NONE - Nothing we can do. We will simply drop any extra data 76962306a36Sopenharmony_ci * that gets sent into us when the queue fills up. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ci if (qleft < 256) { 77262306a36Sopenharmony_ci /* HWFLOW */ 77362306a36Sopenharmony_ci if (ch->ch_c_cflag & CRTSCTS) { 77462306a36Sopenharmony_ci if (!(ch->ch_flags & CH_RECEIVER_OFF)) { 77562306a36Sopenharmony_ci bd_ops->disable_receiver(ch); 77662306a36Sopenharmony_ci ch->ch_flags |= (CH_RECEIVER_OFF); 77762306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 77862306a36Sopenharmony_ci "Internal queue hit hilevel mark (%d)! Turning off interrupts\n", 77962306a36Sopenharmony_ci qleft); 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci /* SWFLOW */ 78362306a36Sopenharmony_ci else if (ch->ch_c_iflag & IXOFF) { 78462306a36Sopenharmony_ci if (ch->ch_stops_sent <= MAX_STOPS_SENT) { 78562306a36Sopenharmony_ci bd_ops->send_stop_character(ch); 78662306a36Sopenharmony_ci ch->ch_stops_sent++; 78762306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 78862306a36Sopenharmony_ci "Sending stop char! Times sent: %x\n", 78962306a36Sopenharmony_ci ch->ch_stops_sent); 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * Check to see if we should unenforce flow control because 79662306a36Sopenharmony_ci * ld (or user) finally read enuf data out of our queue. 79762306a36Sopenharmony_ci * 79862306a36Sopenharmony_ci * NOTE: This is done based on what the current flow control of the 79962306a36Sopenharmony_ci * port is set for. 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt. 80262306a36Sopenharmony_ci * This will cause the UART's FIFO to raise RTS back up, 80362306a36Sopenharmony_ci * which will allow the other side to start sending data again. 80462306a36Sopenharmony_ci * 2) SWFLOW (IXOFF) - Send a start character to 80562306a36Sopenharmony_ci * the other side, so it will start sending data to us again. 80662306a36Sopenharmony_ci * 3) NONE - Do nothing. Since we didn't do anything to turn off the 80762306a36Sopenharmony_ci * other side, we don't need to do anything now. 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_ci if (qleft > (RQUEUESIZE / 2)) { 81062306a36Sopenharmony_ci /* HWFLOW */ 81162306a36Sopenharmony_ci if (ch->ch_c_cflag & CRTSCTS) { 81262306a36Sopenharmony_ci if (ch->ch_flags & CH_RECEIVER_OFF) { 81362306a36Sopenharmony_ci bd_ops->enable_receiver(ch); 81462306a36Sopenharmony_ci ch->ch_flags &= ~(CH_RECEIVER_OFF); 81562306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 81662306a36Sopenharmony_ci "Internal queue hit lowlevel mark (%d)! Turning on interrupts\n", 81762306a36Sopenharmony_ci qleft); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci /* SWFLOW */ 82162306a36Sopenharmony_ci else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) { 82262306a36Sopenharmony_ci ch->ch_stops_sent = 0; 82362306a36Sopenharmony_ci bd_ops->send_start_character(ch); 82462306a36Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 82562306a36Sopenharmony_ci "Sending start char!\n"); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci} 829