diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-21 03:57:27 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-21 03:57:27 (GMT) |
commit | e10abc629f38efd9b6936cf3612583cc846104d9 (patch) | |
tree | 58fb8f6dba5d085d4b0352137d18faf7cfa47a69 /drivers/tty | |
parent | 0eff4589c36edd03d50b835d0768b2c2ef3f20bd (diff) | |
parent | d20bb59af64667a16f2468b54fc0adb2f824a3b6 (diff) | |
download | linux-e10abc629f38efd9b6936cf3612583cc846104d9.tar.xz |
Merge tag 'tty-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty and serial driver updates from Greg KH:
"Here's the large TTY and Serial driver update for 4.7-rc1.
A few new serial drivers are added here, and Peter has fixed a bunch
of long-standing bugs in the tty layer and serial drivers as normal.
Full details in the shortlog.
All of these have been in linux-next for a while with no reported
issues"
* tag 'tty-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (88 commits)
MAINTAINERS: 8250: remove website reference
serial: core: Fix port mutex assert if lockdep disabled
serial: 8250_dw: fix wrong logic in dw8250_check_lcr()
tty: vt, finish looping on duplicate
tty: vt, return error when con_startup fails
QE-UART: add "fsl,t1040-ucc-uart" to of_device_id
serial: mctrl_gpio: Drop support for out1-gpios and out2-gpios
serial: 8250dw: Add device HID for future AMD UART controller
Fix OpenSSH pty regression on close
serial: mctrl_gpio: add IRQ locking
serial: 8250: Integrate Fintek into 8250_base
serial: mps2-uart: add support for early console
serial: mps2-uart: add MPS2 UART driver
dt-bindings: document the MPS2 UART bindings
serial: sirf: Use generic uart-has-rtscts DT property
serial: sirf: Introduce helper variable struct device_node *np
serial: mxs-auart: Use generic uart-has-rtscts DT property
serial: imx: Use generic uart-has-rtscts DT property
doc: DT: Add Generic Serial Device Tree Bindings
serial: 8250: of: Make tegra_serial_handle_break() static
...
Diffstat (limited to 'drivers/tty')
58 files changed, 2213 insertions, 1027 deletions
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index eacf4c9..208f573 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -398,7 +398,7 @@ static void check_modem_status(struct serial_state *info) wake_up_interruptible(&port->delta_msr_wait); } - if ((port->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { + if (tty_port_check_carrier(port) && (dstatus & SER_DCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttyS%d CD now %s...", info->line, (!(status & SER_DCD)) ? "on" : "off"); @@ -525,7 +525,7 @@ static int startup(struct tty_struct *tty, struct serial_state *info) local_irq_save(flags); - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port)) { free_page(page); goto errout; } @@ -586,7 +586,7 @@ static int startup(struct tty_struct *tty, struct serial_state *info) */ change_speed(tty, info, NULL); - port->flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(port, 1); local_irq_restore(flags); return 0; @@ -604,7 +604,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) unsigned long flags; struct serial_state *state; - if (!(info->tport.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->tport)) return; state = info; @@ -645,7 +645,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) set_bit(TTY_IO_ERROR, &tty->flags); - info->tport.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&info->tport, 0); local_irq_restore(flags); } @@ -727,17 +727,12 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info, info->IER &= ~UART_IER_MSI; if (port->flags & ASYNC_HARDPPS_CD) info->IER |= UART_IER_MSI; - if (cflag & CRTSCTS) { - port->flags |= ASYNC_CTS_FLOW; + tty_port_set_cts_flow(port, cflag & CRTSCTS); + if (cflag & CRTSCTS) info->IER |= UART_IER_MSI; - } else - port->flags &= ~ASYNC_CTS_FLOW; - if (cflag & CLOCAL) - port->flags &= ~ASYNC_CHECK_CD; - else { - port->flags |= ASYNC_CHECK_CD; + tty_port_set_check_carrier(port, ~cflag & CLOCAL); + if (~cflag & CLOCAL) info->IER |= UART_IER_MSI; - } /* TBD: * Does clearing IER_MSI imply that we should disable the VBL interrupt ? */ @@ -1089,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port)) { if (change_spd) { if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) tty->alt_speed = 57600; @@ -1143,7 +1138,7 @@ static int rs_tiocmget(struct tty_struct *tty) if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; control = info->MCR; @@ -1165,7 +1160,7 @@ static int rs_tiocmset(struct tty_struct *tty, unsigned int set, if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; local_irq_save(flags); @@ -1250,7 +1245,7 @@ static int rs_ioctl(struct tty_struct *tty, if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; } @@ -1342,7 +1337,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { info->MCR |= SER_DTR; - if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) + if (!C_CRTSCTS(tty) || !tty_throttled(tty)) info->MCR |= SER_RTS; local_irq_save(flags); rtsdtr_ctrl(info->MCR); @@ -1395,7 +1390,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * line status register. */ state->read_status_mask &= ~UART_LSR_DR; - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port)) { /* disable receive interrupts */ custom.intena = IF_RBF; mb(); @@ -1495,7 +1490,7 @@ static void rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(tty, info); info->tport.count = 0; - info->tport.flags &= ~ASYNC_NORMAL_ACTIVE; + tty_port_set_active(&info->tport, 0); info->tport.tty = NULL; wake_up_interruptible(&info->tport.open_wait); } @@ -1543,7 +1538,7 @@ static inline void line_info(struct seq_file *m, int line, local_irq_save(flags); status = ciab.pra; - control = (state->tport.flags & ASYNC_INITIALIZED) ? state->MCR : status; + control = tty_port_initialized(&state->tport) ? state->MCR : status; local_irq_restore(flags); stat_buf[0] = 0; diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index d67e542..3840d6b 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -714,7 +714,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, wake_up_interruptible(&info->port.delta_msr_wait); } - if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { + if ((mdm_change & CyDCD) && tty_port_check_carrier(&info->port)) { if (mdm_status & CyDCD) wake_up_interruptible(&info->port.open_wait); else @@ -1119,7 +1119,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) case C_CM_MDCD: info->icount.dcd++; delta_count++; - if (info->port.flags & ASYNC_CHECK_CD) { + if (tty_port_check_carrier(&info->port)) { u32 dcd = fw_ver > 241 ? param : readl(&info->u.cyz.ch_ctrl->rs_status); if (dcd & C_RS_DCD) @@ -1279,7 +1279,7 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) spin_lock_irqsave(&card->card_lock, flags); - if (info->port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&info->port)) goto errout; if (!info->type) { @@ -1364,7 +1364,7 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) /* enable send, recv, modem !!! */ } - info->port.flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 1); clear_bit(TTY_IO_ERROR, &tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; @@ -1424,7 +1424,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) struct cyclades_card *card; unsigned long flags; - if (!(info->port.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->port)) return; card = info->card; @@ -1448,7 +1448,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) some later date (after testing)!!! */ set_bit(TTY_IO_ERROR, &tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 0); spin_unlock_irqrestore(&card->card_lock, flags); } else { #ifdef CY_DEBUG_OPEN @@ -1473,7 +1473,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) tty_port_lower_dtr_rts(&info->port); set_bit(TTY_IO_ERROR, &tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 0); spin_unlock_irqrestore(&card->card_lock, flags); } @@ -1711,7 +1711,7 @@ static void cy_do_close(struct tty_port *port) /* Stop accepting input */ cyy_writeb(info, CyCAR, channel & 0x03); cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) { /* Waiting for on-board buffers to be empty before closing the port */ spin_unlock_irqrestore(&card->card_lock, flags); @@ -2083,17 +2083,12 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) info->cor1 |= CyPARITY_NONE; /* CTS flow control flag */ - if (cflag & CRTSCTS) { - info->port.flags |= ASYNC_CTS_FLOW; + tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); + if (cflag & CRTSCTS) info->cor2 |= CyCtsAE; - } else { - info->port.flags &= ~ASYNC_CTS_FLOW; - info->cor2 &= ~CyCtsAE; - } - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; else - info->port.flags |= ASYNC_CHECK_CD; + info->cor2 &= ~CyCtsAE; + tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); /*********************************************** The hardware option, CyRtsAO, presents RTS when @@ -2234,7 +2229,7 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) } /* As the HW flow control is done in firmware, the driver doesn't need to care about it */ - info->port.flags &= ~ASYNC_CTS_FLOW; + tty_port_set_cts_flow(&info->port, 0); /* XON/XOFF/XANY flow control flags */ sw_flow = 0; @@ -2252,10 +2247,7 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) } /* CD sensitivity */ - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; + tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); if (baud == 0) { /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, @@ -2342,7 +2334,7 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, info->port.closing_wait = new_serial.closing_wait * HZ / 100; check_and_exit: - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) { cy_set_line_char(info, tty); ret = 0; } else { diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index e46d628..ce86487 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -632,7 +632,7 @@ int hvc_poll(struct hvc_struct *hp) goto bail; /* Now check if we can get data (are we throttled ?) */ - if (test_bit(TTY_THROTTLED, &tty->flags)) + if (tty_throttled(tty)) goto throttled; /* If we aren't notifier driven and aren't throttled, we always @@ -814,7 +814,7 @@ static int hvc_poll_get_char(struct tty_driver *driver, int line) n = hp->ops->get_chars(hp->vtermno, &ch, 1); - if (n == 0) + if (n <= 0) return NO_POLL_CHAR; return ch; diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 5997b17..3c4d7c2 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -600,7 +600,7 @@ static int hvcs_io(struct hvcs_struct *hvcsd) hvcs_try_write(hvcsd); - if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) { + if (!tty || tty_throttled(tty)) { hvcsd->todo_mask &= ~(HVCS_READ_MASK); goto bail; } else if (!(hvcsd->todo_mask & (HVCS_READ_MASK))) diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index a75146f..96ce6bd 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -509,7 +509,7 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg) } spin_lock_irqsave(&hp->lock, flags); - if (tty && hp->n_throttle && !test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty && hp->n_throttle && !tty_throttled(tty)) { /* we weren't hung up and we weren't throttled, so we can * deliver the rest now */ hvsi_send_overflow(hp); diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index ad7031a..df0204b 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1572,6 +1572,11 @@ static void handle_received_SETUP_packet(struct ipw_hardware *hw, sizeof(struct ipw_setup_reboot_msg_ack), ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP, TL_SETUP_SIGNO_REBOOT_MSG_ACK); + if (!packet) { + pr_err(IPWIRELESS_PCCARD_NAME + ": Not enough memory to send reboot packet"); + break; + } packet->header.length = sizeof(struct TlSetupRebootMsgAck); send_packet(hw, PRIO_SETUP, &packet->header); diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 8bf6763..b70187b 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -438,8 +438,8 @@ static void isicom_tx(unsigned long _data) for (; count > 0; count--, port++) { /* port not active or tx disabled to force flow control */ - if (!(port->port.flags & ASYNC_INITIALIZED) || - !(port->status & ISI_TXOK)) + if (!tty_port_initialized(&port->port) || + !(port->status & ISI_TXOK)) continue; txcount = min_t(short, TX_SIZE, port->xmit_cnt); @@ -553,7 +553,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } port = card->ports + channel; - if (!(port->port.flags & ASYNC_INITIALIZED)) { + if (!tty_port_initialized(&port->port)) { outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); return IRQ_HANDLED; @@ -577,7 +577,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) header = inw(base); switch (header & 0xff) { case 0: /* Change in EIA signals */ - if (port->port.flags & ASYNC_CHECK_CD) { + if (tty_port_check_carrier(&port->port)) { if (port->status & ISI_DCD) { if (!(header & ISI_DCD)) { /* Carrier has been lost */ @@ -758,18 +758,13 @@ static void isicom_config_port(struct tty_struct *tty) outw(channel_setup, base); InterruptTheCard(base); } - if (C_CLOCAL(tty)) - port->port.flags &= ~ASYNC_CHECK_CD; - else - port->port.flags |= ASYNC_CHECK_CD; + tty_port_set_check_carrier(&port->port, !C_CLOCAL(tty)); /* flow control settings ...*/ flow_ctrl = 0; - port->port.flags &= ~ASYNC_CTS_FLOW; - if (C_CRTSCTS(tty)) { - port->port.flags |= ASYNC_CTS_FLOW; + tty_port_set_cts_flow(&port->port, C_CRTSCTS(tty)); + if (C_CRTSCTS(tty)) flow_ctrl |= ISICOM_CTSRTS; - } if (I_IXON(tty)) flow_ctrl |= ISICOM_RESPOND_XONXOFF; if (I_IXOFF(tty)) diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 92982d7..60d37b2 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -912,7 +912,7 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) /* pci hot-un-plug support */ for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&brd->ports[a].port)) tty_port_tty_hangup(&brd->ports[a].port, false); for (a = 0; a < MAX_PORTS_PER_BOARD; a++) @@ -921,7 +921,7 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&brd->ports[a].port)) opened++; mutex_unlock(&moxa_openlock); if (!opened) @@ -1192,13 +1192,13 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) tty->driver_data = ch; tty_port_tty_set(&ch->port, tty); mutex_lock(&ch->port.mutex); - if (!(ch->port.flags & ASYNC_INITIALIZED)) { + if (!tty_port_initialized(&ch->port)) { ch->statusflags = 0; moxa_set_tty_param(tty, &tty->termios); MoxaPortLineCtrl(ch, 1, 1); MoxaPortEnable(ch); MoxaSetFifo(ch, ch->type == PORT_16550A); - ch->port.flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(&ch->port, 1); } mutex_unlock(&ch->port.mutex); mutex_unlock(&moxa_openlock); @@ -1379,7 +1379,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, { struct tty_struct *tty = tty_port_tty_get(&p->port); void __iomem *ofsAddr; - unsigned int inited = p->port.flags & ASYNC_INITIALIZED; + unsigned int inited = tty_port_initialized(&p->port); u16 intr; if (tty) { @@ -1394,7 +1394,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, tty_wakeup(tty); } - if (inited && !test_bit(TTY_THROTTLED, &tty->flags) && + if (inited && !tty_throttled(tty) && MoxaPortRxQueue(p) > 0) { /* RX */ MoxaPortReadData(p); tty_schedule_flip(&p->port); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 2f12bb9..98d2bd1 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -711,8 +711,8 @@ static int mxser_change_speed(struct tty_struct *tty, /* CTS flow control flag and modem status interrupts */ info->IER &= ~UART_IER_MSI; info->MCR &= ~UART_MCR_AFE; + tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); if (cflag & CRTSCTS) { - info->port.flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; if ((info->type == PORT_16550A) || (info->board->chip_flag)) { info->MCR |= UART_MCR_AFE; @@ -744,16 +744,11 @@ static int mxser_change_speed(struct tty_struct *tty, } } } - } else { - info->port.flags &= ~ASYNC_CTS_FLOW; } outb(info->MCR, info->ioaddr + UART_MCR); - if (cflag & CLOCAL) { - info->port.flags &= ~ASYNC_CHECK_CD; - } else { - info->port.flags |= ASYNC_CHECK_CD; + tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); + if (~cflag & CLOCAL) info->IER |= UART_IER_MSI; - } outb(info->IER, info->ioaddr + UART_IER); /* @@ -826,7 +821,7 @@ static void mxser_check_modem_status(struct tty_struct *tty, port->mon_data.modem_status = status; wake_up_interruptible(&port->port.delta_msr_wait); - if ((port->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { + if (tty_port_check_carrier(&port->port) && (status & UART_MSR_DDCD)) { if (status & UART_MSR_DCD) wake_up_interruptible(&port->port.open_wait); } @@ -1086,12 +1081,10 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) mutex_lock(&port->mutex); mxser_close_port(port); mxser_flush_buffer(tty); - if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { - if (C_HUPCL(tty)) - tty_port_lower_dtr_rts(port); - } + if (tty_port_initialized(port) && C_HUPCL(tty)) + tty_port_lower_dtr_rts(port); mxser_shutdown_port(port); - clear_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 0); mutex_unlock(&port->mutex); info->closing = 0; /* Right now the tty_port set is done outside of the close_end helper @@ -1287,7 +1280,7 @@ static int mxser_set_serial_info(struct tty_struct *tty, process_txrx_fifo(info); - if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (tty_port_initialized(port)) { if (flags != (port->flags & ASYNC_SPD_MASK)) { spin_lock_irqsave(&info->slock, sl_flags); mxser_change_speed(tty, NULL); @@ -1296,7 +1289,7 @@ static int mxser_set_serial_info(struct tty_struct *tty, } else { retval = mxser_activate(port, tty); if (retval == 0) - set_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 1); } return retval; } @@ -1334,7 +1327,7 @@ static int mxser_tiocmget(struct tty_struct *tty) if (tty->index == MXSER_PORTS) return -ENOIOCTLCMD; - if (test_bit(TTY_IO_ERROR, &tty->flags)) + if (tty_io_error(tty)) return -EIO; control = info->MCR; @@ -1361,7 +1354,7 @@ static int mxser_tiocmset(struct tty_struct *tty, if (tty->index == MXSER_PORTS) return -ENOIOCTLCMD; - if (test_bit(TTY_IO_ERROR, &tty->flags)) + if (tty_io_error(tty)) return -EIO; spin_lock_irqsave(&info->slock, flags); @@ -1715,8 +1708,7 @@ static int mxser_ioctl(struct tty_struct *tty, return 0; } - if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && - test_bit(TTY_IO_ERROR, &tty->flags)) + if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && tty_io_error(tty)) return -EIO; switch (cmd) { @@ -2257,7 +2249,7 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) iir &= MOXA_MUST_IIR_MASK; tty = tty_port_tty_get(&port->port); if (!tty || port->closing || - !(port->port.flags & ASYNC_INITIALIZED)) { + !tty_port_initialized(&port->port)) { status = inb(port->ioaddr + UART_LSR); outb(0x27, port->ioaddr + UART_FCR); inb(port->ioaddr + UART_MSR); @@ -2400,7 +2392,6 @@ static int mxser_initbrd(struct mxser_board *brd, if (brd->chip_flag != MOXA_OTHER_UART) mxser_enable_must_enchance_mode(info->ioaddr); - info->port.flags = ASYNC_SHARE_IRQ; info->type = brd->uart_type; process_txrx_fifo(info); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 0c27a00..54cab59 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2045,7 +2045,9 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm) } } spin_unlock(&gsm_mux_lock); - WARN_ON(i == MAX_MUX); + /* open failed before registering => nothing to do */ + if (i == MAX_MUX) + return; /* In theory disconnecting DLCI 0 is sufficient but for some modems this is apparently not the case. */ @@ -2947,7 +2949,7 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) dlci->modem_rx = 0; /* We could in theory open and close before we wait - eg if we get a DM straight back. This is ok as that will have caused a hangup */ - set_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 1); /* Start sending off SABM messages */ gsm_dlci_begin_open(dlci); /* And wait for virtual carrier */ @@ -2970,10 +2972,8 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) if (tty_port_close_start(&dlci->port, tty, filp) == 0) return; gsm_dlci_begin_close(dlci); - if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { - if (C_HUPCL(tty)) - tty_port_lower_dtr_rts(&dlci->port); - } + if (tty_port_initialized(&dlci->port) && C_HUPCL(tty)) + tty_port_lower_dtr_rts(&dlci->port); tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); return; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index bcaba17..a7fa016 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -599,7 +599,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, add_wait_queue(&tty->read_wait, &wait); for (;;) { - if (test_bit(TTY_OTHER_DONE, &tty->flags)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { ret = -EIO; break; } @@ -827,7 +827,7 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, /* set bits for operations that won't block */ if (n_hdlc->rx_buf_list.head) mask |= POLLIN | POLLRDNORM; /* readable */ - if (test_bit(TTY_OTHER_DONE, &tty->flags)) + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(filp)) mask |= POLLHUP; diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index fb76a7d..bdf0e6e 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1917,18 +1917,6 @@ static inline int input_available_p(struct tty_struct *tty, int poll) return ldata->commit_head - ldata->read_tail >= amt; } -static inline int check_other_done(struct tty_struct *tty) -{ - int done = test_bit(TTY_OTHER_DONE, &tty->flags); - if (done) { - /* paired with cmpxchg() in check_other_closed(); ensures - * read buffer head index is not stale - */ - smp_mb__after_atomic(); - } - return done; -} - /** * copy_from_read_buf - copy read data directly * @tty: terminal device @@ -2124,7 +2112,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, struct n_tty_data *ldata = tty->disc_data; unsigned char __user *b = buf; DEFINE_WAIT_FUNC(wait, woken_wake_function); - int c, done; + int c; int minimum, time; ssize_t retval = 0; long timeout; @@ -2183,32 +2171,35 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, break; } - done = check_other_done(tty); - if (!input_available_p(tty, 0)) { - if (done) { - retval = -EIO; - break; - } - if (tty_hung_up_p(file)) - break; - if (!timeout) - break; - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } up_read(&tty->termios_rwsem); + tty_buffer_flush_work(tty->port); + down_read(&tty->termios_rwsem); + if (!input_available_p(tty, 0)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + retval = -EIO; + break; + } + if (tty_hung_up_p(file)) + break; + if (!timeout) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + up_read(&tty->termios_rwsem); - timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, - timeout); + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, + timeout); - down_read(&tty->termios_rwsem); - continue; + down_read(&tty->termios_rwsem); + continue; + } } if (ldata->icanon && !L_EXTPROC(tty)) { @@ -2386,12 +2377,17 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_wait(file, &tty->read_wait, wait); poll_wait(file, &tty->write_wait, wait); - if (check_other_done(tty)) - mask |= POLLHUP; if (input_available_p(tty, 1)) mask |= POLLIN | POLLRDNORM; + else { + tty_buffer_flush_work(tty->port); + if (input_available_p(tty, 1)) + mask |= POLLIN | POLLRDNORM; + } if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; if (tty->ops->write && !tty_is_writelocked(tty) && diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 5cc80b8..d6fd0e8 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -826,7 +826,7 @@ static int receive_data(enum port_type index, struct nozomi *dc) size = __le32_to_cpu(readl(addr)); /* DBG1( "%d bytes port: %d", size, index); */ - if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty && tty_throttled(tty)) { DBG1("No room in tty, don't read data, don't ack interrupt, " "disable interrupt"); diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index cf0dc51..dd4b841 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -44,7 +44,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) if (tty->driver->subtype == PTY_TYPE_MASTER) WARN_ON(tty->count > 1); else { - if (test_bit(TTY_IO_ERROR, &tty->flags)) + if (tty_io_error(tty)) return; if (tty->count > 2) return; @@ -59,7 +59,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) if (!tty->link) return; set_bit(TTY_OTHER_CLOSED, &tty->link->flags); - tty_flip_buffer_push(tty->link->port); + wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->write_wait); if (tty->driver->subtype == PTY_TYPE_MASTER) { set_bit(TTY_OTHER_CLOSED, &tty->flags); @@ -247,9 +247,7 @@ static int pty_open(struct tty_struct *tty, struct file *filp) goto out; clear_bit(TTY_IO_ERROR, &tty->flags); - /* TTY_OTHER_CLOSED must be cleared before TTY_OTHER_DONE */ clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); - clear_bit(TTY_OTHER_DONE, &tty->link->flags); set_bit(TTY_THROTTLED, &tty->flags); return 0; diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 0b802cd..b0cc47c 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -495,7 +495,7 @@ static void rp_handle_port(struct r_port *info) if (!info) return; - if ((info->port.flags & ASYNC_INITIALIZED) == 0) { + if (!tty_port_initialized(&info->port)) { printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " "info->flags & NOT_INIT\n"); return; @@ -615,7 +615,8 @@ static void rp_do_poll(unsigned long dummy) * the board. * Inputs: board, aiop, chan numbers */ -static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) +static void __init +init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) { unsigned rocketMode; struct r_port *info; @@ -920,7 +921,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp) /* * Info->count is now 1; so it's safe to sleep now. */ - if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (!tty_port_initialized(port)) { cp = &info->channel; sSetRxTrigger(cp, TRIG_1); if (sGetChanStatus(cp) & CD_ACT) @@ -944,7 +945,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp) sEnRxFIFO(cp); sEnTransmit(cp); - set_bit(ASYNCB_INITIALIZED, &info->port.flags); + tty_port_set_initialized(&info->port, 1); /* * Set up the tty->alt_speed kludge @@ -1042,9 +1043,10 @@ static void rp_close(struct tty_struct *tty, struct file *filp) } } spin_lock_irq(&port->lock); - info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_NORMAL_ACTIVE); tty->closing = 0; spin_unlock_irq(&port->lock); + tty_port_set_initialized(port, 0); + tty_port_set_active(port, 0); mutex_unlock(&port->mutex); tty_port_tty_set(port, NULL); @@ -1512,7 +1514,7 @@ static void rp_hangup(struct tty_struct *tty) sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sClrTxXOFF(cp); - clear_bit(ASYNCB_INITIALIZED, &info->port.flags); + tty_port_set_initialized(&info->port, 0); wake_up_interruptible(&info->port.open_wait); } @@ -1624,7 +1626,7 @@ static int rp_write(struct tty_struct *tty, /* Write remaining data into the port's xmit_buf */ while (1) { /* Hung up ? */ - if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags)) + if (!tty_port_active(&info->port)) goto end; c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1); c = min(c, XMIT_BUF_SIZE - info->xmit_head); diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 047a7ba..215a992 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -17,7 +17,7 @@ struct uart_8250_dma { int (*tx_dma)(struct uart_8250_port *p); - int (*rx_dma)(struct uart_8250_port *p, unsigned int iir); + int (*rx_dma)(struct uart_8250_port *p); /* Filter function */ dma_filter_fn fn; @@ -84,7 +84,6 @@ struct serial8250_config { #define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ #define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */ -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) #ifdef CONFIG_SERIAL_8250_SHARE_IRQ #define SERIAL8250_SHARE_IRQS 1 @@ -151,6 +150,12 @@ static inline int serial8250_pnp_init(void) { return 0; } static inline void serial8250_pnp_exit(void) { } #endif +#ifdef CONFIG_SERIAL_8250_FINTEK +int fintek_8250_probe(struct uart_8250_port *uart); +#else +static inline int fintek_8250_probe(struct uart_8250_port *uart) { return 0; } +#endif + #ifdef CONFIG_ARCH_OMAP1 static inline int is_omap1_8250(struct uart_8250_port *pt) { @@ -190,7 +195,8 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt) #ifdef CONFIG_SERIAL_8250_DMA extern int serial8250_tx_dma(struct uart_8250_port *); -extern int serial8250_rx_dma(struct uart_8250_port *, unsigned int iir); +extern int serial8250_rx_dma(struct uart_8250_port *); +extern void serial8250_rx_dma_flush(struct uart_8250_port *); extern int serial8250_request_dma(struct uart_8250_port *); extern void serial8250_release_dma(struct uart_8250_port *); #else @@ -198,10 +204,11 @@ static inline int serial8250_tx_dma(struct uart_8250_port *p) { return -1; } -static inline int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +static inline int serial8250_rx_dma(struct uart_8250_port *p) { return -1; } +static inline void serial8250_rx_dma_flush(struct uart_8250_port *p) { } static inline int serial8250_request_dma(struct uart_8250_port *p) { return -1; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 2f4f5ee..0fbd7c0 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -830,6 +830,7 @@ static int serial8250_probe(struct platform_device *dev) uart.port.handle_irq = p->handle_irq; uart.port.handle_break = p->handle_break; uart.port.set_termios = p->set_termios; + uart.port.get_mctrl = p->get_mctrl; uart.port.pm = p->pm; uart.port.dev = &dev->dev; uart.port.irqflags |= irqflag; @@ -1022,6 +1023,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) /* Possibly override set_termios call */ if (up->port.set_termios) uart->port.set_termios = up->port.set_termios; + if (up->port.get_mctrl) + uart->port.get_mctrl = up->port.get_mctrl; if (up->port.set_mctrl) uart->port.set_mctrl = up->port.set_mctrl; if (up->port.startup) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 78259d3..7f33d1c 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -110,30 +110,11 @@ err: return ret; } -int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +int serial8250_rx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; struct dma_async_tx_descriptor *desc; - switch (iir & 0x3f) { - case UART_IIR_RLSI: - /* 8250_core handles errors and break interrupts */ - return -EIO; - case UART_IIR_RX_TIMEOUT: - /* - * If RCVR FIFO trigger level was not reached, complete the - * transfer and let 8250_core copy the remaining data. - */ - if (dma->rx_running) { - dmaengine_pause(dma->rxchan); - __dma_rx_complete(p); - dmaengine_terminate_all(dma->rxchan); - } - return -ETIMEDOUT; - default: - break; - } - if (dma->rx_running) return 0; @@ -154,10 +135,23 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) return 0; } +void serial8250_rx_dma_flush(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (dma->rx_running) { + dmaengine_pause(dma->rxchan); + __dma_rx_complete(p); + dmaengine_terminate_all(dma->rxchan); + } +} + int serial8250_request_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; dma_cap_mask_t mask; + struct dma_slave_caps caps; + int ret; /* Default slave configuration parameters */ dma->rxconf.direction = DMA_DEV_TO_MEM; @@ -178,6 +172,16 @@ int serial8250_request_dma(struct uart_8250_port *p) if (!dma->rxchan) return -ENODEV; + /* 8250 rx dma requires dmaengine driver to support pause/terminate */ + ret = dma_get_slave_caps(dma->rxchan, &caps); + if (ret) + goto release_rx; + if (!caps.cmd_pause || !caps.cmd_terminate || + caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) { + ret = -EINVAL; + goto release_rx; + } + dmaengine_slave_config(dma->rxchan, &dma->rxconf); /* Get a channel for TX */ @@ -185,8 +189,17 @@ int serial8250_request_dma(struct uart_8250_port *p) dma->fn, dma->tx_param, p->port.dev, "tx"); if (!dma->txchan) { - dma_release_channel(dma->rxchan); - return -ENODEV; + ret = -ENODEV; + goto release_rx; + } + + /* 8250 tx dma requires dmaengine driver to support terminate */ + ret = dma_get_slave_caps(dma->txchan, &caps); + if (ret) + goto err; + if (!caps.cmd_terminate) { + ret = -EINVAL; + goto err; } dmaengine_slave_config(dma->txchan, &dma->txconf); @@ -197,8 +210,10 @@ int serial8250_request_dma(struct uart_8250_port *p) dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, &dma->rx_addr, GFP_KERNEL); - if (!dma->rx_buf) + if (!dma->rx_buf) { + ret = -ENOMEM; goto err; + } /* TX buffer */ dma->tx_addr = dma_map_single(dma->txchan->device->dev, @@ -208,6 +223,7 @@ int serial8250_request_dma(struct uart_8250_port *p) if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, dma->rx_addr); + ret = -ENOMEM; goto err; } @@ -215,10 +231,10 @@ int serial8250_request_dma(struct uart_8250_port *p) return 0; err: - dma_release_channel(dma->rxchan); dma_release_channel(dma->txchan); - - return -ENOMEM; +release_rx: + dma_release_channel(dma->rxchan); + return ret; } EXPORT_SYMBOL_GPL(serial8250_request_dma); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index a3fb95d..e199696 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -104,15 +104,16 @@ static void dw8250_check_lcr(struct uart_port *p, int value) dw8250_force_idle(p); #ifdef CONFIG_64BIT - __raw_writeq(value & 0xff, offset); -#else + if (p->type == PORT_OCTEON) + __raw_writeq(value & 0xff, offset); + else +#endif if (p->iotype == UPIO_MEM32) writel(value, offset); else if (p->iotype == UPIO_MEM32BE) iowrite32be(value, offset); else writeb(value, offset); -#endif } /* * FIXME: this deadlocks if port->lock is already held @@ -617,6 +618,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = { { "8086228A", 0 }, { "APMC0D08", 0}, { "AMD0020", 0 }, + { "AMDI0020", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 8947439..870981d 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -1,9 +1,7 @@ /* * Probe for F81216A LPC to 4 UART * - * Based on drivers/tty/serial/8250_pnp.c, by Russell King, et al - * - * Copyright (C) 2014 Ricardo Ribalda, Qtechnology A/S + * Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S * * * This program is free software; you can redistribute it and/or modify @@ -38,19 +36,15 @@ #define RXW4C_IRA BIT(3) #define TXW4C_IRA BIT(2) -#define DRIVER_NAME "8250_fintek" - struct fintek_8250 { u16 base_port; u8 index; u8 key; - long line; }; static int fintek_8250_enter_key(u16 base_port, u8 key) { - - if (!request_muxed_region(base_port, 2, DRIVER_NAME)) + if (!request_muxed_region(base_port, 2, "8250_fintek")) return -EBUSY; outb(key, base_port + ADDR_PORT); @@ -138,7 +132,7 @@ static int fintek_8250_rs485_config(struct uart_port *port, return 0; } -static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index) +static int find_base_port(struct fintek_8250 *pdata, u16 io_address) { static const u16 addr[] = {0x4e, 0x2e}; static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; @@ -168,10 +162,13 @@ static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index) continue; fintek_8250_exit_key(addr[i]); - *key = keys[j]; - *index = k; - return addr[i]; + pdata->key = keys[j]; + pdata->base_port = addr[i]; + pdata->index = k; + + return 0; } + fintek_8250_exit_key(addr[i]); } } @@ -179,104 +176,21 @@ static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index) return -ENODEV; } -static int -fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +int fintek_8250_probe(struct uart_8250_port *uart) { - struct uart_8250_port uart; struct fintek_8250 *pdata; - int base_port; - u8 key; - u8 index; - - if (!pnp_port_valid(dev, 0)) - return -ENODEV; + struct fintek_8250 probe_data; - base_port = fintek_8250_base_port(pnp_port_start(dev, 0), &key, &index); - if (base_port < 0) + if (find_base_port(&probe_data, uart->port.iobase)) return -ENODEV; - memset(&uart, 0, sizeof(uart)); - - pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - uart.port.private_data = pdata; - if (!pnp_irq_valid(dev, 0)) - return -ENODEV; - uart.port.irq = pnp_irq(dev, 0); - uart.port.iobase = pnp_port_start(dev, 0); - uart.port.iotype = UPIO_PORT; - uart.port.rs485_config = fintek_8250_rs485_config; - - uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - uart.port.flags |= UPF_SHARE_IRQ; - uart.port.uartclk = 1843200; - uart.port.dev = &dev->dev; - - pdata->key = key; - pdata->base_port = base_port; - pdata->index = index; - pdata->line = serial8250_register_8250_port(&uart); - if (pdata->line < 0) - return -ENODEV; + memcpy(pdata, &probe_data, sizeof(probe_data)); + uart->port.rs485_config = fintek_8250_rs485_config; + uart->port.private_data = pdata; - pnp_set_drvdata(dev, pdata); return 0; } - -static void fintek_8250_remove(struct pnp_dev *dev) -{ - struct fintek_8250 *pdata = pnp_get_drvdata(dev); - - if (pdata) - serial8250_unregister_port(pdata->line); -} - -#ifdef CONFIG_PM -static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state) -{ - struct fintek_8250 *pdata = pnp_get_drvdata(dev); - - if (!pdata) - return -ENODEV; - serial8250_suspend_port(pdata->line); - return 0; -} - -static int fintek_8250_resume(struct pnp_dev *dev) -{ - struct fintek_8250 *pdata = pnp_get_drvdata(dev); - - if (!pdata) - return -ENODEV; - serial8250_resume_port(pdata->line); - return 0; -} -#else -#define fintek_8250_suspend NULL -#define fintek_8250_resume NULL -#endif /* CONFIG_PM */ - -static const struct pnp_device_id fintek_dev_table[] = { - /* Qtechnology Panel PC / IO1000 */ - { "PNP0501"}, - {} -}; - -MODULE_DEVICE_TABLE(pnp, fintek_dev_table); - -static struct pnp_driver fintek_8250_driver = { - .name = DRIVER_NAME, - .probe = fintek_8250_probe, - .remove = fintek_8250_remove, - .suspend = fintek_8250_suspend, - .resume = fintek_8250_resume, - .id_table = fintek_dev_table, -}; - -module_pnp_driver(fintek_8250_driver); -MODULE_DESCRIPTION("Fintek F812164 module"); -MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 88531a3..86379a7 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -9,11 +9,13 @@ * published by the Free Software Foundation. */ -#include <linux/rational.h> +#include <linux/bitops.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/rational.h> #include <linux/dma/hsu.h> +#include <linux/8250_pci.h> #include "8250.h" @@ -24,6 +26,7 @@ #define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8 /* Intel MID Specific registers */ +#define INTEL_MID_UART_DNV_FISR 0x08 #define INTEL_MID_UART_PS 0x30 #define INTEL_MID_UART_MUL 0x34 #define INTEL_MID_UART_DIV 0x38 @@ -31,6 +34,7 @@ struct mid8250; struct mid8250_board { + unsigned int flags; unsigned long freq; unsigned int base_baud; int (*setup)(struct mid8250 *, struct uart_port *p); @@ -76,7 +80,11 @@ static int tng_setup(struct mid8250 *mid, struct uart_port *p) struct pci_dev *pdev = to_pci_dev(p->dev); int index = PCI_FUNC(pdev->devfn); - /* Currently no support for HSU port0 */ + /* + * Device 0000:00:04.0 is not a real HSU port. It provides a global + * register set for all HSU ports, although it has the same PCI ID. + * Skip it here. + */ if (index-- == 0) return -ENODEV; @@ -88,16 +96,16 @@ static int tng_setup(struct mid8250 *mid, struct uart_port *p) static int dnv_handle_irq(struct uart_port *p) { struct mid8250 *mid = p->private_data; - int ret; - - ret = hsu_dma_irq(&mid->dma_chip, 0); - ret |= hsu_dma_irq(&mid->dma_chip, 1); - - /* For now, letting the HW generate separate interrupt for the UART */ - if (ret) - return ret; - - return serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + unsigned int fisr = serial_port_in(p, INTEL_MID_UART_DNV_FISR); + int ret = IRQ_NONE; + + if (fisr & BIT(2)) + ret |= hsu_dma_irq(&mid->dma_chip, 1); + if (fisr & BIT(1)) + ret |= hsu_dma_irq(&mid->dma_chip, 0); + if (fisr & BIT(0)) + ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + return ret; } #define DNV_DMA_CHAN_OFFSET 0x80 @@ -106,12 +114,13 @@ static int dnv_setup(struct mid8250 *mid, struct uart_port *p) { struct hsu_dma_chip *chip = &mid->dma_chip; struct pci_dev *pdev = to_pci_dev(p->dev); + unsigned int bar = FL_GET_BASE(mid->board->flags); int ret; chip->dev = &pdev->dev; chip->irq = pdev->irq; chip->regs = p->membase; - chip->length = pci_resource_len(pdev, 0); + chip->length = pci_resource_len(pdev, bar); chip->offset = DNV_DMA_CHAN_OFFSET; /* Falling back to PIO mode if DMA probing fails */ @@ -217,6 +226,7 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct uart_8250_port uart; struct mid8250 *mid; + unsigned int bar; int ret; ret = pcim_enable_device(pdev); @@ -230,6 +240,7 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; mid->board = (struct mid8250_board *)id->driver_data; + bar = FL_GET_BASE(mid->board->flags); memset(&uart, 0, sizeof(struct uart_8250_port)); @@ -242,8 +253,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.set_termios = mid8250_set_termios; - uart.port.mapbase = pci_resource_start(pdev, 0); - uart.port.membase = pcim_iomap(pdev, 0, 0); + uart.port.mapbase = pci_resource_start(pdev, bar); + uart.port.membase = pcim_iomap(pdev, bar, 0); if (!uart.port.membase) return -ENOMEM; @@ -282,18 +293,21 @@ static void mid8250_remove(struct pci_dev *pdev) } static const struct mid8250_board pnw_board = { + .flags = FL_BASE0, .freq = 50000000, .base_baud = 115200, .setup = pnw_setup, }; static const struct mid8250_board tng_board = { + .flags = FL_BASE0, .freq = 38400000, .base_baud = 1843200, .setup = tng_setup, }; static const struct mid8250_board dnv_board = { + .flags = FL_BASE1, .freq = 133333333, .base_baud = 115200, .setup = dnv_setup, diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index c7ed3d2..38963d7 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -29,7 +29,7 @@ struct of_serial_info { }; #ifdef CONFIG_ARCH_TEGRA -void tegra_serial_handle_break(struct uart_port *p) +static void tegra_serial_handle_break(struct uart_port *p) { unsigned int status, tmout = 10000; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 6f76051..2c44c79 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -115,6 +115,12 @@ struct omap8250_priv { bool rx_dma_broken; }; +#ifdef CONFIG_SERIAL_8250_DMA +static void omap_8250_rx_dma_flush(struct uart_8250_port *p); +#else +static inline void omap_8250_rx_dma_flush(struct uart_8250_port *p) { } +#endif + static u32 uart_read(struct uart_8250_port *up, u32 reg) { return readl(up->port.membase + (reg << up->port.regshift)); @@ -635,7 +641,7 @@ static int omap_8250_startup(struct uart_port *port) serial_out(up, UART_OMAP_WER, priv->wer); if (up->dma) - up->dma->rx_dma(up, 0); + up->dma->rx_dma(up); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); @@ -654,7 +660,7 @@ static void omap_8250_shutdown(struct uart_port *port) flush_work(&priv->qos_work); if (up->dma) - up->dma->rx_dma(up, UART_IIR_RX_TIMEOUT); + omap_8250_rx_dma_flush(up); pm_runtime_get_sync(port->dev); @@ -742,9 +748,9 @@ static void omap_8250_unthrottle(struct uart_port *port) } #ifdef CONFIG_SERIAL_8250_DMA -static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir); +static int omap_8250_rx_dma(struct uart_8250_port *p); -static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) +static void __dma_rx_do_complete(struct uart_8250_port *p) { struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; @@ -754,9 +760,6 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) unsigned long flags; int ret; - dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, - dma->rx_size, DMA_FROM_DEVICE); - spin_lock_irqsave(&priv->rx_dma_lock, flags); if (!dma->rx_running) @@ -764,7 +767,6 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) dma->rx_running = 0; dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); - dmaengine_terminate_all(dma->rxchan); count = dma->rx_size - state.residue; @@ -775,15 +777,13 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) unlock: spin_unlock_irqrestore(&priv->rx_dma_lock, flags); - if (!error) - omap_8250_rx_dma(p, 0); - tty_flip_buffer_push(tty_port); } static void __dma_rx_complete(void *param) { - __dma_rx_do_complete(param, false); + __dma_rx_do_complete(param); + omap_8250_rx_dma(param); } static void omap_8250_rx_dma_flush(struct uart_8250_port *p) @@ -806,10 +806,11 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p) spin_unlock_irqrestore(&priv->rx_dma_lock, flags); - __dma_rx_do_complete(p, true); + __dma_rx_do_complete(p); + dmaengine_terminate_all(dma->rxchan); } -static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +static int omap_8250_rx_dma(struct uart_8250_port *p) { struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; @@ -817,35 +818,6 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) struct dma_async_tx_descriptor *desc; unsigned long flags; - switch (iir & 0x3f) { - case UART_IIR_RLSI: - /* 8250_core handles errors and break interrupts */ - omap_8250_rx_dma_flush(p); - return -EIO; - case UART_IIR_RX_TIMEOUT: - /* - * If RCVR FIFO trigger level was not reached, complete the - * transfer and let 8250_core copy the remaining data. - */ - omap_8250_rx_dma_flush(p); - return -ETIMEDOUT; - case UART_IIR_RDI: - /* - * The OMAP UART is a special BEAST. If we receive RDI we _have_ - * a DMA transfer programmed but it didn't work. One reason is - * that we were too slow and there were too many bytes in the - * FIFO, the UART counted wrong and never kicked the DMA engine - * to do anything. That means once we receive RDI on OMAP then - * the DMA won't do anything soon so we have to cancel the DMA - * transfer and purge the FIFO manually. - */ - omap_8250_rx_dma_flush(p); - return -ETIMEDOUT; - - default: - break; - } - if (priv->rx_dma_broken) return -EINVAL; @@ -868,9 +840,6 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) dma->rx_cookie = dmaengine_submit(desc); - dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr, - dma->rx_size, DMA_FROM_DEVICE); - dma_async_issue_pending(dma->rxchan); out: spin_unlock_irqrestore(&priv->rx_dma_lock, flags); @@ -1022,6 +991,18 @@ err: return ret; } +static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) +{ + switch (iir & 0x3f) { + case UART_IIR_RLSI: + case UART_IIR_RX_TIMEOUT: + case UART_IIR_RDI: + omap_8250_rx_dma_flush(up); + return true; + } + return omap_8250_rx_dma(up); +} + /* * This is mostly serial8250_handle_irq(). We have a slightly different DMA * hoook for RX/TX and need different logic for them in the ISR. Therefore we @@ -1033,7 +1014,6 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) unsigned char status; unsigned long flags; u8 iir; - int dma_err = 0; serial8250_rpm_get(up); @@ -1048,11 +1028,9 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) status = serial_port_in(port, UART_LSR); if (status & (UART_LSR_DR | UART_LSR_BI)) { - - dma_err = omap_8250_rx_dma(up, iir); - if (dma_err) { + if (handle_rx_dma(up, iir)) { status = serial8250_rx_chars(up, status); - omap_8250_rx_dma(up, 0); + omap_8250_rx_dma(up); } } serial8250_modem_status(up); @@ -1066,8 +1044,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) * try again due to an earlier failer which * might have been resolved by now. */ - dma_err = omap_8250_tx_dma(up); - if (dma_err) + if (omap_8250_tx_dma(up)) serial8250_tx_chars(up); } } @@ -1084,7 +1061,7 @@ static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param) #else -static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +static inline int omap_8250_rx_dma(struct uart_8250_port *p) { return -EINVAL; } @@ -1395,7 +1372,7 @@ static int omap8250_runtime_suspend(struct device *dev) } if (up->dma && up->dma->rxchan) - omap_8250_rx_dma(up, UART_IIR_RX_TIMEOUT); + omap_8250_rx_dma_flush(up); priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; schedule_work(&priv->qos_work); @@ -1407,20 +1384,18 @@ static int omap8250_runtime_resume(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up; - int loss_cntx; /* In case runtime-pm tries this before we are setup */ if (!priv) return 0; up = serial8250_get_port(priv->line); - loss_cntx = omap8250_lost_context(up); - if (loss_cntx) + if (omap8250_lost_context(up)) omap8250_restore_regs(up); if (up->dma && up->dma->rxchan) - omap_8250_rx_dma(up, 0); + omap_8250_rx_dma(up); priv->latency = priv->calc_latency; schedule_work(&priv->qos_work); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 5eea74d..8dd250f 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1377,6 +1377,9 @@ byt_set_termios(struct uart_port *p, struct ktermios *termios, unsigned long m, n; u32 reg; + /* Gracefully handle the B0 case: fall back to B9600 */ + fuart = fuart ? fuart : 9600 * 16; + /* Get Fuart closer to Fref */ fuart *= rounddown_pow_of_two(fref / fuart); @@ -1413,6 +1416,17 @@ static bool byt_dma_filter(struct dma_chan *chan, void *param) return true; } +static unsigned int +byt_get_mctrl(struct uart_port *port) +{ + unsigned int ret = serial8250_do_get_mctrl(port); + + /* Force DCD and DSR signals to permanently be reported as active. */ + ret |= TIOCM_CAR | TIOCM_DSR; + + return ret; +} + static int byt_serial_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1477,6 +1491,7 @@ byt_serial_setup(struct serial_private *priv, port->port.type = PORT_16550A; port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); port->port.set_termios = byt_set_termios; + port->port.get_mctrl = byt_get_mctrl; port->port.fifosize = 64; port->tx_loadsz = 64; port->dma = dma; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 00ad263..d403603 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1315,6 +1315,13 @@ static void autoconfig(struct uart_8250_port *up) out_lock: spin_unlock_irqrestore(&port->lock, flags); + + /* + * Check if the device is a Fintek F81216A + */ + if (port->type == PORT_16550A) + fintek_8250_probe(up); + if (up->capabilities != old_capabilities) { pr_warn("ttyS%d: detected caps %08x should be %08x\n", serial_index(port), old_capabilities, @@ -1788,6 +1795,18 @@ unsigned int serial8250_modem_status(struct uart_8250_port *up) } EXPORT_SYMBOL_GPL(serial8250_modem_status); +static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) +{ + switch (iir & 0x3f) { + case UART_IIR_RX_TIMEOUT: + serial8250_rx_dma_flush(up); + /* fall-through */ + case UART_IIR_RLSI: + return true; + } + return up->dma->rx_dma(up); +} + /* * This handles the interrupt from one port. */ @@ -1796,7 +1815,6 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) unsigned char status; unsigned long flags; struct uart_8250_port *up = up_to_u8250p(port); - int dma_err = 0; if (iir & UART_IIR_NO_INT) return 0; @@ -1808,15 +1826,11 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) DEBUG_INTR("status = %x...", status); if (status & (UART_LSR_DR | UART_LSR_BI)) { - if (up->dma) - dma_err = up->dma->rx_dma(up, iir); - - if (!up->dma || dma_err) + if (!up->dma || handle_rx_dma(up, iir)) status = serial8250_rx_chars(up, status); } serial8250_modem_status(up); - if ((!up->dma || (up->dma && up->dma->tx_err)) && - (status & UART_LSR_THRE)) + if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE)) serial8250_tx_chars(up); spin_unlock_irqrestore(&port->lock, flags); @@ -1882,7 +1896,7 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; } -static unsigned int serial8250_get_mctrl(struct uart_port *port) +unsigned int serial8250_do_get_mctrl(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned int status; @@ -1903,6 +1917,14 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) ret |= TIOCM_CTS; return ret; } +EXPORT_SYMBOL_GPL(serial8250_do_get_mctrl); + +static unsigned int serial8250_get_mctrl(struct uart_port *port) +{ + if (port->get_mctrl) + return port->get_mctrl(port); + return serial8250_do_get_mctrl(port); +} void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl) { diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 1b7bd26..efd1f9c 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -209,7 +209,7 @@ static int uniphier_uart_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "failed to get IRQ number"); + dev_err(dev, "failed to get IRQ number\n"); return irq; } diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 4d7cb9c..e46761d 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -57,6 +57,18 @@ config SERIAL_8250_PNP This builds standard PNP serial support. You may be able to disable this feature if you only need legacy serial support. +config SERIAL_8250_FINTEK + bool "Support for Fintek F81216A LPC to 4 UART RS485 API" + depends on SERIAL_8250 + ---help--- + Selecting this option will add support for the RS485 capabilities + of the Fintek F81216A LPC to 4 UART. + + If this option is not selected the device will be configured as a + standard 16550A serial port. + + If unsure, say N. + config SERIAL_8250_CONSOLE bool "Console on 8250/16550 and compatible serial port" depends on SERIAL_8250=y @@ -358,14 +370,6 @@ config SERIAL_8250_OMAP_TTYO_FIXUP not booting kernel because the serial console remains silent in case they forgot to update the command line. -config SERIAL_8250_FINTEK - tristate "Support for Fintek F81216A LPC to 4 UART" - depends on SERIAL_8250 && PNP - help - Selecting this option will add support for the Fintek F81216A - LPC to 4 UART. This device has some RS485 functionality not available - through the PNP driver. If unsure, say N. - config SERIAL_8250_LPC18XX tristate "NXP LPC18xx/43xx serial port support" depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST) @@ -398,8 +402,10 @@ config SERIAL_8250_INGENIC its UARTs, say Y to this option. If unsure, say N. config SERIAL_8250_MID - tristate "Support for serial ports on Intel MID platforms" + tristate "Support for serial ports on Intel MID platforms" if EXPERT + default SERIAL_8250 depends on SERIAL_8250 && PCI + depends on X86 || COMPILE_TEST select HSU_DMA if SERIAL_8250_DMA select HSU_DMA_PCI if (HSU_DMA && X86_INTEL_MID) select RATIONAL diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index c9a2d6e..367d403 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o 8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o 8250_base-y := 8250_port.o 8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o +8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o @@ -23,7 +24,6 @@ obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o -obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7711b26..7e3a58c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -213,6 +213,7 @@ config SERIAL_MESON_CONSOLE bool "Support for console on meson" depends on SERIAL_MESON=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Say Y here if you wish to use a Amlogic MesonX UART as the system console (the system console is the device which @@ -1404,11 +1405,12 @@ config SERIAL_PCH_UART_CONSOLE config SERIAL_MXS_AUART tristate "MXS AUART support" depends on HAS_DMA - depends on ARCH_MXS || COMPILE_TEST + depends on ARCH_MXS || MACH_ASM9260 || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB help - This driver supports the MXS Application UART (AUART) port. + This driver supports the MXS and Alphascale ASM9260 Application + UART (AUART) port. config SERIAL_MXS_AUART_CONSOLE bool "MXS AUART console support" @@ -1467,6 +1469,19 @@ config SERIAL_EFM32_UART This driver support the USART and UART ports on Energy Micro's efm32 SoCs. +config SERIAL_MPS2_UART_CONSOLE + bool "MPS2 UART console support" + depends on SERIAL_MPS2_UART + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + +config SERIAL_MPS2_UART + bool "MPS2 UART port" + depends on ARM || COMPILE_TEST + select SERIAL_CORE + help + This driver support the UART ports on ARM MPS2. + config SERIAL_EFM32_UART_CONSOLE bool "EFM32 UART/USART console support" depends on SERIAL_EFM32_UART=y @@ -1625,6 +1640,7 @@ config SERIAL_STM32_CONSOLE config SERIAL_MVEBU_UART bool "Marvell EBU serial port support" + depends on ARCH_MVEBU || COMPILE_TEST select SERIAL_CORE help This driver is for Marvell EBU SoC's UART. If you have a machine diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 74914aa..1278d37 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o +obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 7c198e0..a2aa655 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -121,6 +121,7 @@ static struct vendor_data vendor_arm = { static struct vendor_data vendor_sbsa = { .reg_offset = pl011_std_offsets, + .access_32b = true, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index d9439e6..954941d 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -274,6 +274,13 @@ static bool atmel_use_dma_rx(struct uart_port *port) return atmel_port->use_dma_rx; } +static bool atmel_use_fifo(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->fifo_size; +} + static unsigned int atmel_get_lines_status(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); @@ -2090,7 +2097,12 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, mode |= ATMEL_US_USMODE_RS485; } else if (termios->c_cflag & CRTSCTS) { /* RS232 with hardware handshake (RTS/CTS) */ - mode |= ATMEL_US_USMODE_HWHS; + if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) { + dev_info(port->dev, "not enabling hardware flow control because DMA is used"); + termios->c_cflag &= ~CRTSCTS; + } else { + mode |= ATMEL_US_USMODE_HWHS; + } } else { /* RS232 without hadware handshake */ mode |= ATMEL_US_USMODE_NORMAL; diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index c0172bf54..315c849 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -2599,7 +2599,7 @@ startup(struct e100_serial * info) /* if it was already initialized, skip this */ - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) { local_irq_restore(flags); free_page(xmit_page); return 0; @@ -2703,7 +2703,7 @@ startup(struct e100_serial * info) e100_rts(info, 1); e100_dtr(info, 1); - info->port.flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 1); local_irq_restore(flags); return 0; @@ -2745,7 +2745,7 @@ shutdown(struct e100_serial * info) info->tr_running = 0; } - if (!(info->port.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->port)) return; #ifdef SERIAL_DEBUG_OPEN @@ -2776,7 +2776,7 @@ shutdown(struct e100_serial * info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 0); local_irq_restore(flags); } @@ -3273,9 +3273,9 @@ set_serial_info(struct e100_serial *info, info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) change_speed(info); - } else + else retval = startup(info); return retval; } @@ -3445,7 +3445,7 @@ rs_ioctl(struct tty_struct *tty, if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; } @@ -3628,7 +3628,7 @@ rs_close(struct tty_struct *tty, struct file * filp) e100_disable_rx(info); e100_disable_rx_irq(info); - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) { /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -3648,8 +3648,8 @@ rs_close(struct tty_struct *tty, struct file * filp) schedule_timeout_interruptible(info->port.close_delay); wake_up_interruptible(&info->port.open_wait); } - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; local_irq_restore(flags); + tty_port_set_active(&info->port, 0); /* port closed */ @@ -3732,7 +3732,7 @@ rs_hangup(struct tty_struct *tty) shutdown(info); info->event = 0; info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + tty_port_set_active(&info->port, 0); info->port.tty = NULL; wake_up_interruptible(&info->port.open_wait); } @@ -3755,9 +3755,8 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->port.flags |= ASYNC_NORMAL_ACTIVE; + if ((filp->f_flags & O_NONBLOCK) || tty_io_error(tty)) { + tty_port_set_active(&info->port, 1); return 0; } @@ -3788,8 +3787,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, e100_dtr(info, 1); local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->port.flags & ASYNC_INITIALIZED)) { + if (tty_hung_up_p(filp) || !tty_port_initialized(&info->port)) { #ifdef SERIAL_DO_RESTART if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; @@ -3826,7 +3824,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif if (retval) return retval; - info->port.flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(&info->port, 1); return 0; } diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 2085a6c..d386346 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -651,7 +651,7 @@ static void ifx_spi_complete(void *ctx) struct ifx_spi_device *ifx_dev = ctx; int length; int actual_length; - unsigned char more; + unsigned char more = 0; unsigned char cts; int local_write_pending = 0; int queue_length; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 231e7d5..0df2b1c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -114,6 +114,7 @@ #define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ #define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ #define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ +#define UCR3_DTRDEN (1<<3) /* Data Terminal Ready Delta Enable. */ #define IMX21_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select */ #define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ #define UCR3_BPEN (1<<0) /* Preset registers enable */ @@ -142,7 +143,7 @@ #define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ #define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ #define USR1_AGTIM (1<<8) /* Ageing timer interrupt flag */ -#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ +#define USR1_DTRD (1<<7) /* DTR Delta */ #define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ #define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ #define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */ @@ -361,6 +362,7 @@ static void imx_stop_tx(struct uart_port *port) imx_port_rts_inactive(sport, &temp); else imx_port_rts_active(sport, &temp); + temp |= UCR2_RXEN; writel(temp, port->membase + UCR2); temp = readl(port->membase + UCR4); @@ -568,6 +570,8 @@ static void imx_start_tx(struct uart_port *port) imx_port_rts_inactive(sport, &temp); else imx_port_rts_active(sport, &temp); + if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) + temp &= ~UCR2_RXEN; writel(temp, port->membase + UCR2); /* enable transmitter and shifter empty irq */ @@ -729,11 +733,61 @@ static void imx_dma_rxint(struct imx_port *sport) spin_unlock_irqrestore(&sport->port.lock, flags); } +/* + * We have a modem side uart, so the meanings of RTS and CTS are inverted. + */ +static unsigned int imx_get_hwmctrl(struct imx_port *sport) +{ + unsigned int tmp = TIOCM_DSR; + unsigned usr1 = readl(sport->port.membase + USR1); + + if (usr1 & USR1_RTSS) + tmp |= TIOCM_CTS; + + /* in DCE mode DCDIN is always 0 */ + if (!(usr1 & USR2_DCDIN)) + tmp |= TIOCM_CAR; + + if (sport->dte_mode) + if (!(readl(sport->port.membase + USR2) & USR2_RIIN)) + tmp |= TIOCM_RI; + + return tmp; +} + +/* + * Handle any change of modem status signal since we were last called. + */ +static void imx_mctrl_check(struct imx_port *sport) +{ + unsigned int status, changed; + + status = imx_get_hwmctrl(sport); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI && status & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); +} + static irqreturn_t imx_int(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int sts; unsigned int sts2; + irqreturn_t ret = IRQ_NONE; sts = readl(sport->port.membase + USR1); sts2 = readl(sport->port.membase + USR2); @@ -743,26 +797,47 @@ static irqreturn_t imx_int(int irq, void *dev_id) imx_dma_rxint(sport); else imx_rxint(irq, dev_id); + ret = IRQ_HANDLED; } if ((sts & USR1_TRDY && readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) || (sts2 & USR2_TXDC && - readl(sport->port.membase + UCR4) & UCR4_TCEN)) + readl(sport->port.membase + UCR4) & UCR4_TCEN)) { imx_txint(irq, dev_id); + ret = IRQ_HANDLED; + } + + if (sts & USR1_DTRD) { + unsigned long flags; + + if (sts & USR1_DTRD) + writel(USR1_DTRD, sport->port.membase + USR1); + + spin_lock_irqsave(&sport->port.lock, flags); + imx_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); - if (sts & USR1_RTSD) + ret = IRQ_HANDLED; + } + + if (sts & USR1_RTSD) { imx_rtsint(irq, dev_id); + ret = IRQ_HANDLED; + } - if (sts & USR1_AWAKE) + if (sts & USR1_AWAKE) { writel(USR1_AWAKE, sport->port.membase + USR1); + ret = IRQ_HANDLED; + } if (sts2 & USR2_ORE) { sport->port.icount.overrun++; writel(USR2_ORE, sport->port.membase + USR2); + ret = IRQ_HANDLED; } - return IRQ_HANDLED; + return ret; } /* @@ -782,28 +857,6 @@ static unsigned int imx_tx_empty(struct uart_port *port) return ret; } -/* - * We have a modem side uart, so the meanings of RTS and CTS are inverted. - */ -static unsigned int imx_get_hwmctrl(struct imx_port *sport) -{ - unsigned int tmp = TIOCM_DSR; - unsigned usr1 = readl(sport->port.membase + USR1); - - if (usr1 & USR1_RTSS) - tmp |= TIOCM_CTS; - - /* in DCE mode DCDIN is always 0 */ - if (!(usr1 & USR2_DCDIN)) - tmp |= TIOCM_CAR; - - /* in DCE mode RIIN is always 0 */ - if (readl(sport->port.membase + USR2) & USR2_RIIN) - tmp |= TIOCM_RI; - - return tmp; -} - static unsigned int imx_get_mctrl(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -861,33 +914,6 @@ static void imx_break_ctl(struct uart_port *port, int break_state) } /* - * Handle any change of modem status signal since we were last called. - */ -static void imx_mctrl_check(struct imx_port *sport) -{ - unsigned int status, changed; - - status = imx_get_hwmctrl(sport); - changed = status ^ sport->old_status; - - if (changed == 0) - return; - - sport->old_status = status; - - if (changed & TIOCM_RI) - sport->port.icount.rng++; - if (changed & TIOCM_DSR) - sport->port.icount.dsr++; - if (changed & TIOCM_CAR) - uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); - if (changed & TIOCM_CTS) - uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - - wake_up_interruptible(&sport->port.state->port.delta_msr_wait); -} - -/* * This is our per-port timeout handler, for checking the * modem status signals. */ @@ -1193,7 +1219,7 @@ static int imx_startup(struct uart_port *port) /* * Finally, clear and enable interrupts */ - writel(USR1_RTSD, sport->port.membase + USR1); + writel(USR1_RTSD | USR1_DTRD, sport->port.membase + USR1); writel(USR2_ORE, sport->port.membase + USR2); if (sport->dma_is_inited && !sport->dma_is_enabled) @@ -1212,11 +1238,32 @@ static int imx_startup(struct uart_port *port) temp |= (UCR2_RXEN | UCR2_TXEN); if (!sport->have_rtscts) temp |= UCR2_IRTS; + /* + * make sure the edge sensitive RTS-irq is disabled, + * we're using RTSD instead. + */ + if (!is_imx1_uart(sport)) + temp &= ~UCR2_RTSEN; writel(temp, sport->port.membase + UCR2); if (!is_imx1_uart(sport)) { temp = readl(sport->port.membase + UCR3); - temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP; + + /* + * The effect of RI and DCD differs depending on the UFCR_DCEDTE + * bit. In DCE mode they control the outputs, in DTE mode they + * enable the respective irqs. At least the DCD irq cannot be + * cleared on i.MX25 at least, so it's not usable and must be + * disabled. I don't have test hardware to check if RI has the + * same problem but I consider this likely so it's disabled for + * now, too. + */ + temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | + UCR3_DTRDEN | UCR3_RI | UCR3_DCD; + + if (sport->dte_mode) + temp &= ~(UCR3_RI | UCR3_DCD); + writel(temp, sport->port.membase + UCR3); } @@ -1610,19 +1657,17 @@ static int imx_rs485_config(struct uart_port *port, struct serial_rs485 *rs485conf) { struct imx_port *sport = (struct imx_port *)port; + unsigned long temp; /* unimplemented */ rs485conf->delay_rts_before_send = 0; rs485conf->delay_rts_after_send = 0; - rs485conf->flags |= SER_RS485_RX_DURING_TX; /* RTS is required to control the transmitter */ if (!sport->have_rtscts) rs485conf->flags &= ~SER_RS485_ENABLED; if (rs485conf->flags & SER_RS485_ENABLED) { - unsigned long temp; - /* disable transmitter */ temp = readl(sport->port.membase + UCR2); if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) @@ -1632,6 +1677,14 @@ static int imx_rs485_config(struct uart_port *port, writel(temp, sport->port.membase + UCR2); } + /* Make sure Rx is enabled in case Tx is active with Rx disabled */ + if (!(rs485conf->flags & SER_RS485_ENABLED) || + rs485conf->flags & SER_RS485_RX_DURING_TX) { + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_RXEN; + writel(temp, sport->port.membase + UCR2); + } + port->rs485 = *rs485conf; return 0; @@ -1927,7 +1980,8 @@ static int serial_imx_probe_dt(struct imx_port *sport, } sport->port.line = ret; - if (of_get_property(np, "fsl,uart-has-rtscts", NULL)) + if (of_get_property(np, "uart-has-rtscts", NULL) || + of_get_property(np, "fsl,uart-has-rtscts", NULL) /* deprecated */) sport->have_rtscts = 1; if (of_get_property(np, "fsl,dte-mode", NULL)) diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 024445a..6aea0f4 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -481,18 +481,13 @@ static void meson_console_putchar(struct uart_port *port, int ch) writel(ch, port->membase + AML_UART_WFIFO); } -static void meson_serial_console_write(struct console *co, const char *s, - u_int count) +static void meson_serial_port_write(struct uart_port *port, const char *s, + u_int count) { - struct uart_port *port; unsigned long flags; int locked; u32 val, tmp; - port = meson_ports[co->index]; - if (!port) - return; - local_irq_save(flags); if (port->sysrq) { locked = 0; @@ -516,6 +511,18 @@ static void meson_serial_console_write(struct console *co, const char *s, local_irq_restore(flags); } +static void meson_serial_console_write(struct console *co, const char *s, + u_int count) +{ + struct uart_port *port; + + port = meson_ports[co->index]; + if (!port) + return; + + meson_serial_port_write(port, s, count); +} + static int meson_serial_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -554,6 +561,27 @@ static int __init meson_serial_console_init(void) } console_initcall(meson_serial_console_init); +static void meson_serial_early_console_write(struct console *co, + const char *s, + u_int count) +{ + struct earlycon_device *dev = co->data; + + meson_serial_port_write(&dev->port, s, count); +} + +static int __init +meson_serial_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = meson_serial_early_console_write; + return 0; +} +OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart", + meson_serial_early_console_setup); + #define MESON_SERIAL_CONSOLE (&meson_serial_console) #else #define MESON_SERIAL_CONSOLE NULL diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c new file mode 100644 index 0000000..da9e27d --- /dev/null +++ b/drivers/tty/serial/mps2-uart.c @@ -0,0 +1,625 @@ +/* + * Copyright (C) 2015 ARM Limited + * + * Author: Vladimir Murzin <vladimir.murzin@arm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: support for SysRq + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial_core.h> +#include <linux/tty_flip.h> +#include <linux/types.h> + +#define SERIAL_NAME "ttyMPS" +#define DRIVER_NAME "mps2-uart" +#define MAKE_NAME(x) (DRIVER_NAME # x) + +#define UARTn_DATA 0x00 + +#define UARTn_STATE 0x04 +#define UARTn_STATE_TX_FULL BIT(0) +#define UARTn_STATE_RX_FULL BIT(1) +#define UARTn_STATE_TX_OVERRUN BIT(2) +#define UARTn_STATE_RX_OVERRUN BIT(3) + +#define UARTn_CTRL 0x08 +#define UARTn_CTRL_TX_ENABLE BIT(0) +#define UARTn_CTRL_RX_ENABLE BIT(1) +#define UARTn_CTRL_TX_INT_ENABLE BIT(2) +#define UARTn_CTRL_RX_INT_ENABLE BIT(3) +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE BIT(4) +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE BIT(5) + +#define UARTn_INT 0x0c +#define UARTn_INT_TX BIT(0) +#define UARTn_INT_RX BIT(1) +#define UARTn_INT_TX_OVERRUN BIT(2) +#define UARTn_INT_RX_OVERRUN BIT(3) + +#define UARTn_BAUDDIV 0x10 +#define UARTn_BAUDDIV_MASK GENMASK(20, 0) + +/* + * Helpers to make typical enable/disable operations more readable. + */ +#define UARTn_CTRL_TX_GRP (UARTn_CTRL_TX_ENABLE |\ + UARTn_CTRL_TX_INT_ENABLE |\ + UARTn_CTRL_TX_OVERRUN_INT_ENABLE) + +#define UARTn_CTRL_RX_GRP (UARTn_CTRL_RX_ENABLE |\ + UARTn_CTRL_RX_INT_ENABLE |\ + UARTn_CTRL_RX_OVERRUN_INT_ENABLE) + +#define MPS2_MAX_PORTS 3 + +struct mps2_uart_port { + struct uart_port port; + struct clk *clk; + unsigned int tx_irq; + unsigned int rx_irq; +}; + +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port) +{ + return container_of(port, struct mps2_uart_port, port); +} + +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned int off) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + + writeb(val, mps_port->port.membase + off); +} + +static u8 mps2_uart_read8(struct uart_port *port, unsigned int off) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + + return readb(mps_port->port.membase + off); +} + +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned int off) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + + writel_relaxed(val, mps_port->port.membase + off); +} + +static unsigned int mps2_uart_tx_empty(struct uart_port *port) +{ + u8 status = mps2_uart_read8(port, UARTn_STATE); + + return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT; +} + +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int mps2_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +static void mps2_uart_stop_tx(struct uart_port *port) +{ + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control &= ~UARTn_CTRL_TX_INT_ENABLE; + + mps2_uart_write8(port, control, UARTn_CTRL); +} + +static void mps2_uart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) { + if (port->x_char) { + mps2_uart_write8(port, port->x_char, UARTn_DATA); + port->x_char = 0; + port->icount.tx++; + continue; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + break; + + mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA); + xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE; + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + mps2_uart_stop_tx(port); +} + +static void mps2_uart_start_tx(struct uart_port *port) +{ + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control |= UARTn_CTRL_TX_INT_ENABLE; + + mps2_uart_write8(port, control, UARTn_CTRL); + + /* + * We've just unmasked the TX IRQ and now slow-starting via + * polling; if there is enough data to fill up the internal + * write buffer in one go, the TX IRQ should assert, at which + * point we switch to fully interrupt-driven TX. + */ + + mps2_uart_tx_chars(port); +} + +static void mps2_uart_stop_rx(struct uart_port *port) +{ + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control &= ~UARTn_CTRL_RX_GRP; + + mps2_uart_write8(port, control, UARTn_CTRL); +} + +static void mps2_uart_break_ctl(struct uart_port *port, int ctl) +{ +} + +static void mps2_uart_rx_chars(struct uart_port *port) +{ + struct tty_port *tport = &port->state->port; + + while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) { + u8 rxdata = mps2_uart_read8(port, UARTn_DATA); + + port->icount.rx++; + tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL); + } + + tty_flip_buffer_push(tport); +} + +static irqreturn_t mps2_uart_rxirq(int irq, void *data) +{ + struct uart_port *port = data; + u8 irqflag = mps2_uart_read8(port, UARTn_INT); + + if (unlikely(!(irqflag & UARTn_INT_RX))) + return IRQ_NONE; + + spin_lock(&port->lock); + + mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT); + mps2_uart_rx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mps2_uart_txirq(int irq, void *data) +{ + struct uart_port *port = data; + u8 irqflag = mps2_uart_read8(port, UARTn_INT); + + if (unlikely(!(irqflag & UARTn_INT_TX))) + return IRQ_NONE; + + spin_lock(&port->lock); + + mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT); + mps2_uart_tx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mps2_uart_oerrirq(int irq, void *data) +{ + irqreturn_t handled = IRQ_NONE; + struct uart_port *port = data; + u8 irqflag = mps2_uart_read8(port, UARTn_INT); + + spin_lock(&port->lock); + + if (irqflag & UARTn_INT_RX_OVERRUN) { + struct tty_port *tport = &port->state->port; + + mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT); + port->icount.overrun++; + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + tty_flip_buffer_push(tport); + handled = IRQ_HANDLED; + } + + /* + * It's never been seen in practice and it never *should* happen since + * we check if there is enough room in TX buffer before sending data. + * So we keep this check in case something suspicious has happened. + */ + if (irqflag & UARTn_INT_TX_OVERRUN) { + mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT); + handled = IRQ_HANDLED; + } + + spin_unlock(&port->lock); + + return handled; +} + +static int mps2_uart_startup(struct uart_port *port) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + u8 control = mps2_uart_read8(port, UARTn_CTRL); + int ret; + + control &= ~(UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP); + + mps2_uart_write8(port, control, UARTn_CTRL); + + ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0, + MAKE_NAME(-rx), mps_port); + if (ret) { + dev_err(port->dev, "failed to register rxirq (%d)\n", ret); + return ret; + } + + ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0, + MAKE_NAME(-tx), mps_port); + if (ret) { + dev_err(port->dev, "failed to register txirq (%d)\n", ret); + goto err_free_rxirq; + } + + ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED, + MAKE_NAME(-overrun), mps_port); + + if (ret) { + dev_err(port->dev, "failed to register oerrirq (%d)\n", ret); + goto err_free_txirq; + } + + control |= UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP; + + mps2_uart_write8(port, control, UARTn_CTRL); + + return 0; + +err_free_txirq: + free_irq(mps_port->tx_irq, mps_port); +err_free_rxirq: + free_irq(mps_port->rx_irq, mps_port); + + return ret; +} + +static void mps2_uart_shutdown(struct uart_port *port) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control &= ~(UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP); + + mps2_uart_write8(port, control, UARTn_CTRL); + + free_irq(mps_port->rx_irq, mps_port); + free_irq(mps_port->tx_irq, mps_port); + free_irq(port->irq, mps_port); +} + +static void +mps2_uart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, bauddiv; + + termios->c_cflag &= ~(CRTSCTS | CMSPAR); + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + termios->c_cflag &= ~PARENB; + termios->c_cflag &= ~CSTOPB; + + baud = uart_get_baud_rate(port, termios, old, + DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK), + DIV_ROUND_CLOSEST(port->uartclk, 16)); + + bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud); + + spin_lock_irqsave(&port->lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV); + + spin_unlock_irqrestore(&port->lock, flags); + + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *mps2_uart_type(struct uart_port *port) +{ + return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL; +} + +static void mps2_uart_release_port(struct uart_port *port) +{ +} + +static int mps2_uart_request_port(struct uart_port *port) +{ + return 0; +} + +static void mps2_uart_config_port(struct uart_port *port, int type) +{ + if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port)) + port->type = PORT_MPS2UART; +} + +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo) +{ + return -EINVAL; +} + +static const struct uart_ops mps2_uart_pops = { + .tx_empty = mps2_uart_tx_empty, + .set_mctrl = mps2_uart_set_mctrl, + .get_mctrl = mps2_uart_get_mctrl, + .stop_tx = mps2_uart_stop_tx, + .start_tx = mps2_uart_start_tx, + .stop_rx = mps2_uart_stop_rx, + .break_ctl = mps2_uart_break_ctl, + .startup = mps2_uart_startup, + .shutdown = mps2_uart_shutdown, + .set_termios = mps2_uart_set_termios, + .type = mps2_uart_type, + .release_port = mps2_uart_release_port, + .request_port = mps2_uart_request_port, + .config_port = mps2_uart_config_port, + .verify_port = mps2_uart_verify_port, +}; + +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS]; + +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE +static void mps2_uart_console_putchar(struct uart_port *port, int ch) +{ + while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL) + cpu_relax(); + + mps2_uart_write8(port, ch, UARTn_DATA); +} + +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt) +{ + struct uart_port *port = &mps2_uart_ports[co->index].port; + + uart_console_write(port, s, cnt, mps2_uart_console_putchar); +} + +static int mps2_uart_console_setup(struct console *co, char *options) +{ + struct mps2_uart_port *mps_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= MPS2_MAX_PORTS) + return -ENODEV; + + mps_port = &mps2_uart_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&mps_port->port, co, baud, parity, bits, flow); +} + +static struct uart_driver mps2_uart_driver; + +static struct console mps2_uart_console = { + .name = SERIAL_NAME, + .device = uart_console_device, + .write = mps2_uart_console_write, + .setup = mps2_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mps2_uart_driver, +}; + +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console) + +static void mps2_early_putchar(struct uart_port *port, int ch) +{ + while (readb(port->membase + UARTn_STATE) & UARTn_STATE_TX_FULL) + cpu_relax(); + + writeb((unsigned char)ch, port->membase + UARTn_DATA); +} + +static void mps2_early_write(struct console *con, const char *s, unsigned int n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, mps2_early_putchar); +} + +static int __init mps2_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = mps2_early_write; + + return 0; +} + +OF_EARLYCON_DECLARE(mps2, "arm,mps2-uart", mps2_early_console_setup); + +#else +#define MPS2_SERIAL_CONSOLE NULL +#endif + +static struct uart_driver mps2_uart_driver = { + .driver_name = DRIVER_NAME, + .dev_name = SERIAL_NAME, + .nr = MPS2_MAX_PORTS, + .cons = MPS2_SERIAL_CONSOLE, +}; + +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int id; + + if (!np) + return NULL; + + id = of_alias_get_id(np, "serial"); + if (id < 0) + id = 0; + + if (WARN_ON(id >= MPS2_MAX_PORTS)) + return NULL; + + mps2_uart_ports[id].port.line = id; + return &mps2_uart_ports[id]; +} + +static int mps2_init_port(struct mps2_uart_port *mps_port, + struct platform_device *pdev) +{ + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mps_port->port.membase)) + return PTR_ERR(mps_port->port.membase); + + mps_port->port.mapbase = res->start; + mps_port->port.mapsize = resource_size(res); + + mps_port->rx_irq = platform_get_irq(pdev, 0); + mps_port->tx_irq = platform_get_irq(pdev, 1); + mps_port->port.irq = platform_get_irq(pdev, 2); + + mps_port->port.iotype = UPIO_MEM; + mps_port->port.flags = UPF_BOOT_AUTOCONF; + mps_port->port.fifosize = 1; + mps_port->port.ops = &mps2_uart_pops; + mps_port->port.dev = &pdev->dev; + + mps_port->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mps_port->clk)) + return PTR_ERR(mps_port->clk); + + ret = clk_prepare_enable(mps_port->clk); + if (ret) + return ret; + + mps_port->port.uartclk = clk_get_rate(mps_port->clk); + + clk_disable_unprepare(mps_port->clk); + + return ret; +} + +static int mps2_serial_probe(struct platform_device *pdev) +{ + struct mps2_uart_port *mps_port; + int ret; + + mps_port = mps2_of_get_port(pdev); + if (!mps_port) + return -ENODEV; + + ret = mps2_init_port(mps_port, pdev); + if (ret) + return ret; + + ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port); + if (ret) + return ret; + + platform_set_drvdata(pdev, mps_port); + + return 0; +} + +static int mps2_serial_remove(struct platform_device *pdev) +{ + struct mps2_uart_port *mps_port = platform_get_drvdata(pdev); + + uart_remove_one_port(&mps2_uart_driver, &mps_port->port); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mps2_match[] = { + { .compatible = "arm,mps2-uart", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mps2_match); +#endif + +static struct platform_driver mps2_serial_driver = { + .probe = mps2_serial_probe, + .remove = mps2_serial_remove, + + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(mps2_match), + }, +}; + +static int __init mps2_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&mps2_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&mps2_serial_driver); + if (ret) + uart_unregister_driver(&mps2_uart_driver); + + return ret; +} +module_init(mps2_uart_init); + +static void __exit mps2_uart_exit(void) +{ + platform_driver_unregister(&mps2_serial_driver); + uart_unregister_driver(&mps2_uart_driver); +} +module_exit(mps2_uart_exit); + +MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>"); +MODULE_DESCRIPTION("MPS2 UART driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 96d3ce8..b7d80bd 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -861,37 +861,72 @@ struct msm_baud_map { }; static const struct msm_baud_map * -msm_find_best_baud(struct uart_port *port, unsigned int baud) +msm_find_best_baud(struct uart_port *port, unsigned int baud, + unsigned long *rate) { - unsigned int i, divisor; - const struct msm_baud_map *entry; + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int divisor, result; + unsigned long target, old, best_rate = 0, diff, best_diff = ULONG_MAX; + const struct msm_baud_map *entry, *end, *best; static const struct msm_baud_map table[] = { - { 1536, 0x00, 1 }, - { 768, 0x11, 1 }, - { 384, 0x22, 1 }, - { 192, 0x33, 1 }, - { 96, 0x44, 1 }, - { 48, 0x55, 1 }, - { 32, 0x66, 1 }, - { 24, 0x77, 1 }, - { 16, 0x88, 1 }, - { 12, 0x99, 6 }, - { 8, 0xaa, 6 }, - { 6, 0xbb, 6 }, - { 4, 0xcc, 6 }, - { 3, 0xdd, 8 }, - { 2, 0xee, 16 }, { 1, 0xff, 31 }, - { 0, 0xff, 31 }, + { 2, 0xee, 16 }, + { 3, 0xdd, 8 }, + { 4, 0xcc, 6 }, + { 6, 0xbb, 6 }, + { 8, 0xaa, 6 }, + { 12, 0x99, 6 }, + { 16, 0x88, 1 }, + { 24, 0x77, 1 }, + { 32, 0x66, 1 }, + { 48, 0x55, 1 }, + { 96, 0x44, 1 }, + { 192, 0x33, 1 }, + { 384, 0x22, 1 }, + { 768, 0x11, 1 }, + { 1536, 0x00, 1 }, }; - divisor = uart_get_divisor(port, baud); + best = table; /* Default to smallest divider */ + target = clk_round_rate(msm_port->clk, 16 * baud); + divisor = DIV_ROUND_CLOSEST(target, 16 * baud); + + end = table + ARRAY_SIZE(table); + entry = table; + while (entry < end) { + if (entry->divisor <= divisor) { + result = target / entry->divisor / 16; + diff = abs(result - baud); + + /* Keep track of best entry */ + if (diff < best_diff) { + best_diff = diff; + best = entry; + best_rate = target; + } - for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++) - if (entry->divisor <= divisor) - break; + if (result == baud) + break; + } else if (entry->divisor > divisor) { + old = target; + target = clk_round_rate(msm_port->clk, old + 1); + /* + * The rate didn't get any faster so we can't do + * better at dividing it down + */ + if (target == old) + break; - return entry; /* Default to smallest divider */ + /* Start the divisor search over at this new rate */ + entry = table; + divisor = DIV_ROUND_CLOSEST(target, 16 * baud); + continue; + } + entry++; + } + + *rate = best_rate; + return best; } static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, @@ -900,22 +935,20 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, unsigned int rxstale, watermark, mask; struct msm_port *msm_port = UART_TO_MSM(port); const struct msm_baud_map *entry; - unsigned long flags; - - entry = msm_find_best_baud(port, baud); - - msm_write(port, entry->code, UART_CSR); - - if (baud > 460800) - port->uartclk = baud * 16; + unsigned long flags, rate; flags = *saved_flags; spin_unlock_irqrestore(&port->lock, flags); - clk_set_rate(msm_port->clk, port->uartclk); + entry = msm_find_best_baud(port, baud, &rate); + clk_set_rate(msm_port->clk, rate); + baud = rate / 16 / entry->divisor; spin_lock_irqsave(&port->lock, flags); *saved_flags = flags; + port->uartclk = rate; + + msm_write(port, entry->code, UART_CSR); /* RX stale watermark */ rxstale = entry->rxstale; @@ -1577,8 +1610,6 @@ static int msm_serial_probe(struct platform_device *pdev) msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(msm_port->pclk)) return PTR_ERR(msm_port->pclk); - - clk_set_rate(msm_port->clk, 1843200); } port->uartclk = clk_get_rate(msm_port->clk); diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 0ff2781..ce362bd 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -1,5 +1,7 @@ /* * *************************************************************************** +* Marvell Armada-3700 Serial Driver +* Author: Wilson Ding <dingwei@marvell.com> * Copyright (C) 2015 Marvell International Ltd. * *************************************************************************** * This program is free software: you can redistribute it and/or modify it @@ -23,7 +25,6 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/iopoll.h> -#include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> @@ -594,30 +595,18 @@ static int mvebu_uart_probe(struct platform_device *pdev) return 0; } -static int mvebu_uart_remove(struct platform_device *pdev) -{ - struct mvebu_uart_data *data = platform_get_drvdata(pdev); - - uart_remove_one_port(&mvebu_uart_driver, data->port); - data->port->private_data = NULL; - data->port->mapbase = 0; - return 0; -} - /* Match table for of_platform binding */ static const struct of_device_id mvebu_uart_of_match[] = { { .compatible = "marvell,armada-3700-uart", }, {} }; -MODULE_DEVICE_TABLE(of, mvebu_uart_of_match); static struct platform_driver mvebu_uart_platform_driver = { .probe = mvebu_uart_probe, - .remove = mvebu_uart_remove, .driver = { - .owner = THIS_MODULE, .name = "mvebu-uart", .of_match_table = of_match_ptr(mvebu_uart_of_match), + .suppress_bind_attrs = true, }, }; @@ -635,16 +624,4 @@ static int __init mvebu_uart_init(void) return ret; } - -static void __exit mvebu_uart_exit(void) -{ - platform_driver_unregister(&mvebu_uart_platform_driver); - uart_unregister_driver(&mvebu_uart_driver); -} - arch_initcall(mvebu_uart_init); -module_exit(mvebu_uart_exit); - -MODULE_AUTHOR("Wilson Ding <dingwei@marvell.com>"); -MODULE_DESCRIPTION("Marvell Armada-3700 Serial Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index cd0414b..eb54e5c 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1,17 +1,18 @@ /* - * Freescale STMP37XX/STMP378X Application UART driver + * Application UART driver for: + * Freescale STMP37XX/STMP378X + * Alphascale ASM9260 * * Author: dmitry pervushin <dimka@embeddedalley.com> * + * Copyright 2014 Oleksij Rempel <linux@rempel-privat.de> + * Provide Alphascale ASM9260 support. * Copyright 2008-2010 Freescale Semiconductor, Inc. * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html */ #if defined(CONFIG_SERIAL_MXS_AUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -51,30 +52,16 @@ #define MXS_AUART_PORTS 5 #define MXS_AUART_FIFO_SIZE 16 +#define SET_REG 0x4 +#define CLR_REG 0x8 +#define TOG_REG 0xc + #define AUART_CTRL0 0x00000000 -#define AUART_CTRL0_SET 0x00000004 -#define AUART_CTRL0_CLR 0x00000008 -#define AUART_CTRL0_TOG 0x0000000c #define AUART_CTRL1 0x00000010 -#define AUART_CTRL1_SET 0x00000014 -#define AUART_CTRL1_CLR 0x00000018 -#define AUART_CTRL1_TOG 0x0000001c #define AUART_CTRL2 0x00000020 -#define AUART_CTRL2_SET 0x00000024 -#define AUART_CTRL2_CLR 0x00000028 -#define AUART_CTRL2_TOG 0x0000002c #define AUART_LINECTRL 0x00000030 -#define AUART_LINECTRL_SET 0x00000034 -#define AUART_LINECTRL_CLR 0x00000038 -#define AUART_LINECTRL_TOG 0x0000003c #define AUART_LINECTRL2 0x00000040 -#define AUART_LINECTRL2_SET 0x00000044 -#define AUART_LINECTRL2_CLR 0x00000048 -#define AUART_LINECTRL2_TOG 0x0000004c #define AUART_INTR 0x00000050 -#define AUART_INTR_SET 0x00000054 -#define AUART_INTR_CLR 0x00000058 -#define AUART_INTR_TOG 0x0000005c #define AUART_DATA 0x00000060 #define AUART_STAT 0x00000070 #define AUART_DEBUG 0x00000080 @@ -136,11 +123,301 @@ #define AUART_STAT_FERR (1 << 16) #define AUART_STAT_RXCOUNT_MASK 0xffff +/* + * Start of Alphascale asm9260 defines + * This list contains only differences of existing bits + * between imx2x and asm9260 + */ +#define ASM9260_HW_CTRL0 0x0000 +/* + * RW. Tell the UART to execute the RX DMA Command. The + * UART will clear this bit at the end of receive execution. + */ +#define ASM9260_BM_CTRL0_RXDMA_RUN BIT(28) +/* RW. 0 use FIFO for status register; 1 use DMA */ +#define ASM9260_BM_CTRL0_RXTO_SOURCE_STATUS BIT(25) +/* + * RW. RX TIMEOUT Enable. Valid for FIFO and DMA. + * Warning: If this bit is set to 0, the RX timeout will not affect receive DMA + * operation. If this bit is set to 1, a receive timeout will cause the receive + * DMA logic to terminate by filling the remaining DMA bytes with garbage data. + */ +#define ASM9260_BM_CTRL0_RXTO_ENABLE BIT(24) +/* + * RW. Receive Timeout Counter Value: number of 8-bit-time to wait before + * asserting timeout on the RX input. If the RXFIFO is not empty and the RX + * input is idle, then the watchdog counter will decrement each bit-time. Note + * 7-bit-time is added to the programmed value, so a value of zero will set + * the counter to 7-bit-time, a value of 0x1 gives 15-bit-time and so on. Also + * note that the counter is reloaded at the end of each frame, so if the frame + * is 10 bits long and the timeout counter value is zero, then timeout will + * occur (when FIFO is not empty) even if the RX input is not idle. The default + * value is 0x3 (31 bit-time). + */ +#define ASM9260_BM_CTRL0_RXTO_MASK (0xff << 16) +/* TIMEOUT = (100*7+1)*(1/BAUD) */ +#define ASM9260_BM_CTRL0_DEFAULT_RXTIMEOUT (20 << 16) + +/* TX ctrl register */ +#define ASM9260_HW_CTRL1 0x0010 +/* + * RW. Tell the UART to execute the TX DMA Command. The + * UART will clear this bit at the end of transmit execution. + */ +#define ASM9260_BM_CTRL1_TXDMA_RUN BIT(28) + +#define ASM9260_HW_CTRL2 0x0020 +/* + * RW. Receive Interrupt FIFO Level Select. + * The trigger points for the receive interrupt are as follows: + * ONE_EIGHTHS = 0x0 Trigger on FIFO full to at least 2 of 16 entries. + * ONE_QUARTER = 0x1 Trigger on FIFO full to at least 4 of 16 entries. + * ONE_HALF = 0x2 Trigger on FIFO full to at least 8 of 16 entries. + * THREE_QUARTERS = 0x3 Trigger on FIFO full to at least 12 of 16 entries. + * SEVEN_EIGHTHS = 0x4 Trigger on FIFO full to at least 14 of 16 entries. + */ +#define ASM9260_BM_CTRL2_RXIFLSEL (7 << 20) +#define ASM9260_BM_CTRL2_DEFAULT_RXIFLSEL (3 << 20) +/* RW. Same as RXIFLSEL */ +#define ASM9260_BM_CTRL2_TXIFLSEL (7 << 16) +#define ASM9260_BM_CTRL2_DEFAULT_TXIFLSEL (2 << 16) +/* RW. Set DTR. When this bit is 1, the output is 0. */ +#define ASM9260_BM_CTRL2_DTR BIT(10) +/* RW. Loop Back Enable */ +#define ASM9260_BM_CTRL2_LBE BIT(7) +#define ASM9260_BM_CTRL2_PORT_ENABLE BIT(0) + +#define ASM9260_HW_LINECTRL 0x0030 +/* + * RW. Stick Parity Select. When bits 1, 2, and 7 of this register are set, the + * parity bit is transmitted and checked as a 0. When bits 1 and 7 are set, + * and bit 2 is 0, the parity bit is transmitted and checked as a 1. When this + * bit is cleared stick parity is disabled. + */ +#define ASM9260_BM_LCTRL_SPS BIT(7) +/* RW. Word length */ +#define ASM9260_BM_LCTRL_WLEN (3 << 5) +#define ASM9260_BM_LCTRL_CHRL_5 (0 << 5) +#define ASM9260_BM_LCTRL_CHRL_6 (1 << 5) +#define ASM9260_BM_LCTRL_CHRL_7 (2 << 5) +#define ASM9260_BM_LCTRL_CHRL_8 (3 << 5) + +/* + * Interrupt register. + * contains the interrupt enables and the interrupt status bits + */ +#define ASM9260_HW_INTR 0x0040 +/* Tx FIFO EMPTY Raw Interrupt enable */ +#define ASM9260_BM_INTR_TFEIEN BIT(27) +/* Overrun Error Interrupt Enable. */ +#define ASM9260_BM_INTR_OEIEN BIT(26) +/* Break Error Interrupt Enable. */ +#define ASM9260_BM_INTR_BEIEN BIT(25) +/* Parity Error Interrupt Enable. */ +#define ASM9260_BM_INTR_PEIEN BIT(24) +/* Framing Error Interrupt Enable. */ +#define ASM9260_BM_INTR_FEIEN BIT(23) + +/* nUARTDSR Modem Interrupt Enable. */ +#define ASM9260_BM_INTR_DSRMIEN BIT(19) +/* nUARTDCD Modem Interrupt Enable. */ +#define ASM9260_BM_INTR_DCDMIEN BIT(18) +/* nUARTRI Modem Interrupt Enable. */ +#define ASM9260_BM_INTR_RIMIEN BIT(16) +/* Auto-Boud Timeout */ +#define ASM9260_BM_INTR_ABTO BIT(13) +#define ASM9260_BM_INTR_ABEO BIT(12) +/* Tx FIFO EMPTY Raw Interrupt state */ +#define ASM9260_BM_INTR_TFEIS BIT(11) +/* Overrun Error */ +#define ASM9260_BM_INTR_OEIS BIT(10) +/* Break Error */ +#define ASM9260_BM_INTR_BEIS BIT(9) +/* Parity Error */ +#define ASM9260_BM_INTR_PEIS BIT(8) +/* Framing Error */ +#define ASM9260_BM_INTR_FEIS BIT(7) +#define ASM9260_BM_INTR_DSRMIS BIT(3) +#define ASM9260_BM_INTR_DCDMIS BIT(2) +#define ASM9260_BM_INTR_RIMIS BIT(0) + +/* + * RW. In DMA mode, up to 4 Received/Transmit characters can be accessed at a + * time. In PIO mode, only one character can be accessed at a time. The status + * register contains the receive data flags and valid bits. + */ +#define ASM9260_HW_DATA 0x0050 + +#define ASM9260_HW_STAT 0x0060 +/* RO. If 1, UARTAPP is present in this product. */ +#define ASM9260_BM_STAT_PRESENT BIT(31) +/* RO. If 1, HISPEED is present in this product. */ +#define ASM9260_BM_STAT_HISPEED BIT(30) +/* RO. Receive FIFO Full. */ +#define ASM9260_BM_STAT_RXFULL BIT(26) + +/* RO. The UART Debug Register contains the state of the DMA signals. */ +#define ASM9260_HW_DEBUG 0x0070 +/* DMA Command Run Status */ +#define ASM9260_BM_DEBUG_TXDMARUN BIT(5) +#define ASM9260_BM_DEBUG_RXDMARUN BIT(4) +/* DMA Command End Status */ +#define ASM9260_BM_DEBUG_TXCMDEND BIT(3) +#define ASM9260_BM_DEBUG_RXCMDEND BIT(2) +/* DMA Request Status */ +#define ASM9260_BM_DEBUG_TXDMARQ BIT(1) +#define ASM9260_BM_DEBUG_RXDMARQ BIT(0) + +#define ASM9260_HW_ILPR 0x0080 + +#define ASM9260_HW_RS485CTRL 0x0090 +/* + * RW. This bit reverses the polarity of the direction control signal on the RTS + * (or DTR) pin. + * If 0, The direction control pin will be driven to logic ‘0’ when the + * transmitter has data to be sent. It will be driven to logic ‘1’ after the + * last bit of data has been transmitted. + */ +#define ASM9260_BM_RS485CTRL_ONIV BIT(5) +/* RW. Enable Auto Direction Control. */ +#define ASM9260_BM_RS485CTRL_DIR_CTRL BIT(4) +/* + * RW. If 0 and DIR_CTRL = 1, pin RTS is used for direction control. + * If 1 and DIR_CTRL = 1, pin DTR is used for direction control. + */ +#define ASM9260_BM_RS485CTRL_PINSEL BIT(3) +/* RW. Enable Auto Address Detect (AAD). */ +#define ASM9260_BM_RS485CTRL_AADEN BIT(2) +/* RW. Disable receiver. */ +#define ASM9260_BM_RS485CTRL_RXDIS BIT(1) +/* RW. Enable RS-485/EIA-485 Normal Multidrop Mode (NMM) */ +#define ASM9260_BM_RS485CTRL_RS485EN BIT(0) + +#define ASM9260_HW_RS485ADRMATCH 0x00a0 +/* Contains the address match value. */ +#define ASM9260_BM_RS485ADRMATCH_MASK (0xff << 0) + +#define ASM9260_HW_RS485DLY 0x00b0 +/* + * RW. Contains the direction control (RTS or DTR) delay value. This delay time + * is in periods of the baud clock. + */ +#define ASM9260_BM_RS485DLY_MASK (0xff << 0) + +#define ASM9260_HW_AUTOBAUD 0x00c0 +/* WO. Auto-baud time-out interrupt clear bit. */ +#define ASM9260_BM_AUTOBAUD_TO_INT_CLR BIT(9) +/* WO. End of auto-baud interrupt clear bit. */ +#define ASM9260_BM_AUTOBAUD_EO_INT_CLR BIT(8) +/* Restart in case of timeout (counter restarts at next UART Rx falling edge) */ +#define ASM9260_BM_AUTOBAUD_AUTORESTART BIT(2) +/* Auto-baud mode select bit. 0 - Mode 0, 1 - Mode 1. */ +#define ASM9260_BM_AUTOBAUD_MODE BIT(1) +/* + * Auto-baud start (auto-baud is running). Auto-baud run bit. This bit is + * automatically cleared after auto-baud completion. + */ +#define ASM9260_BM_AUTOBAUD_START BIT(0) + +#define ASM9260_HW_CTRL3 0x00d0 +#define ASM9260_BM_CTRL3_OUTCLK_DIV_MASK (0xffff << 16) +/* + * RW. Provide clk over OUTCLK pin. In case of asm9260 it can be configured on + * pins 137 and 144. + */ +#define ASM9260_BM_CTRL3_MASTERMODE BIT(6) +/* RW. Baud Rate Mode: 1 - Enable sync mode. 0 - async mode. */ +#define ASM9260_BM_CTRL3_SYNCMODE BIT(4) +/* RW. 1 - MSB bit send frist; 0 - LSB bit frist. */ +#define ASM9260_BM_CTRL3_MSBF BIT(2) +/* RW. 1 - sample rate = 8 x Baudrate; 0 - sample rate = 16 x Baudrate. */ +#define ASM9260_BM_CTRL3_BAUD8 BIT(1) +/* RW. 1 - Set word length to 9bit. 0 - use ASM9260_BM_LCTRL_WLEN */ +#define ASM9260_BM_CTRL3_9BIT BIT(0) + +#define ASM9260_HW_ISO7816_CTRL 0x00e0 +/* RW. Enable High Speed mode. */ +#define ASM9260_BM_ISO7816CTRL_HS BIT(12) +/* Disable Successive Receive NACK */ +#define ASM9260_BM_ISO7816CTRL_DS_NACK BIT(8) +#define ASM9260_BM_ISO7816CTRL_MAX_ITER_MASK (0xff << 4) +/* Receive NACK Inhibit */ +#define ASM9260_BM_ISO7816CTRL_INACK BIT(3) +#define ASM9260_BM_ISO7816CTRL_NEG_DATA BIT(2) +/* RW. 1 - ISO7816 mode; 0 - USART mode */ +#define ASM9260_BM_ISO7816CTRL_ENABLE BIT(0) + +#define ASM9260_HW_ISO7816_ERRCNT 0x00f0 +/* Parity error counter. Will be cleared after reading */ +#define ASM9260_BM_ISO7816_NB_ERRORS_MASK (0xff << 0) + +#define ASM9260_HW_ISO7816_STATUS 0x0100 +/* Max number of Repetitions Reached */ +#define ASM9260_BM_ISO7816_STAT_ITERATION BIT(0) + +/* End of Alphascale asm9260 defines */ + static struct uart_driver auart_driver; enum mxs_auart_type { IMX23_AUART, IMX28_AUART, + ASM9260_AUART, +}; + +struct vendor_data { + const u16 *reg_offset; +}; + +enum { + REG_CTRL0, + REG_CTRL1, + REG_CTRL2, + REG_LINECTRL, + REG_LINECTRL2, + REG_INTR, + REG_DATA, + REG_STAT, + REG_DEBUG, + REG_VERSION, + REG_AUTOBAUD, + + /* The size of the array - must be last */ + REG_ARRAY_SIZE, +}; + +static const u16 mxs_asm9260_offsets[REG_ARRAY_SIZE] = { + [REG_CTRL0] = ASM9260_HW_CTRL0, + [REG_CTRL1] = ASM9260_HW_CTRL1, + [REG_CTRL2] = ASM9260_HW_CTRL2, + [REG_LINECTRL] = ASM9260_HW_LINECTRL, + [REG_INTR] = ASM9260_HW_INTR, + [REG_DATA] = ASM9260_HW_DATA, + [REG_STAT] = ASM9260_HW_STAT, + [REG_DEBUG] = ASM9260_HW_DEBUG, + [REG_AUTOBAUD] = ASM9260_HW_AUTOBAUD, +}; + +static const u16 mxs_stmp37xx_offsets[REG_ARRAY_SIZE] = { + [REG_CTRL0] = AUART_CTRL0, + [REG_CTRL1] = AUART_CTRL1, + [REG_CTRL2] = AUART_CTRL2, + [REG_LINECTRL] = AUART_LINECTRL, + [REG_LINECTRL2] = AUART_LINECTRL2, + [REG_INTR] = AUART_INTR, + [REG_DATA] = AUART_DATA, + [REG_STAT] = AUART_STAT, + [REG_DEBUG] = AUART_DEBUG, + [REG_VERSION] = AUART_VERSION, + [REG_AUTOBAUD] = AUART_AUTOBAUD, +}; + +static const struct vendor_data vendor_alphascale_asm9260 = { + .reg_offset = mxs_asm9260_offsets, +}; + +static const struct vendor_data vendor_freescale_stmp37xx = { + .reg_offset = mxs_stmp37xx_offsets, }; struct mxs_auart_port { @@ -153,8 +430,10 @@ struct mxs_auart_port { unsigned long flags; unsigned int mctrl_prev; enum mxs_auart_type devtype; + const struct vendor_data *vendor; struct clk *clk; + struct clk *clk_ahb; struct device *dev; /* for DMA */ @@ -174,6 +453,7 @@ struct mxs_auart_port { static const struct platform_device_id mxs_auart_devtype[] = { { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART }, { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART }, + { .name = "as-auart-asm9260", .driver_data = ASM9260_AUART }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mxs_auart_devtype); @@ -185,6 +465,9 @@ static const struct of_device_id mxs_auart_dt_ids[] = { }, { .compatible = "fsl,imx23-auart", .data = &mxs_auart_devtype[IMX23_AUART] + }, { + .compatible = "alphascale,asm9260-auart", + .data = &mxs_auart_devtype[ASM9260_AUART] }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); @@ -194,11 +477,54 @@ static inline int is_imx28_auart(struct mxs_auart_port *s) return s->devtype == IMX28_AUART; } +static inline int is_asm9260_auart(struct mxs_auart_port *s) +{ + return s->devtype == ASM9260_AUART; +} + static inline bool auart_dma_enabled(struct mxs_auart_port *s) { return s->flags & MXS_AUART_DMA_ENABLED; } +static unsigned int mxs_reg_to_offset(const struct mxs_auart_port *uap, + unsigned int reg) +{ + return uap->vendor->reg_offset[reg]; +} + +static unsigned int mxs_read(const struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + return readl_relaxed(addr); +} + +static void mxs_write(unsigned int val, struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + writel_relaxed(val, addr); +} + +static void mxs_set(unsigned int val, struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + writel_relaxed(val, addr + SET_REG); +} + +static void mxs_clr(unsigned int val, struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + writel_relaxed(val, addr + CLR_REG); +} + static void mxs_auart_stop_tx(struct uart_port *u); #define to_auart_port(u) container_of(u, struct mxs_auart_port, port) @@ -295,19 +621,16 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) } - while (!(readl(s->port.membase + AUART_STAT) & - AUART_STAT_TXFF)) { + while (!(mxs_read(s, REG_STAT) & AUART_STAT_TXFF)) { if (s->port.x_char) { s->port.icount.tx++; - writel(s->port.x_char, - s->port.membase + AUART_DATA); + mxs_write(s->port.x_char, s, REG_DATA); s->port.x_char = 0; continue; } if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { s->port.icount.tx++; - writel(xmit->buf[xmit->tail], - s->port.membase + AUART_DATA); + mxs_write(xmit->buf[xmit->tail], s, REG_DATA); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); } else break; @@ -316,11 +639,9 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) uart_write_wakeup(&s->port); if (uart_circ_empty(&(s->port.state->xmit))) - writel(AUART_INTR_TXIEN, - s->port.membase + AUART_INTR_CLR); + mxs_clr(AUART_INTR_TXIEN, s, REG_INTR); else - writel(AUART_INTR_TXIEN, - s->port.membase + AUART_INTR_SET); + mxs_set(AUART_INTR_TXIEN, s, REG_INTR); if (uart_tx_stopped(&s->port)) mxs_auart_stop_tx(&s->port); @@ -332,8 +653,8 @@ static void mxs_auart_rx_char(struct mxs_auart_port *s) u32 stat; u8 c; - c = readl(s->port.membase + AUART_DATA); - stat = readl(s->port.membase + AUART_STAT); + c = mxs_read(s, REG_DATA); + stat = mxs_read(s, REG_STAT); flag = TTY_NORMAL; s->port.icount.rx++; @@ -368,7 +689,7 @@ static void mxs_auart_rx_char(struct mxs_auart_port *s) uart_insert_char(&s->port, stat, AUART_STAT_OERR, c, flag); out: - writel(stat, s->port.membase + AUART_STAT); + mxs_write(stat, s, REG_STAT); } static void mxs_auart_rx_chars(struct mxs_auart_port *s) @@ -376,13 +697,13 @@ static void mxs_auart_rx_chars(struct mxs_auart_port *s) u32 stat = 0; for (;;) { - stat = readl(s->port.membase + AUART_STAT); + stat = mxs_read(s, REG_STAT); if (stat & AUART_STAT_RXFE) break; mxs_auart_rx_char(s); } - writel(stat, s->port.membase + AUART_STAT); + mxs_write(stat, s, REG_STAT); tty_flip_buffer_push(&s->port.state->port); } @@ -418,7 +739,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) { struct mxs_auart_port *s = to_auart_port(u); - u32 ctrl = readl(u->membase + AUART_CTRL2); + u32 ctrl = mxs_read(s, REG_CTRL2); ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); if (mctrl & TIOCM_RTS) { @@ -428,7 +749,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) ctrl |= AUART_CTRL2_RTS; } - writel(ctrl, u->membase + AUART_CTRL2); + mxs_write(ctrl, s, REG_CTRL2); mctrl_gpio_set(s->gpios, mctrl); } @@ -459,7 +780,7 @@ static u32 mxs_auart_modem_status(struct mxs_auart_port *s, u32 mctrl) static u32 mxs_auart_get_mctrl(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); - u32 stat = readl(u->membase + AUART_STAT); + u32 stat = mxs_read(s, REG_STAT); u32 mctrl = 0; if (stat & AUART_STAT_CTS) @@ -536,14 +857,14 @@ static void dma_rx_callback(void *arg) dma_unmap_sg(s->dev, &s->rx_sgl, 1, DMA_FROM_DEVICE); - stat = readl(s->port.membase + AUART_STAT); + stat = mxs_read(s, REG_STAT); stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR | AUART_STAT_PERR | AUART_STAT_FERR); count = stat & AUART_STAT_RXCOUNT_MASK; tty_insert_flip_string(port, s->rx_dma_buf, count); - writel(stat, s->port.membase + AUART_STAT); + mxs_write(stat, s, REG_STAT); tty_flip_buffer_push(port); /* start the next DMA for RX. */ @@ -606,8 +927,8 @@ static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s) static void mxs_auart_dma_exit(struct mxs_auart_port *s) { - writel(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, - s->port.membase + AUART_CTRL2_CLR); + mxs_clr(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, + s, REG_CTRL2); mxs_auart_dma_exit_channel(s); s->flags &= ~MXS_AUART_DMA_ENABLED; @@ -666,7 +987,7 @@ static void mxs_auart_settermios(struct uart_port *u, cflag = termios->c_cflag; ctrl = AUART_LINECTRL_FEN; - ctrl2 = readl(u->membase + AUART_CTRL2); + ctrl2 = mxs_read(s, REG_CTRL2); /* byte size */ switch (cflag & CSIZE) { @@ -754,15 +1075,24 @@ static void mxs_auart_settermios(struct uart_port *u, } /* set baud rate */ - baud_min = DIV_ROUND_UP(u->uartclk * 32, AUART_LINECTRL_BAUD_DIV_MAX); - baud_max = u->uartclk * 32 / AUART_LINECTRL_BAUD_DIV_MIN; - baud = uart_get_baud_rate(u, termios, old, baud_min, baud_max); - div = u->uartclk * 32 / baud; + if (is_asm9260_auart(s)) { + baud = uart_get_baud_rate(u, termios, old, + u->uartclk * 4 / 0x3FFFFF, + u->uartclk / 16); + div = u->uartclk * 4 / baud; + } else { + baud_min = DIV_ROUND_UP(u->uartclk * 32, + AUART_LINECTRL_BAUD_DIV_MAX); + baud_max = u->uartclk * 32 / AUART_LINECTRL_BAUD_DIV_MIN; + baud = uart_get_baud_rate(u, termios, old, baud_min, baud_max); + div = u->uartclk * 32 / baud; + } + ctrl |= AUART_LINECTRL_BAUD_DIVFRAC(div & 0x3F); ctrl |= AUART_LINECTRL_BAUD_DIVINT(div >> 6); + mxs_write(ctrl, s, REG_LINECTRL); - writel(ctrl, u->membase + AUART_LINECTRL); - writel(ctrl2, u->membase + AUART_CTRL2); + mxs_write(ctrl2, s, REG_CTRL2); uart_update_timeout(u, termios->c_cflag, baud); @@ -771,8 +1101,8 @@ static void mxs_auart_settermios(struct uart_port *u, !test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) { if (!mxs_auart_dma_prep_rx(s)) { /* Disable the normal RX interrupt. */ - writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN, - u->membase + AUART_INTR_CLR); + mxs_clr(AUART_INTR_RXIEN | AUART_INTR_RTIEN, + s, REG_INTR); } else { mxs_auart_dma_exit(s); dev_err(s->dev, "We can not start up the DMA.\n"); @@ -802,16 +1132,13 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) u32 istat; struct mxs_auart_port *s = context; u32 mctrl_temp = s->mctrl_prev; - u32 stat = readl(s->port.membase + AUART_STAT); + u32 stat = mxs_read(s, REG_STAT); - istat = readl(s->port.membase + AUART_INTR); + istat = mxs_read(s, REG_INTR); /* ack irq */ - writel(istat & (AUART_INTR_RTIS - | AUART_INTR_TXIS - | AUART_INTR_RXIS - | AUART_INTR_CTSMIS), - s->port.membase + AUART_INTR_CLR); + mxs_clr(istat & (AUART_INTR_RTIS | AUART_INTR_TXIS | AUART_INTR_RXIS + | AUART_INTR_CTSMIS), s, REG_INTR); /* * Dealing with GPIO interrupt @@ -827,8 +1154,7 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) if (CTS_AT_AUART() && s->ms_irq_enabled) uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); - writel(AUART_INTR_CTSMIS, - s->port.membase + AUART_INTR_CLR); + mxs_clr(AUART_INTR_CTSMIS, s, REG_INTR); istat &= ~AUART_INTR_CTSMIS; } @@ -846,44 +1172,44 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) return IRQ_HANDLED; } -static void mxs_auart_reset_deassert(struct uart_port *u) +static void mxs_auart_reset_deassert(struct mxs_auart_port *s) { int i; unsigned int reg; - writel(AUART_CTRL0_SFTRST, u->membase + AUART_CTRL0_CLR); + mxs_clr(AUART_CTRL0_SFTRST, s, REG_CTRL0); for (i = 0; i < 10000; i++) { - reg = readl(u->membase + AUART_CTRL0); + reg = mxs_read(s, REG_CTRL0); if (!(reg & AUART_CTRL0_SFTRST)) break; udelay(3); } - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); } -static void mxs_auart_reset_assert(struct uart_port *u) +static void mxs_auart_reset_assert(struct mxs_auart_port *s) { int i; u32 reg; - reg = readl(u->membase + AUART_CTRL0); + reg = mxs_read(s, REG_CTRL0); /* if already in reset state, keep it untouched */ if (reg & AUART_CTRL0_SFTRST) return; - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); - writel(AUART_CTRL0_SFTRST, u->membase + AUART_CTRL0_SET); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); + mxs_set(AUART_CTRL0_SFTRST, s, REG_CTRL0); for (i = 0; i < 1000; i++) { - reg = readl(u->membase + AUART_CTRL0); + reg = mxs_read(s, REG_CTRL0); /* reset is finished when the clock is gated */ if (reg & AUART_CTRL0_CLKGATE) return; udelay(10); } - dev_err(u->dev, "Failed to reset the unit."); + dev_err(s->dev, "Failed to reset the unit."); } static int mxs_auart_startup(struct uart_port *u) @@ -896,17 +1222,17 @@ static int mxs_auart_startup(struct uart_port *u) return ret; if (uart_console(u)) { - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); } else { /* reset the unit to a well known state */ - mxs_auart_reset_assert(u); - mxs_auart_reset_deassert(u); + mxs_auart_reset_assert(s); + mxs_auart_reset_deassert(s); } - writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_SET); + mxs_set(AUART_CTRL2_UARTEN, s, REG_CTRL2); - writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, - u->membase + AUART_INTR); + mxs_write(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, + s, REG_INTR); /* Reset FIFO size (it could have changed if DMA was enabled) */ u->fifosize = MXS_AUART_FIFO_SIZE; @@ -915,7 +1241,7 @@ static int mxs_auart_startup(struct uart_port *u) * Enable fifo so all four bytes of a DMA word are written to * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) */ - writel(AUART_LINECTRL_FEN, u->membase + AUART_LINECTRL_SET); + mxs_set(AUART_LINECTRL_FEN, s, REG_LINECTRL); /* get initial status of modem lines */ mctrl_gpio_get(s->gpios, &s->mctrl_prev); @@ -934,12 +1260,13 @@ static void mxs_auart_shutdown(struct uart_port *u) mxs_auart_dma_exit(s); if (uart_console(u)) { - writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); - writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, - u->membase + AUART_INTR_CLR); - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET); + mxs_clr(AUART_CTRL2_UARTEN, s, REG_CTRL2); + + mxs_clr(AUART_INTR_RXIEN | AUART_INTR_RTIEN | + AUART_INTR_CTSMIEN, s, REG_INTR); + mxs_set(AUART_CTRL0_CLKGATE, s, REG_CTRL0); } else { - mxs_auart_reset_assert(u); + mxs_auart_reset_assert(s); } clk_disable_unprepare(s->clk); @@ -947,7 +1274,9 @@ static void mxs_auart_shutdown(struct uart_port *u) static unsigned int mxs_auart_tx_empty(struct uart_port *u) { - if ((readl(u->membase + AUART_STAT) & + struct mxs_auart_port *s = to_auart_port(u); + + if ((mxs_read(s, REG_STAT) & (AUART_STAT_TXFE | AUART_STAT_BUSY)) == AUART_STAT_TXFE) return TIOCSER_TEMT; @@ -959,29 +1288,33 @@ static void mxs_auart_start_tx(struct uart_port *u) struct mxs_auart_port *s = to_auart_port(u); /* enable transmitter */ - writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_SET); + mxs_set(AUART_CTRL2_TXE, s, REG_CTRL2); mxs_auart_tx_chars(s); } static void mxs_auart_stop_tx(struct uart_port *u) { - writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_CLR); + struct mxs_auart_port *s = to_auart_port(u); + + mxs_clr(AUART_CTRL2_TXE, s, REG_CTRL2); } static void mxs_auart_stop_rx(struct uart_port *u) { - writel(AUART_CTRL2_RXE, u->membase + AUART_CTRL2_CLR); + struct mxs_auart_port *s = to_auart_port(u); + + mxs_clr(AUART_CTRL2_RXE, s, REG_CTRL2); } static void mxs_auart_break_ctl(struct uart_port *u, int ctl) { + struct mxs_auart_port *s = to_auart_port(u); + if (ctl) - writel(AUART_LINECTRL_BRK, - u->membase + AUART_LINECTRL_SET); + mxs_set(AUART_LINECTRL_BRK, s, REG_LINECTRL); else - writel(AUART_LINECTRL_BRK, - u->membase + AUART_LINECTRL_CLR); + mxs_clr(AUART_LINECTRL_BRK, s, REG_LINECTRL); } static struct uart_ops mxs_auart_ops = { @@ -1009,15 +1342,16 @@ static struct mxs_auart_port *auart_port[MXS_AUART_PORTS]; #ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE static void mxs_auart_console_putchar(struct uart_port *port, int ch) { + struct mxs_auart_port *s = to_auart_port(port); unsigned int to = 1000; - while (readl(port->membase + AUART_STAT) & AUART_STAT_TXFF) { + while (mxs_read(s, REG_STAT) & AUART_STAT_TXFF) { if (!to--) break; udelay(1); } - writel(ch, port->membase + AUART_DATA); + mxs_write(ch, s, REG_DATA); } static void @@ -1037,18 +1371,16 @@ auart_console_write(struct console *co, const char *str, unsigned int count) clk_enable(s->clk); /* First save the CR then disable the interrupts */ - old_ctrl2 = readl(port->membase + AUART_CTRL2); - old_ctrl0 = readl(port->membase + AUART_CTRL0); + old_ctrl2 = mxs_read(s, REG_CTRL2); + old_ctrl0 = mxs_read(s, REG_CTRL0); - writel(AUART_CTRL0_CLKGATE, - port->membase + AUART_CTRL0_CLR); - writel(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE, - port->membase + AUART_CTRL2_SET); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); + mxs_set(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE, s, REG_CTRL2); uart_console_write(port, str, count, mxs_auart_console_putchar); /* Finally, wait for transmitter to become empty ... */ - while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) { + while (mxs_read(s, REG_STAT) & AUART_STAT_BUSY) { udelay(1); if (!to--) break; @@ -1060,24 +1392,25 @@ auart_console_write(struct console *co, const char *str, unsigned int count) * unused, but that is better than to disable it while it is still * transmitting. */ - if (!(readl(port->membase + AUART_STAT) & AUART_STAT_BUSY)) { - writel(old_ctrl0, port->membase + AUART_CTRL0); - writel(old_ctrl2, port->membase + AUART_CTRL2); + if (!(mxs_read(s, REG_STAT) & AUART_STAT_BUSY)) { + mxs_write(old_ctrl0, s, REG_CTRL0); + mxs_write(old_ctrl2, s, REG_CTRL2); } clk_disable(s->clk); } static void __init -auart_console_get_options(struct uart_port *port, int *baud, +auart_console_get_options(struct mxs_auart_port *s, int *baud, int *parity, int *bits) { + struct uart_port *port = &s->port; unsigned int lcr_h, quot; - if (!(readl(port->membase + AUART_CTRL2) & AUART_CTRL2_UARTEN)) + if (!(mxs_read(s, REG_CTRL2) & AUART_CTRL2_UARTEN)) return; - lcr_h = readl(port->membase + AUART_LINECTRL); + lcr_h = mxs_read(s, REG_LINECTRL); *parity = 'n'; if (lcr_h & AUART_LINECTRL_PEN) { @@ -1092,12 +1425,10 @@ auart_console_get_options(struct uart_port *port, int *baud, else *bits = 8; - quot = ((readl(port->membase + AUART_LINECTRL) - & AUART_LINECTRL_BAUD_DIVINT_MASK)) - >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6); - quot |= ((readl(port->membase + AUART_LINECTRL) - & AUART_LINECTRL_BAUD_DIVFRAC_MASK)) - >> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT; + quot = ((mxs_read(s, REG_LINECTRL) & AUART_LINECTRL_BAUD_DIVINT_MASK)) + >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6); + quot |= ((mxs_read(s, REG_LINECTRL) & AUART_LINECTRL_BAUD_DIVFRAC_MASK)) + >> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT; if (quot == 0) quot = 1; @@ -1132,7 +1463,7 @@ auart_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - auart_console_get_options(&s->port, &baud, &parity, &bits); + auart_console_get_options(s, &baud, &parity, &bits); ret = uart_set_options(&s->port, co, baud, parity, bits, flow); @@ -1164,6 +1495,60 @@ static struct uart_driver auart_driver = { #endif }; +static void mxs_init_regs(struct mxs_auart_port *s) +{ + if (is_asm9260_auart(s)) + s->vendor = &vendor_alphascale_asm9260; + else + s->vendor = &vendor_freescale_stmp37xx; +} + +static int mxs_get_clks(struct mxs_auart_port *s, + struct platform_device *pdev) +{ + int err; + + if (!is_asm9260_auart(s)) { + s->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(s->clk)) + return PTR_ERR(s->clk); + + return 0; + } + + s->clk = devm_clk_get(s->dev, "mod"); + if (IS_ERR(s->clk)) { + dev_err(s->dev, "Failed to get \"mod\" clk\n"); + return PTR_ERR(s->clk); + } + + s->clk_ahb = devm_clk_get(s->dev, "ahb"); + if (IS_ERR(s->clk_ahb)) { + dev_err(s->dev, "Failed to get \"ahb\" clk\n"); + return PTR_ERR(s->clk_ahb); + } + + err = clk_prepare_enable(s->clk_ahb); + if (err) { + dev_err(s->dev, "Failed to enable ahb_clk!\n"); + return err; + } + + err = clk_set_rate(s->clk, clk_get_rate(s->clk_ahb)); + if (err) { + dev_err(s->dev, "Failed to set rate!\n"); + return err; + } + + err = clk_prepare_enable(s->clk); + if (err) { + dev_err(s->dev, "Failed to enable clk!\n"); + return err; + } + + return 0; +} + /* * This function returns 1 if pdev isn't a device instatiated by dt, 0 if it * could successfully get all information from dt or a negative errno. @@ -1185,7 +1570,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; - if (of_get_property(np, "fsl,uart-has-rtscts", NULL)) + if (of_get_property(np, "uart-has-rtscts", NULL) || + of_get_property(np, "fsl,uart-has-rtscts", NULL) /* deprecated */) set_bit(MXS_AUART_RTSCTS, &s->flags); return 0; @@ -1269,6 +1655,9 @@ static int mxs_auart_probe(struct platform_device *pdev) if (!s) return -ENOMEM; + s->port.dev = &pdev->dev; + s->dev = &pdev->dev; + ret = serial_mxs_probe_dt(s, pdev); if (ret > 0) s->port.line = pdev->id < 0 ? 0 : pdev->id; @@ -1280,15 +1669,14 @@ static int mxs_auart_probe(struct platform_device *pdev) s->devtype = pdev->id_entry->driver_data; } - s->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(s->clk)) - return PTR_ERR(s->clk); + ret = mxs_get_clks(s, pdev); + if (ret) + return ret; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) return -ENXIO; - s->port.mapbase = r->start; s->port.membase = ioremap(r->start, resource_size(r)); s->port.ops = &mxs_auart_ops; @@ -1296,7 +1684,8 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.fifosize = MXS_AUART_FIFO_SIZE; s->port.uartclk = clk_get_rate(s->clk); s->port.type = PORT_IMX; - s->port.dev = s->dev = &pdev->dev; + + mxs_init_regs(s); s->mctrl_prev = 0; @@ -1327,16 +1716,21 @@ static int mxs_auart_probe(struct platform_device *pdev) auart_port[s->port.line] = s; - mxs_auart_reset_deassert(&s->port); + mxs_auart_reset_deassert(s); ret = uart_add_one_port(&auart_driver, &s->port); if (ret) goto out_free_gpio_irq; - version = readl(s->port.membase + AUART_VERSION); - dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", - (version >> 24) & 0xff, - (version >> 16) & 0xff, version & 0xffff); + /* ASM9260 don't have version reg */ + if (is_asm9260_auart(s)) { + dev_info(&pdev->dev, "Found APPUART ASM9260\n"); + } else { + version = mxs_read(s, REG_VERSION); + dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", + (version >> 24) & 0xff, + (version >> 16) & 0xff, version & 0xffff); + } return 0; diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index ac7f8df..99bb231 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1271,6 +1271,8 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, /* check to see if we need to change clock source */ if (ourport->baudclk != clk) { + clk_prepare_enable(clk); + s3c24xx_serial_setsource(port, clk_sel); if (!IS_ERR(ourport->baudclk)) { @@ -1278,8 +1280,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, ourport->baudclk = ERR_PTR(-EINVAL); } - clk_prepare_enable(clk); - ourport->baudclk = clk; ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; } diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index e639361..f36e6df 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -666,7 +666,7 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) struct uart_port *port = &s->p[portno].port; do { - unsigned int iir, msr, rxlen; + unsigned int iir, rxlen; iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); if (iir & SC16IS7XX_IIR_NO_INT_BIT) @@ -683,12 +683,6 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) if (rxlen) sc16is7xx_handle_rx(port, rxlen, iir); break; - - case SC16IS7XX_IIR_CTSRTS_SRC: - msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG); - uart_handle_cts_change(port, - !!(msr & SC16IS7XX_MSR_DCTS_BIT)); - break; case SC16IS7XX_IIR_THRI_SRC: sc16is7xx_handle_tx(port); break; @@ -1014,9 +1008,8 @@ static int sc16is7xx_startup(struct uart_port *port) SC16IS7XX_EFCR_TXDISABLE_BIT, 0); - /* Enable RX, TX, CTS change interrupts */ - val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT | - SC16IS7XX_IER_CTSI_BIT; + /* Enable RX, TX interrupts */ + val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT; sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val); return 0; diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 1d6fc60..1dba671 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -206,10 +206,8 @@ static void set_dtr(struct tegra_uart_port *tup, bool active) static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl) { struct tegra_uart_port *tup = to_tegra_uport(u); - unsigned long mcr; int dtr_enable; - mcr = tup->mcr_shadow; tup->rts_active = !!(mctrl & TIOCM_RTS); set_rts(tup, tup->rts_active); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a126a60..a333c59 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -64,6 +64,41 @@ static int uart_dcd_enabled(struct uart_port *uport) return !!(uport->status & UPSTAT_DCD_ENABLE); } +static inline struct uart_port *uart_port_ref(struct uart_state *state) +{ + if (atomic_add_unless(&state->refcount, 1, 0)) + return state->uart_port; + return NULL; +} + +static inline void uart_port_deref(struct uart_port *uport) +{ + if (uport && atomic_dec_and_test(&uport->state->refcount)) + wake_up(&uport->state->remove_wait); +} + +#define uart_port_lock(state, flags) \ + ({ \ + struct uart_port *__uport = uart_port_ref(state); \ + if (__uport) \ + spin_lock_irqsave(&__uport->lock, flags); \ + __uport; \ + }) + +#define uart_port_unlock(uport, flags) \ + ({ \ + struct uart_port *__uport = uport; \ + if (__uport) \ + spin_unlock_irqrestore(&__uport->lock, flags); \ + uart_port_deref(__uport); \ + }) + +static inline struct uart_port *uart_port_check(struct uart_state *state) +{ + lockdep_assert_held(&state->port.mutex); + return state->uart_port; +} + /* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. @@ -82,12 +117,13 @@ void uart_write_wakeup(struct uart_port *port) static void uart_stop(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - port->ops->stop_tx(port); - spin_unlock_irqrestore(&port->lock, flags); + port = uart_port_lock(state, flags); + if (port) + port->ops->stop_tx(port); + uart_port_unlock(port, flags); } static void __uart_start(struct tty_struct *tty) @@ -95,19 +131,19 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - if (!uart_tx_stopped(port)) + if (port && !uart_tx_stopped(port)) port->ops->start_tx(port); } static void uart_start(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + port = uart_port_lock(state, flags); __uart_start(tty); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock(port, flags); } static void @@ -134,7 +170,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); unsigned long page; int retval = 0; @@ -196,7 +232,7 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, struct tty_port *port = &state->port; int retval; - if (port->flags & ASYNC_INITIALIZED) + if (tty_port_initialized(port)) return 0; /* @@ -207,7 +243,7 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, retval = uart_port_startup(tty, state, init_hw); if (!retval) { - set_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 1); clear_bit(TTY_IO_ERROR, &tty->flags); } else if (retval > 0) retval = 0; @@ -219,10 +255,12 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. Calls to * uart_shutdown are serialised by the per-port semaphore. + * + * uport == NULL if uart_port has already been removed */ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); struct tty_port *port = &state->port; /* @@ -231,11 +269,13 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) if (tty) set_bit(TTY_IO_ERROR, &tty->flags); - if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (tty_port_initialized(port)) { + tty_port_set_initialized(port, 0); + /* * Turn off DTR and RTS early. */ - if (uart_console(uport) && tty) + if (uport && uart_console(uport) && tty) uport->cons->cflag = tty->termios.c_cflag; if (!tty || C_HUPCL(tty)) @@ -249,7 +289,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) * a DCD drop (hangup) at just the right time. Clear suspended bit so * we don't try to resume a port that has been shutdown. */ - clear_bit(ASYNCB_SUSPENDED, &port->flags); + tty_port_set_suspended(port, 0); /* * Free the transmit buffer page. @@ -441,7 +481,7 @@ EXPORT_SYMBOL(uart_get_divisor); static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); struct ktermios *termios; int hw_stopped; @@ -486,7 +526,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, static int uart_put_char(struct tty_struct *tty, unsigned char c) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; struct circ_buf *circ; unsigned long flags; int ret = 0; @@ -495,13 +535,13 @@ static int uart_put_char(struct tty_struct *tty, unsigned char c) if (!circ->buf) return 0; - spin_lock_irqsave(&port->lock, flags); - if (uart_circ_chars_free(circ) != 0) { + port = uart_port_lock(state, flags); + if (port && uart_circ_chars_free(circ) != 0) { circ->buf[circ->head] = c; circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); ret = 1; } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock(port, flags); return ret; } @@ -528,14 +568,12 @@ static int uart_write(struct tty_struct *tty, return -EL3HLT; } - port = state->uart_port; circ = &state->xmit; - if (!circ->buf) return 0; - spin_lock_irqsave(&port->lock, flags); - while (1) { + port = uart_port_lock(state, flags); + while (port) { c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (count < c) c = count; @@ -549,32 +587,33 @@ static int uart_write(struct tty_struct *tty, } __uart_start(tty); - spin_unlock_irqrestore(&port->lock, flags); - + uart_port_unlock(port, flags); return ret; } static int uart_write_room(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + struct uart_port *port; unsigned long flags; int ret; - spin_lock_irqsave(&state->uart_port->lock, flags); + port = uart_port_lock(state, flags); ret = uart_circ_chars_free(&state->xmit); - spin_unlock_irqrestore(&state->uart_port->lock, flags); + uart_port_unlock(port, flags); return ret; } static int uart_chars_in_buffer(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + struct uart_port *port; unsigned long flags; int ret; - spin_lock_irqsave(&state->uart_port->lock, flags); + port = uart_port_lock(state, flags); ret = uart_circ_chars_pending(&state->xmit); - spin_unlock_irqrestore(&state->uart_port->lock, flags); + uart_port_unlock(port, flags); return ret; } @@ -593,14 +632,15 @@ static void uart_flush_buffer(struct tty_struct *tty) return; } - port = state->uart_port; pr_debug("uart_flush_buffer(%d) called\n", tty->index); - spin_lock_irqsave(&port->lock, flags); + port = uart_port_lock(state, flags); + if (!port) + return; uart_circ_clear(&state->xmit); if (port->ops->flush_buffer) port->ops->flush_buffer(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock(port, flags); tty_wakeup(tty); } @@ -611,9 +651,13 @@ static void uart_flush_buffer(struct tty_struct *tty) static void uart_send_xchar(struct tty_struct *tty, char ch) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long flags; + port = uart_port_ref(state); + if (!port) + return; + if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { @@ -623,14 +667,19 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) port->ops->start_tx(port); spin_unlock_irqrestore(&port->lock, flags); } + uart_port_deref(port); } static void uart_throttle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; upstat_t mask = 0; + port = uart_port_ref(state); + if (!port) + return; + if (I_IXOFF(tty)) mask |= UPSTAT_AUTOXOFF; if (C_CRTSCTS(tty)) @@ -646,14 +695,20 @@ static void uart_throttle(struct tty_struct *tty) if (mask & UPSTAT_AUTOXOFF) uart_send_xchar(tty, STOP_CHAR(tty)); + + uart_port_deref(port); } static void uart_unthrottle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; upstat_t mask = 0; + port = uart_port_ref(state); + if (!port) + return; + if (I_IXOFF(tty)) mask |= UPSTAT_AUTOXOFF; if (C_CRTSCTS(tty)) @@ -669,12 +724,15 @@ static void uart_unthrottle(struct tty_struct *tty) if (mask & UPSTAT_AUTOXOFF) uart_send_xchar(tty, START_CHAR(tty)); + + uart_port_deref(port); } -static void uart_get_info(struct tty_port *port, struct serial_struct *retinfo) +static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + int ret = -ENODEV; memset(retinfo, 0, sizeof(*retinfo)); @@ -683,6 +741,10 @@ static void uart_get_info(struct tty_port *port, struct serial_struct *retinfo) * occur as we go */ mutex_lock(&port->mutex); + uport = uart_port_check(state); + if (!uport) + goto out; + retinfo->type = uport->type; retinfo->line = uport->line; retinfo->port = uport->iobase; @@ -701,7 +763,11 @@ static void uart_get_info(struct tty_port *port, struct serial_struct *retinfo) retinfo->io_type = uport->iotype; retinfo->iomem_reg_shift = uport->regshift; retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; + + ret = 0; +out: mutex_unlock(&port->mutex); + return ret; } static int uart_get_info_user(struct tty_port *port, @@ -709,7 +775,8 @@ static int uart_get_info_user(struct tty_port *port, { struct serial_struct tmp; - uart_get_info(port, &tmp); + if (uart_get_info(port, &tmp) < 0) + return -EIO; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -720,13 +787,16 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, struct uart_state *state, struct serial_struct *new_info) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); unsigned long new_port; unsigned int change_irq, change_port, closing_wait; unsigned int old_custom_divisor, close_delay; upf_t old_flags, new_flags; int retval = 0; + if (!uport) + return -EIO; + new_port = new_info->port; if (HIGH_BITS_OFFSET) new_port += (unsigned long) new_info->port_high << HIGH_BITS_OFFSET; @@ -886,7 +956,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, retval = 0; if (uport->type == PORT_UNKNOWN) goto exit; - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port)) { if (((old_flags ^ uport->flags) & UPF_SPD_MASK) || old_custom_divisor != uport->custom_divisor) { /* @@ -936,13 +1006,11 @@ static int uart_set_info_user(struct tty_struct *tty, struct uart_state *state, * @tty: tty associated with the UART * @state: UART being queried * @value: returned modem value - * - * Note: uart_ioctl protects us against hangups. */ static int uart_get_lsr_info(struct tty_struct *tty, struct uart_state *state, unsigned int __user *value) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); unsigned int result; result = uport->ops->tx_empty(uport); @@ -965,18 +1033,22 @@ static int uart_tiocmget(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; int result = -EIO; mutex_lock(&port->mutex); - if (!(tty->flags & (1 << TTY_IO_ERROR))) { + uport = uart_port_check(state); + if (!uport) + goto out; + + if (!tty_io_error(tty)) { result = uport->mctrl; spin_lock_irq(&uport->lock); result |= uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); } +out: mutex_unlock(&port->mutex); - return result; } @@ -984,15 +1056,20 @@ static int uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; + struct uart_port *uport; int ret = -EIO; mutex_lock(&port->mutex); - if (!(tty->flags & (1 << TTY_IO_ERROR))) { + uport = uart_port_check(state); + if (!uport) + goto out; + + if (!tty_io_error(tty)) { uart_update_mctrl(uport, set, clear); ret = 0; } +out: mutex_unlock(&port->mutex); return ret; } @@ -1001,21 +1078,26 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + int ret = -EIO; mutex_lock(&port->mutex); + uport = uart_port_check(state); + if (!uport) + goto out; if (uport->type != PORT_UNKNOWN) uport->ops->break_ctl(uport, break_state); - + ret = 0; +out: mutex_unlock(&port->mutex); - return 0; + return ret; } static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) { - struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; + struct uart_port *uport; int flags, ret; if (!capable(CAP_SYS_ADMIN)) @@ -1029,6 +1111,12 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) if (mutex_lock_interruptible(&port->mutex)) return -ERESTARTSYS; + uport = uart_port_check(state); + if (!uport) { + ret = -EIO; + goto out; + } + ret = -EBUSY; if (tty_port_users(port) == 1) { uart_shutdown(tty, state); @@ -1052,6 +1140,7 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) ret = uart_startup(tty, state, 1); } +out: mutex_unlock(&port->mutex); return ret; } @@ -1074,10 +1163,9 @@ static void uart_enable_ms(struct uart_port *uport) * FIXME: This wants extracting into a common all driver implementation * of TIOCMWAIT using tty_port. */ -static int -uart_wait_modem_status(struct uart_state *state, unsigned long arg) +static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport; struct tty_port *port = &state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; @@ -1086,6 +1174,9 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) /* * note the counters on entry */ + uport = uart_port_ref(state); + if (!uport) + return -EIO; spin_lock_irq(&uport->lock); memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); uart_enable_ms(uport); @@ -1119,6 +1210,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) } __set_current_state(TASK_RUNNING); remove_wait_queue(&port->delta_msr_wait, &wait); + uart_port_deref(uport); return ret; } @@ -1134,11 +1226,15 @@ static int uart_get_icount(struct tty_struct *tty, { struct uart_state *state = tty->driver_data; struct uart_icount cnow; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + uport = uart_port_ref(state); + if (!uport) + return -EIO; spin_lock_irq(&uport->lock); memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); spin_unlock_irq(&uport->lock); + uart_port_deref(uport); icount->cts = cnow.cts; icount->dsr = cnow.dsr; @@ -1200,11 +1296,11 @@ static int uart_set_rs485_config(struct uart_port *port, * Called via sys_ioctl. We can use spin_lock_irq() here. */ static int -uart_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) +uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; + struct uart_port *uport; void __user *uarg = (void __user *)arg; int ret = -ENOIOCTLCMD; @@ -1238,7 +1334,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, if (ret != -ENOIOCTLCMD) goto out; - if (tty->flags & (1 << TTY_IO_ERROR)) { + if (tty_io_error(tty)) { ret = -EIO; goto out; } @@ -1256,8 +1352,9 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, goto out; mutex_lock(&port->mutex); + uport = uart_port_check(state); - if (tty->flags & (1 << TTY_IO_ERROR)) { + if (!uport || tty_io_error(tty)) { ret = -EIO; goto out_up; } @@ -1273,19 +1370,17 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, break; case TIOCGRS485: - ret = uart_get_rs485_config(state->uart_port, uarg); + ret = uart_get_rs485_config(uport, uarg); break; case TIOCSRS485: - ret = uart_set_rs485_config(state->uart_port, uarg); + ret = uart_set_rs485_config(uport, uarg); break; - default: { - struct uart_port *uport = state->uart_port; + default: if (uport->ops->ioctl) ret = uport->ops->ioctl(uport, cmd, arg); break; } - } out_up: mutex_unlock(&port->mutex); out: @@ -1295,24 +1390,29 @@ out: static void uart_set_ldisc(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; - if (uport->ops->set_ldisc) { - mutex_lock(&state->port.mutex); + mutex_lock(&state->port.mutex); + uport = uart_port_check(state); + if (uport && uport->ops->set_ldisc) uport->ops->set_ldisc(uport, &tty->termios); - mutex_unlock(&state->port.mutex); - } + mutex_unlock(&state->port.mutex); } static void uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; unsigned int cflag = tty->termios.c_cflag; unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; bool sw_changed = false; + mutex_lock(&state->port.mutex); + uport = uart_port_check(state); + if (!uport) + goto out; + /* * Drivers doing software flow control also need to know * about changes to these input settings. @@ -1335,12 +1435,10 @@ static void uart_set_termios(struct tty_struct *tty, tty->termios.c_ispeed == old_termios->c_ispeed && ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 && !sw_changed) { - return; + goto out; } - mutex_lock(&state->port.mutex); uart_change_speed(tty, state, old_termios); - mutex_unlock(&state->port.mutex); /* reload cflag from termios; port driver may have overriden flags */ cflag = tty->termios.c_cflag; @@ -1350,17 +1448,18 @@ static void uart_set_termios(struct tty_struct *tty, /* Handle transition away from B0 status */ else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; - if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) + if (!(cflag & CRTSCTS) || !tty_throttled(tty)) mask |= TIOCM_RTS; uart_set_mctrl(uport, mask); } +out: + mutex_unlock(&state->port.mutex); } /* * Calls to uart_close() are serialised via the tty_lock in * drivers/tty/tty_io.c:tty_release() * drivers/tty/tty_io.c:do_tty_hangup() - * This runs from a workqueue and can sleep for a _short_ time only. */ static void uart_close(struct tty_struct *tty, struct file *filp) { @@ -1379,18 +1478,21 @@ static void uart_close(struct tty_struct *tty, struct file *filp) return; } - uport = state->uart_port; port = &state->port; pr_debug("uart_close(%d) called\n", tty->index); - if (!port->count || tty_port_close_start(port, tty, filp) == 0) + if (tty_port_close_start(port, tty, filp) == 0) return; + mutex_lock(&port->mutex); + uport = uart_port_check(state); + /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port) && + !WARN(!uport, "detached port still initialized!\n")) { spin_lock_irq(&uport->lock); uport->ops->stop_rx(uport); spin_unlock_irq(&uport->lock); @@ -1402,7 +1504,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uart_wait_until_sent(tty, uport->timeout); } - mutex_lock(&port->mutex); uart_shutdown(tty, state); tty_port_tty_set(port, NULL); @@ -1413,17 +1514,17 @@ static void uart_close(struct tty_struct *tty, struct file *filp) if (port->close_delay) msleep_interruptible(jiffies_to_msecs(port->close_delay)); spin_lock_irq(&port->lock); - } else if (!uart_console(uport)) { + } else if (uport && !uart_console(uport)) { spin_unlock_irq(&port->lock); uart_change_pm(state, UART_PM_STATE_OFF); spin_lock_irq(&port->lock); } + spin_unlock_irq(&port->lock); + tty_port_set_active(port, 0); /* * Wake up anyone trying to open this port. */ - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); - spin_unlock_irq(&port->lock); wake_up_interruptible(&port->open_wait); mutex_unlock(&port->mutex); @@ -1435,11 +1536,14 @@ static void uart_close(struct tty_struct *tty, struct file *filp) static void uart_wait_until_sent(struct tty_struct *tty, int timeout) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long char_time, expire; - if (port->type == PORT_UNKNOWN || port->fifosize == 0) + port = uart_port_ref(state); + if (!port || port->type == PORT_UNKNOWN || port->fifosize == 0) { + uart_port_deref(port); return; + } /* * Set the check interval to be 1/5 of the estimated time to @@ -1485,6 +1589,7 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) if (time_after(jiffies, expire)) break; } + uart_port_deref(port); } /* @@ -1496,20 +1601,24 @@ static void uart_hangup(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; + struct uart_port *uport; unsigned long flags; pr_debug("uart_hangup(%d)\n", tty->index); mutex_lock(&port->mutex); - if (port->flags & ASYNC_NORMAL_ACTIVE) { + uport = uart_port_check(state); + WARN(!uport, "hangup of detached port!\n"); + + if (tty_port_active(port)) { uart_flush_buffer(tty); uart_shutdown(tty, state); spin_lock_irqsave(&port->lock, flags); port->count = 0; - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); spin_unlock_irqrestore(&port->lock, flags); + tty_port_set_active(port, 0); tty_port_tty_set(port, NULL); - if (!uart_console(state->uart_port)) + if (uport && !uart_console(uport)) uart_change_pm(state, UART_PM_STATE_OFF); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); @@ -1517,10 +1626,11 @@ static void uart_hangup(struct tty_struct *tty) mutex_unlock(&port->mutex); } +/* uport == NULL if uart_port has already been removed */ static void uart_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); /* * clear delta_msr_wait queue to avoid mem leaks: we may free @@ -1534,23 +1644,36 @@ static void uart_port_shutdown(struct tty_port *port) /* * Free the IRQ and disable the port. */ - uport->ops->shutdown(uport); + if (uport) + uport->ops->shutdown(uport); /* * Ensure that the IRQ handler isn't running on another CPU. */ - synchronize_irq(uport->irq); + if (uport) + synchronize_irq(uport->irq); } static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport; int mctrl; + + uport = uart_port_ref(state); + /* + * Should never observe uport == NULL since checks for hangup should + * abort the tty_port_block_til_ready() loop before checking for carrier + * raised -- but report carrier raised if it does anyway so open will + * continue and not sleep + */ + if (WARN_ON(!uport)) + return 1; spin_lock_irq(&uport->lock); uart_enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); + uart_port_deref(uport); if (mctrl & TIOCM_CAR) return 1; return 0; @@ -1559,12 +1682,18 @@ static int uart_carrier_raised(struct tty_port *port) static void uart_dtr_rts(struct tty_port *port, int onoff) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + + uport = uart_port_ref(state); + if (!uport) + return; if (onoff) uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); else uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); + + uart_port_deref(uport); } /* @@ -1583,6 +1712,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) int retval, line = tty->index; struct uart_state *state = drv->state + line; struct tty_port *port = &state->port; + struct uart_port *uport; pr_debug("uart_open(%d) called\n", line); @@ -1602,15 +1732,15 @@ static int uart_open(struct tty_struct *tty, struct file *filp) goto end; } - if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { + uport = uart_port_check(state); + if (!uport || uport->flags & UPF_DEAD) { retval = -ENXIO; goto err_unlock; } tty->driver_data = state; - state->uart_port->state = state; - state->port.low_latency = - (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; + uport->state = state; + port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; tty_port_tty_set(port, tty); /* @@ -1649,13 +1779,15 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) struct uart_state *state = drv->state + i; struct tty_port *port = &state->port; enum uart_pm_state pm_state; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; char stat_buf[32]; unsigned int status; int mmio; + mutex_lock(&port->mutex); + uport = uart_port_check(state); if (!uport) - return; + goto out; mmio = uport->iotype >= UPIO_MEM; seq_printf(m, "%d: uart:%s %s%08llX irq:%d", @@ -1667,11 +1799,10 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); - return; + goto out; } if (capable(CAP_SYS_ADMIN)) { - mutex_lock(&port->mutex); pm_state = state->pm_state; if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, UART_PM_STATE_ON); @@ -1680,7 +1811,6 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) spin_unlock_irq(&uport->lock); if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, pm_state); - mutex_unlock(&port->mutex); seq_printf(m, " tx:%d rx:%d", uport->icount.tx, uport->icount.rx); @@ -1718,6 +1848,8 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) seq_putc(m, '\n'); #undef STATBIT #undef INFOBIT +out: + mutex_unlock(&port->mutex); } static int uart_proc_show(struct seq_file *m, void *v) @@ -1954,10 +2086,10 @@ EXPORT_SYMBOL_GPL(uart_set_options); static void uart_change_pm(struct uart_state *state, enum uart_pm_state pm_state) { - struct uart_port *port = state->uart_port; + struct uart_port *port = uart_port_check(state); if (state->pm_state != pm_state) { - if (port->ops->pm) + if (port && port->ops->pm) port->ops->pm(port, pm_state, state->pm_state); state->pm_state = pm_state; } @@ -2003,12 +2135,12 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) uport->suspended = 1; - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port)) { const struct uart_ops *ops = uport->ops; int tries; - set_bit(ASYNCB_SUSPENDED, &port->flags); - clear_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_suspended(port, 1); + tty_port_set_initialized(port, 0); spin_lock_irq(&uport->lock); ops->stop_tx(uport); @@ -2088,7 +2220,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) console_start(uport->cons); } - if (port->flags & ASYNC_SUSPENDED) { + if (tty_port_suspended(port)) { const struct uart_ops *ops = uport->ops; int ret; @@ -2107,7 +2239,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) ops->set_mctrl(uport, uport->mctrl); ops->start_tx(uport); spin_unlock_irq(&uport->lock); - set_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 1); } else { /* * Failed to resume - maybe hardware went away? @@ -2118,7 +2250,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) } } - clear_bit(ASYNCB_SUSPENDED, &port->flags); + tty_port_set_suspended(port, 0); } mutex_unlock(&port->mutex); @@ -2228,42 +2360,42 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) { struct uart_driver *drv = driver->driver_state; struct uart_state *state = drv->state + line; + struct tty_port *tport; struct uart_port *port; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; + int ret = 0; - if (!state || !state->uart_port) + if (!state) return -1; - port = state->uart_port; - if (!(port->ops->poll_get_char && port->ops->poll_put_char)) - return -1; + tport = &state->port; + mutex_lock(&tport->mutex); - if (port->ops->poll_init) { - struct tty_port *tport = &state->port; + port = uart_port_check(state); + if (!port || !(port->ops->poll_get_char && port->ops->poll_put_char)) { + ret = -1; + goto out; + } - ret = 0; - mutex_lock(&tport->mutex); + if (port->ops->poll_init) { /* - * We don't set ASYNCB_INITIALIZED as we only initialized the - * hw, e.g. state->xmit is still uninitialized. + * We don't set initialized as we only initialized the hw, + * e.g. state->xmit is still uninitialized. */ - if (!test_bit(ASYNCB_INITIALIZED, &tport->flags)) + if (!tty_port_initialized(tport)) ret = port->ops->poll_init(port); - mutex_unlock(&tport->mutex); - if (ret) - return ret; } - if (options) { + if (!ret && options) { uart_parse_options(options, &baud, &parity, &bits, &flow); - return uart_set_options(port, NULL, baud, parity, bits, flow); + ret = uart_set_options(port, NULL, baud, parity, bits, flow); } - - return 0; +out: + mutex_unlock(&tport->mutex); + return ret; } static int uart_poll_get_char(struct tty_driver *driver, int line) @@ -2271,12 +2403,15 @@ static int uart_poll_get_char(struct tty_driver *driver, int line) struct uart_driver *drv = driver->driver_state; struct uart_state *state = drv->state + line; struct uart_port *port; + int ret = -1; - if (!state || !state->uart_port) - return -1; - - port = state->uart_port; - return port->ops->poll_get_char(port); + if (state) { + port = uart_port_ref(state); + if (port) + ret = port->ops->poll_get_char(port); + uart_port_deref(port); + } + return ret; } static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) @@ -2285,14 +2420,17 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) struct uart_state *state = drv->state + line; struct uart_port *port; - if (!state || !state->uart_port) + if (!state) return; - port = state->uart_port; + port = uart_port_ref(state); + if (!port) + return; if (ch == '\n') port->ops->poll_put_char(port, '\r'); port->ops->poll_put_char(port, ch); + uart_port_deref(port); } #endif @@ -2639,6 +2777,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) } /* Link the port to the driver state table and vice versa */ + atomic_set(&state->refcount, 1); + init_waitqueue_head(&state->remove_wait); state->uart_port = uport; uport->state = state; @@ -2711,15 +2851,12 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) { struct uart_state *state = drv->state + uport->line; struct tty_port *port = &state->port; + struct uart_port *uart_port; struct tty_struct *tty; int ret = 0; BUG_ON(in_interrupt()); - if (state->uart_port != uport) - dev_alert(uport->dev, "Removing wrong port: %p != %p\n", - state->uart_port, uport); - mutex_lock(&port_mutex); /* @@ -2727,7 +2864,12 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) * succeeding while we shut down the port. */ mutex_lock(&port->mutex); - if (!state->uart_port) { + uart_port = uart_port_check(state); + if (uart_port != uport) + dev_alert(uport->dev, "Removing wrong port: %p != %p\n", + uart_port, uport); + + if (!uart_port) { mutex_unlock(&port->mutex); ret = -EINVAL; goto out; @@ -2764,7 +2906,11 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) */ uport->type = PORT_UNKNOWN; + mutex_lock(&port->mutex); + WARN_ON(atomic_dec_return(&state->refcount) < 0); + wait_event(state->remove_wait, !atomic_read(&state->refcount)); state->uart_port = NULL; + mutex_unlock(&port->mutex); out: mutex_unlock(&port_mutex); diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 0214736..e8dd509 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -43,8 +43,6 @@ static const struct { { "rng", TIOCM_RNG, false, }, { "rts", TIOCM_RTS, true, }, { "dtr", TIOCM_DTR, true, }, - { "out1", TIOCM_OUT1, true, }, - { "out2", TIOCM_OUT2, true, }, }; void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) @@ -125,9 +123,12 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) struct uart_port *port = gpios->port; u32 mctrl = gpios->mctrl_prev; u32 mctrl_diff; + unsigned long flags; mctrl_gpio_get(gpios, &mctrl); + spin_lock_irqsave(&port->lock, flags); + mctrl_diff = mctrl ^ gpios->mctrl_prev; gpios->mctrl_prev = mctrl; @@ -147,6 +148,8 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) wake_up_interruptible(&port->state->port.delta_msr_wait); } + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_HANDLED; } diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h index bcfad5d..332a33a 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.h +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -32,8 +32,6 @@ enum mctrl_gpio_idx { UART_GPIO_RI = UART_GPIO_RNG, UART_GPIO_RTS, UART_GPIO_DTR, - UART_GPIO_OUT1, - UART_GPIO_OUT2, UART_GPIO_MAX, }; diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index c6657de..b186c9c 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1264,6 +1264,7 @@ MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); static int sirfsoc_uart_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct sirfsoc_uart_port *sirfport; struct uart_port *port; struct resource *res; @@ -1276,13 +1277,13 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) }; const struct of_device_id *match; - match = of_match_node(sirfsoc_uart_ids, pdev->dev.of_node); + match = of_match_node(sirfsoc_uart_ids, np); sirfport = devm_kzalloc(&pdev->dev, sizeof(*sirfport), GFP_KERNEL); if (!sirfport) { ret = -ENOMEM; goto err; } - sirfport->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); + sirfport->port.line = of_alias_get_id(np, "serial"); sirf_ports[sirfport->port.line] = sirfport; sirfport->port.iotype = UPIO_MEM; sirfport->port.flags = UPF_BOOT_AUTOCONF; @@ -1291,25 +1292,25 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) port->private_data = sirfport; sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data; - sirfport->hw_flow_ctrl = of_property_read_bool(pdev->dev.of_node, - "sirf,uart-has-rtscts"); - if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart") || - of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart")) + sirfport->hw_flow_ctrl = + of_property_read_bool(np, "uart-has-rtscts") || + of_property_read_bool(np, "sirf,uart-has-rtscts") /* deprecated */; + if (of_device_is_compatible(np, "sirf,prima2-uart") || + of_device_is_compatible(np, "sirf,atlas7-uart")) sirfport->uart_reg->uart_type = SIRF_REAL_UART; - if (of_device_is_compatible(pdev->dev.of_node, - "sirf,prima2-usp-uart") || of_device_is_compatible( - pdev->dev.of_node, "sirf,atlas7-usp-uart")) { + if (of_device_is_compatible(np, "sirf,prima2-usp-uart") || + of_device_is_compatible(np, "sirf,atlas7-usp-uart")) { sirfport->uart_reg->uart_type = SIRF_USP_UART; if (!sirfport->hw_flow_ctrl) goto usp_no_flow_control; - if (of_find_property(pdev->dev.of_node, "cts-gpios", NULL)) - sirfport->cts_gpio = of_get_named_gpio( - pdev->dev.of_node, "cts-gpios", 0); + if (of_find_property(np, "cts-gpios", NULL)) + sirfport->cts_gpio = + of_get_named_gpio(np, "cts-gpios", 0); else sirfport->cts_gpio = -1; - if (of_find_property(pdev->dev.of_node, "rts-gpios", NULL)) - sirfport->rts_gpio = of_get_named_gpio( - pdev->dev.of_node, "rts-gpios", 0); + if (of_find_property(np, "rts-gpios", NULL)) + sirfport->rts_gpio = + of_get_named_gpio(np, "rts-gpios", 0); else sirfport->rts_gpio = -1; @@ -1336,13 +1337,11 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) gpio_direction_output(sirfport->rts_gpio, 1); } usp_no_flow_control: - if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart") || - of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-usp-uart")) + if (of_device_is_compatible(np, "sirf,atlas7-uart") || + of_device_is_compatible(np, "sirf,atlas7-usp-uart")) sirfport->is_atlas7 = true; - if (of_property_read_u32(pdev->dev.of_node, - "fifosize", - &port->fifosize)) { + if (of_property_read_u32(np, "fifosize", &port->fifosize)) { dev_err(&pdev->dev, "Unable to find fifosize in uart node.\n"); ret = -EFAULT; diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index d08baa6..05089b6 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -72,7 +72,7 @@ static void uartlite_outbe32(u32 val, void __iomem *addr) iowrite32be(val, addr); } -static struct uartlite_reg_ops uartlite_be = { +static const struct uartlite_reg_ops uartlite_be = { .in = uartlite_inbe32, .out = uartlite_outbe32, }; @@ -87,21 +87,21 @@ static void uartlite_outle32(u32 val, void __iomem *addr) iowrite32(val, addr); } -static struct uartlite_reg_ops uartlite_le = { +static const struct uartlite_reg_ops uartlite_le = { .in = uartlite_inle32, .out = uartlite_outle32, }; static inline u32 uart_in32(u32 offset, struct uart_port *port) { - struct uartlite_reg_ops *reg_ops = port->private_data; + const struct uartlite_reg_ops *reg_ops = port->private_data; return reg_ops->in(port->membase + offset); } static inline void uart_out32(u32 val, u32 offset, struct uart_port *port) { - struct uartlite_reg_ops *reg_ops = port->private_data; + const struct uartlite_reg_ops *reg_ops = port->private_data; reg_ops->out(val, port->membase + offset); } @@ -345,13 +345,13 @@ static int ulite_request_port(struct uart_port *port) return -EBUSY; } - port->private_data = &uartlite_be; + port->private_data = (void *)&uartlite_be; ret = uart_in32(ULITE_CONTROL, port); uart_out32(ULITE_CONTROL_RST_TX, ULITE_CONTROL, port); ret = uart_in32(ULITE_STATUS, port); /* Endianess detection */ if ((ret & ULITE_STATUS_TXEMPTY) != ULITE_STATUS_TXEMPTY) - port->private_data = &uartlite_le; + port->private_data = (void *)&uartlite_le; return 0; } diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 1a7dc3c..481eb29 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1478,6 +1478,9 @@ static const struct of_device_id ucc_uart_match[] = { .type = "serial", .compatible = "ucc_uart", }, + { + .compatible = "fsl,t1040-ucc-uart", + }, {}, }; MODULE_DEVICE_TABLE(of, ucc_uart_match); diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index c8c7601..c13e27e 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -1340,7 +1340,7 @@ static void mgsl_isr_io_pin( struct mgsl_struct *info ) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - if ( (info->port.flags & ASYNC_CHECK_CD) && + if (tty_port_check_carrier(&info->port) && (status & MISCSTATUS_DCD_LATCHED) ) { if ( debug_level >= DEBUG_LEVEL_ISR ) printk("%s CD now %s...", info->device_name, @@ -1361,8 +1361,7 @@ static void mgsl_isr_io_pin( struct mgsl_struct *info ) if (status & MISCSTATUS_CTS) { if ( debug_level >= DEBUG_LEVEL_ISR ) printk("CTS tx start..."); - if (info->port.tty) - info->port.tty->hw_stopped = 0; + info->port.tty->hw_stopped = 0; usc_start_transmitter(info); info->pending_bh |= BH_TRANSMIT; return; @@ -1749,13 +1748,13 @@ static irqreturn_t mgsl_interrupt(int dummy, void *dev_id) static int startup(struct mgsl_struct * info) { int retval = 0; - + if ( debug_level >= DEBUG_LEVEL_INFO ) printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name); - - if (info->port.flags & ASYNC_INITIALIZED) + + if (tty_port_initialized(&info->port)) return 0; - + if (!info->xmit_buf) { /* allocate a page of memory for a transmit buffer */ info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); @@ -1788,14 +1787,13 @@ static int startup(struct mgsl_struct * info) /* program hardware for current parameters */ mgsl_change_params(info); - + if (info->port.tty) clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags |= ASYNC_INITIALIZED; - + tty_port_set_initialized(&info->port, 1); + return 0; - } /* end of startup() */ /* shutdown() @@ -1808,8 +1806,8 @@ static int startup(struct mgsl_struct * info) static void shutdown(struct mgsl_struct * info) { unsigned long flags; - - if (!(info->port.flags & ASYNC_INITIALIZED)) + + if (!tty_port_initialized(&info->port)) return; if (debug_level >= DEBUG_LEVEL_INFO) @@ -1853,13 +1851,12 @@ static void shutdown(struct mgsl_struct * info) spin_unlock_irqrestore(&info->irq_spinlock,flags); - mgsl_release_resources(info); - + mgsl_release_resources(info); + if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; - + tty_port_set_initialized(&info->port, 0); } /* end of shutdown() */ static void mgsl_program_hw(struct mgsl_struct *info) @@ -1966,15 +1963,8 @@ static void mgsl_change_params(struct mgsl_struct *info) } info->timeout += HZ/50; /* Add .02 seconds of slop */ - if (cflag & CRTSCTS) - info->port.flags |= ASYNC_CTS_FLOW; - else - info->port.flags &= ~ASYNC_CTS_FLOW; - - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; + tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); + tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); /* process tty input control flags */ @@ -2972,7 +2962,7 @@ static int mgsl_ioctl(struct tty_struct *tty, if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCMIWAIT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; } @@ -3049,7 +3039,7 @@ static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termio /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { info->serial_signals |= SerialSignal_DTR; - if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) + if (!C_CRTSCTS(tty) || !tty_throttled(tty)) info->serial_signals |= SerialSignal_RTS; spin_lock_irqsave(&info->irq_spinlock,flags); usc_set_serial_signals(info); @@ -3091,7 +3081,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) goto cleanup; mutex_lock(&info->port.mutex); - if (info->port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&info->port)) mgsl_wait_until_sent(tty, info->timeout); mgsl_flush_buffer(tty); tty_ldisc_flush(tty); @@ -3129,15 +3119,15 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_wait_until_sent(%s) entry\n", __FILE__,__LINE__, info->device_name ); - + if (mgsl_paranoia_check(info, tty->name, "mgsl_wait_until_sent")) return; - if (!(info->port.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->port)) goto exit; - + orig_jiffies = jiffies; - + /* Set check interval to 1/5 of estimated time to * send a character, and make it at least 1. The check * interval should also be less than the timeout. @@ -3204,7 +3194,7 @@ static void mgsl_hangup(struct tty_struct *tty) shutdown(info); info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + tty_port_set_active(&info->port, 0); info->port.tty = NULL; wake_up_interruptible(&info->port.open_wait); @@ -3270,9 +3260,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready on %s\n", __FILE__,__LINE__, tty->driver->name ); - if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { /* nonblock mode is set or port is not enabled */ - port->flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(port, 1); return 0; } @@ -3297,14 +3287,14 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, port->count--; spin_unlock_irqrestore(&info->irq_spinlock, flags); port->blocked_open++; - + while (1) { - if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) + if (C_BAUD(tty) && tty_port_initialized(port)) tty_port_raise_dtr_rts(port); - + set_current_state(TASK_INTERRUPTIBLE); - - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + + if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { retval = (port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; @@ -3341,7 +3331,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, __FILE__,__LINE__, tty->driver->name, port->count ); if (!retval) - port->flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(port, 1); return retval; diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index d5b6471..7aca2d4 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -726,7 +726,7 @@ static void close(struct tty_struct *tty, struct file *filp) goto cleanup; mutex_lock(&info->port.mutex); - if (info->port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&info->port)) wait_until_sent(tty, info->timeout); flush_buffer(tty); tty_ldisc_flush(tty); @@ -756,9 +756,9 @@ static void hangup(struct tty_struct *tty) spin_lock_irqsave(&info->port.lock, flags); info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; spin_unlock_irqrestore(&info->port.lock, flags); + tty_port_set_active(&info->port, 0); mutex_unlock(&info->port.mutex); wake_up_interruptible(&info->port.open_wait); @@ -784,7 +784,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { info->signals |= SerialSignal_DTR; - if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) + if (!C_CRTSCTS(tty) || !tty_throttled(tty)) info->signals |= SerialSignal_RTS; spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -893,7 +893,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) if (sanity_check(info, tty->name, "wait_until_sent")) return; DBGINFO(("%s wait_until_sent entry\n", info->device_name)); - if (!(info->port.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->port)) goto exit; orig_jiffies = jiffies; @@ -1032,7 +1032,7 @@ static int ioctl(struct tty_struct *tty, if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCMIWAIT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; } @@ -2080,7 +2080,7 @@ static void dcd_change(struct slgt_info *info, unsigned short status) wake_up_interruptible(&info->event_wait_q); info->pending_bh |= BH_STATUS; - if (info->port.flags & ASYNC_CHECK_CD) { + if (tty_port_check_carrier(&info->port)) { if (info->signals & SerialSignal_DCD) wake_up_interruptible(&info->port.open_wait); else { @@ -2421,7 +2421,7 @@ static int startup(struct slgt_info *info) { DBGINFO(("%s startup\n", info->device_name)); - if (info->port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&info->port)) return 0; if (!info->tx_buf) { @@ -2442,7 +2442,7 @@ static int startup(struct slgt_info *info) if (info->port.tty) clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 1); return 0; } @@ -2454,7 +2454,7 @@ static void shutdown(struct slgt_info *info) { unsigned long flags; - if (!(info->port.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->port)) return; DBGINFO(("%s shutdown\n", info->device_name)); @@ -2489,7 +2489,7 @@ static void shutdown(struct slgt_info *info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 0); } static void program_hw(struct slgt_info *info) @@ -2576,15 +2576,8 @@ static void change_params(struct slgt_info *info) } info->timeout += HZ/50; /* Add .02 seconds of slop */ - if (cflag & CRTSCTS) - info->port.flags |= ASYNC_CTS_FLOW; - else - info->port.flags &= ~ASYNC_CTS_FLOW; - - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; + tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); + tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); /* process tty input control flags */ @@ -3269,9 +3262,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, DBGINFO(("%s block_til_ready\n", tty->driver->name)); - if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { /* nonblock mode is set or port is not enabled */ - port->flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(port, 1); return 0; } @@ -3294,12 +3287,12 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) + if (C_BAUD(tty) && tty_port_initialized(port)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { retval = (port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; @@ -3328,7 +3321,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open--; if (!retval) - port->flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(port, 1); DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); return retval; diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 3f89685..dec1565 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -812,7 +812,7 @@ static void close(struct tty_struct *tty, struct file *filp) goto cleanup; mutex_lock(&info->port.mutex); - if (info->port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&info->port)) wait_until_sent(tty, info->timeout); flush_buffer(tty); @@ -849,9 +849,9 @@ static void hangup(struct tty_struct *tty) spin_lock_irqsave(&info->port.lock, flags); info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; spin_unlock_irqrestore(&info->port.lock, flags); + tty_port_set_active(&info->port, 1); mutex_unlock(&info->port.mutex); wake_up_interruptible(&info->port.open_wait); @@ -881,7 +881,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { info->serial_signals |= SerialSignal_DTR; - if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) + if (!C_CRTSCTS(tty) || !tty_throttled(tty)) info->serial_signals |= SerialSignal_RTS; spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -1061,7 +1061,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) if (sanity_check(info, tty->name, "wait_until_sent")) return; - if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags)) + if (!tty_port_initialized(&info->port)) goto exit; orig_jiffies = jiffies; @@ -1261,7 +1261,7 @@ static int ioctl(struct tty_struct *tty, if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCMIWAIT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; } @@ -2463,7 +2463,7 @@ static void isr_io_pin( SLMP_INFO *info, u16 status ) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - if ( (info->port.flags & ASYNC_CHECK_CD) && + if (tty_port_check_carrier(&info->port) && (status & MISCSTATUS_DCD_LATCHED) ) { if ( debug_level >= DEBUG_LEVEL_ISR ) printk("%s CD now %s...", info->device_name, @@ -2636,7 +2636,7 @@ static int startup(SLMP_INFO * info) if ( debug_level >= DEBUG_LEVEL_INFO ) printk("%s(%d):%s tx_releaseup()\n",__FILE__,__LINE__,info->device_name); - if (info->port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&info->port)) return 0; if (!info->tx_buf) { @@ -2662,7 +2662,7 @@ static int startup(SLMP_INFO * info) if (info->port.tty) clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 1); return 0; } @@ -2673,7 +2673,7 @@ static void shutdown(SLMP_INFO * info) { unsigned long flags; - if (!(info->port.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->port)) return; if (debug_level >= DEBUG_LEVEL_INFO) @@ -2705,7 +2705,7 @@ static void shutdown(SLMP_INFO * info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 0); } static void program_hw(SLMP_INFO *info) @@ -2813,15 +2813,8 @@ static void change_params(SLMP_INFO *info) } info->timeout += HZ/50; /* Add .02 seconds of slop */ - if (cflag & CRTSCTS) - info->port.flags |= ASYNC_CTS_FLOW; - else - info->port.flags &= ~ASYNC_CTS_FLOW; - - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; + tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); + tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); /* process tty input control flags */ @@ -3285,10 +3278,10 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready()\n", __FILE__,__LINE__, tty->driver->name ); - if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { /* nonblock mode is set or port is not enabled */ /* just verify that callout device is not active */ - port->flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(port, 1); return 0; } @@ -3315,12 +3308,12 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) + if (C_BAUD(tty) && tty_port_initialized(port)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { retval = (port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; @@ -3355,7 +3348,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, __FILE__,__LINE__, tty->driver->name, port->count ); if (!retval) - port->flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(port, 1); return retval; } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index a946e49..aa80dc9 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -37,29 +37,6 @@ #define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) -/* - * If all tty flip buffers have been processed by flush_to_ldisc() or - * dropped by tty_buffer_flush(), check if the linked pty has been closed. - * If so, wake the reader/poll to process - */ -static inline void check_other_closed(struct tty_struct *tty) -{ - unsigned long flags, old; - - /* transition from TTY_OTHER_CLOSED => TTY_OTHER_DONE must be atomic */ - for (flags = ACCESS_ONCE(tty->flags); - test_bit(TTY_OTHER_CLOSED, &flags); - ) { - old = flags; - __set_bit(TTY_OTHER_DONE, &flags); - flags = cmpxchg(&tty->flags, old, flags); - if (old == flags) { - wake_up_interruptible(&tty->read_wait); - break; - } - } -} - /** * tty_buffer_lock_exclusive - gain exclusive access to buffer * tty_buffer_unlock_exclusive - release exclusive access @@ -254,8 +231,6 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) if (ld && ld->ops->flush_buffer) ld->ops->flush_buffer(tty); - check_other_closed(tty); - atomic_dec(&buf->priority); mutex_unlock(&buf->lock); } @@ -522,10 +497,8 @@ static void flush_to_ldisc(struct work_struct *work) */ count = smp_load_acquire(&head->commit) - head->read; if (!count) { - if (next == NULL) { - check_other_closed(tty); + if (next == NULL) break; - } buf->head = next; tty_buffer_free(port, head); continue; @@ -614,3 +587,8 @@ bool tty_buffer_cancel_work(struct tty_port *port) { return cancel_work_sync(&port->buf.work); } + +void tty_buffer_flush_work(struct tty_port *port) +{ + flush_work(&port->buf.work); +} diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 24d5491..734a635 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -230,9 +230,6 @@ static void tty_del_file(struct file *file) tty_free_file(file); } - -#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base) - /** * tty_name - return tty naming * @tty: tty structure @@ -1070,7 +1067,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, if (tty_paranoia_check(tty, inode, "tty_read")) return -EIO; - if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) + if (!tty || tty_io_error(tty)) return -EIO; /* We want to wait for the line discipline to sort out in this @@ -1245,8 +1242,7 @@ static ssize_t tty_write(struct file *file, const char __user *buf, if (tty_paranoia_check(tty, file_inode(file), "tty_write")) return -EIO; - if (!tty || !tty->ops->write || - (test_bit(TTY_IO_ERROR, &tty->flags))) + if (!tty || !tty->ops->write || tty_io_error(tty)) return -EIO; /* Short term debug to catch buggy drivers */ if (tty->ops->write_room == NULL) @@ -1964,7 +1960,6 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) * tty_lookup_driver - lookup a tty driver for a given device file * @device: device number * @filp: file pointer to tty - * @noctty: set if the device should not become a controlling tty * @index: index for the device in the @return driver * @return: driver for this inode (with increased refcount) * diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 23bf5bb..bf36ac9 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -158,7 +158,7 @@ int tty_throttle_safe(struct tty_struct *tty) int ret = 0; mutex_lock(&tty->throttle_mutex); - if (!test_bit(TTY_THROTTLED, &tty->flags)) { + if (!tty_throttled(tty)) { if (tty->flow_change != TTY_THROTTLE_SAFE) ret = 1; else { @@ -189,7 +189,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) int ret = 0; mutex_lock(&tty->throttle_mutex); - if (test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty_throttled(tty)) { if (tty->flow_change != TTY_UNTHROTTLE_SAFE) ret = 1; else { diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index dbcca30..c3f9d93 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -204,7 +204,8 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) if (port->console) goto out; - if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (tty_port_initialized(port)) { + tty_port_set_initialized(port, 0); /* * Drop DTR/RTS if HUPCL is set. This causes any attached * modem to hang up the line. @@ -236,12 +237,12 @@ void tty_port_hangup(struct tty_port *port) spin_lock_irqsave(&port->lock, flags); port->count = 0; - port->flags &= ~ASYNC_NORMAL_ACTIVE; tty = port->tty; if (tty) set_bit(TTY_IO_ERROR, &tty->flags); port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); + tty_port_set_active(port, 0); tty_port_shutdown(port, tty); tty_kref_put(tty); wake_up_interruptible(&port->open_wait); @@ -364,15 +365,15 @@ int tty_port_block_til_ready(struct tty_port *port, /* if non-blocking mode is set we can pass directly to open unless the port has just hung up or is in another error state */ - if (tty->flags & (1 << TTY_IO_ERROR)) { - port->flags |= ASYNC_NORMAL_ACTIVE; + if (tty_io_error(tty)) { + tty_port_set_active(port, 1); return 0; } if (filp->f_flags & O_NONBLOCK) { /* Indicate we are open */ if (C_BAUD(tty)) tty_port_raise_dtr_rts(port); - port->flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(port, 1); return 0; } @@ -393,13 +394,13 @@ int tty_port_block_til_ready(struct tty_port *port, while (1) { /* Indicate we are open */ - if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) + if (C_BAUD(tty) && tty_port_initialized(port)) tty_port_raise_dtr_rts(port); prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); /* Check for a hangup or uninitialised port. Return accordingly */ - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { + if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else @@ -430,9 +431,9 @@ int tty_port_block_til_ready(struct tty_port *port, if (!tty_hung_up_p(filp)) port->count++; port->blocked_open--; - if (retval == 0) - port->flags |= ASYNC_NORMAL_ACTIVE; spin_unlock_irqrestore(&port->lock, flags); + if (retval == 0) + tty_port_set_active(port, 1); return retval; } EXPORT_SYMBOL(tty_port_block_til_ready); @@ -480,7 +481,7 @@ int tty_port_close_start(struct tty_port *port, tty->closing = 1; - if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (tty_port_initialized(port)) { /* Don't block on a stalled port, just pull the chain */ if (tty->flow_stopped) tty_driver_flush_buffer(tty); @@ -514,8 +515,8 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); wake_up_interruptible(&port->open_wait); } - port->flags &= ~ASYNC_NORMAL_ACTIVE; spin_unlock_irqrestore(&port->lock, flags); + tty_port_set_active(port, 0); } EXPORT_SYMBOL(tty_port_close_end); @@ -578,7 +579,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, mutex_lock(&port->mutex); - if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (!tty_port_initialized(port)) { clear_bit(TTY_IO_ERROR, &tty->flags); if (port->ops->activate) { int retval = port->ops->activate(port, tty); @@ -587,7 +588,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, return retval; } } - set_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 1); } mutex_unlock(&port->mutex); return tty_port_block_til_ready(port, tty, filp); diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 4dd9dd2..368ce18 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -354,7 +354,7 @@ int paste_selection(struct tty_struct *tty) add_wait_queue(&vc->paste_wait, &wait); while (sel_buffer && sel_buffer_lth > pasted) { set_current_state(TASK_INTERRUPTIBLE); - if (test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty_throttled(tty)) { schedule(); continue; } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 3e3c757..dc12532 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -760,50 +760,54 @@ static void visual_init(struct vc_data *vc, int num, int init) int vc_allocate(unsigned int currcons) /* return 0 on success */ { + struct vt_notifier_param param; + struct vc_data *vc; + WARN_CONSOLE_UNLOCKED(); if (currcons >= MAX_NR_CONSOLES) return -ENXIO; - if (!vc_cons[currcons].d) { - struct vc_data *vc; - struct vt_notifier_param param; - - /* prevent users from taking too much memory */ - if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE)) - return -EPERM; - - /* due to the granularity of kmalloc, we waste some memory here */ - /* the alloc is done in two steps, to optimize the common situation - of a 25x80 console (structsize=216, screenbuf_size=4000) */ - /* although the numbers above are not valid since long ago, the - point is still up-to-date and the comment still has its value - even if only as a historical artifact. --mj, July 1998 */ - param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL); - if (!vc) + + if (vc_cons[currcons].d) + return 0; + + /* due to the granularity of kmalloc, we waste some memory here */ + /* the alloc is done in two steps, to optimize the common situation + of a 25x80 console (structsize=216, screenbuf_size=4000) */ + /* although the numbers above are not valid since long ago, the + point is still up-to-date and the comment still has its value + even if only as a historical artifact. --mj, July 1998 */ + param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL); + if (!vc) return -ENOMEM; - vc_cons[currcons].d = vc; - tty_port_init(&vc->port); - INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); - visual_init(vc, currcons, 1); - if (!*vc->vc_uni_pagedir_loc) + + vc_cons[currcons].d = vc; + tty_port_init(&vc->port); + INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); + + visual_init(vc, currcons, 1); + + if (!*vc->vc_uni_pagedir_loc) con_set_default_unimap(vc); - vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); - if (!vc->vc_screenbuf) { - kfree(vc); - vc_cons[currcons].d = NULL; - return -ENOMEM; - } - /* If no drivers have overridden us and the user didn't pass a - boot option, default to displaying the cursor */ - if (global_cursor_default == -1) - global_cursor_default = 1; + vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); + if (!vc->vc_screenbuf) + goto err_free; + + /* If no drivers have overridden us and the user didn't pass a + boot option, default to displaying the cursor */ + if (global_cursor_default == -1) + global_cursor_default = 1; + + vc_init(vc, vc->vc_rows, vc->vc_cols, 1); + vcs_make_sysfs(currcons); + atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); - vc_init(vc, vc->vc_rows, vc->vc_cols, 1); - vcs_make_sysfs(currcons); - atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); - } return 0; +err_free: + kfree(vc); + vc_cons[currcons].d = NULL; + return -ENOMEM; } static inline int resize_screen(struct vc_data *vc, int width, int height, @@ -1035,20 +1039,27 @@ struct vc_data *vc_deallocate(unsigned int currcons) #define VT100ID "\033[?1;2c" #define VT102ID "\033[?6c" -unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, +const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15 }; /* the default colour table, for VGA+ colour systems */ -int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa, - 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff}; -int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa, - 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff}; -int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa, - 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff}; +unsigned char default_red[] = { + 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, + 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff +}; +module_param_array(default_red, byte, NULL, S_IRUGO | S_IWUSR); -module_param_array(default_red, int, NULL, S_IRUGO | S_IWUSR); -module_param_array(default_grn, int, NULL, S_IRUGO | S_IWUSR); -module_param_array(default_blu, int, NULL, S_IRUGO | S_IWUSR); +unsigned char default_grn[] = { + 0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa, + 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff +}; +module_param_array(default_grn, byte, NULL, S_IRUGO | S_IWUSR); + +unsigned char default_blu[] = { + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff +}; +module_param_array(default_blu, byte, NULL, S_IRUGO | S_IWUSR); /* * gotoxy() must verify all boundaries, because the arguments @@ -3564,7 +3575,7 @@ static int do_register_con_driver(const struct consw *csw, int first, int last) struct module *owner = csw->owner; struct con_driver *con_driver; const char *desc; - int i, retval = 0; + int i, retval; WARN_CONSOLE_UNLOCKED(); @@ -3575,17 +3586,17 @@ static int do_register_con_driver(const struct consw *csw, int first, int last) con_driver = ®istered_con_driver[i]; /* already registered */ - if (con_driver->con == csw) + if (con_driver->con == csw) { retval = -EBUSY; + goto err; + } } - if (retval) - goto err; - desc = csw->con_startup(); - - if (!desc) + if (!desc) { + retval = -ENODEV; goto err; + } retval = -EINVAL; |